from __future__ import annotations import json import sys from pathlib import Path from typing import Any import pytest ROOT = Path(__file__).resolve().parents[2] sys.path.insert(0, str(ROOT)) import scripts.audit_1c_adapter_coverage as coverage # noqa: E402 def test_record_ref_from_public_reference_value() -> None: ref = "0123456789ABCDEF0123456789ABCDEF" assert coverage.record_ref_from_row({"ref": ref}) == ref assert coverage.record_ref_from_row({"ref": {"type": "reference", "hex": ref}}) == ref assert coverage.record_ref_from_row({"period": "2026-01-01"}) is None def test_audit_data_kind_runs_complete_read_chain() -> None: calls: list[tuple[str, dict[str, Any]]] = [] ref = "0123456789ABCDEF0123456789ABCDEF" def fake_rpc(_url: str, _token: str, method: str, payload: dict[str, Any], _timeout: float) -> dict[str, Any]: calls.append((method, payload)) responses = { "metadata.objects.list": {"status": "ok", "objects": [{"kind": "Catalog", "name": "Товары", "ref": "Catalog.Товары"}]}, "data.schema": {"status": "ok", "table": {"name": "_Reference1"}, "fields": [{"name": "ref"}]}, "data.list": {"status": "ok", "rows": [{"ref": {"type": "reference", "hex": ref}}]}, "data.count": {"status": "ok", "count": 7}, "data.get": {"status": "ok", "rows": [{"ref": ref}]}, } return responses[method] result = coverage.audit_data_kind( "http://adapter", "upo_test", "secret", "Catalog", 10, True, fake_rpc, ) assert result["status"] == "ok" assert [method for method, _ in calls] == [ "metadata.objects.list", "data.schema", "data.list", "data.count", "data.get", ] assert calls[-1][1]["record_ref"] == ref assert result["operations"]["data.count"]["count"] == 7 def test_audit_data_kind_marks_get_not_applicable_for_register_row() -> None: def fake_rpc(_url: str, _token: str, method: str, _payload: dict[str, Any], _timeout: float) -> dict[str, Any]: responses = { "metadata.objects.list": {"status": "ok", "objects": [{"kind": "InformationRegister", "name": "Курсы"}]}, "data.schema": {"status": "ok", "table": {"name": "_InfoRg1"}, "fields": [{"name": "period"}]}, "data.list": {"status": "ok", "rows": [{"period": "2026-01-01"}]}, "data.count": {"status": "ok", "count": 1}, } return responses[method] result = coverage.audit_data_kind( "http://adapter", "upo_test", "secret", "InformationRegister", 10, True, fake_rpc ) assert result["status"] == "ok" assert result["operations"]["data.get"] == { "status": "not_applicable", "reason": "object_has_no_reference_key", "duration_ms": 0, } def test_audit_data_kind_does_not_mark_get_not_applicable_when_list_failed() -> None: def fake_rpc(_url: str, _token: str, method: str, _payload: dict[str, Any], _timeout: float) -> dict[str, Any]: responses = { "metadata.objects.list": {"status": "ok", "objects": [{"kind": "Document", "name": "Продажа"}]}, "data.schema": {"status": "ok", "table": {"name": "_Document1"}, "fields": [{"name": "ref"}]}, "data.list": {"status": "transport_error", "diagnostics": {"message": "timed out"}}, "data.count": {"status": "ok", "count": 3}, } return responses[method] result = coverage.audit_data_kind( "http://adapter", "upo_test", "secret", "Document", 10, True, fake_rpc ) assert result["status"] == "degraded" assert result["operations"]["data.get"]["status"] == "blocked" assert result["operations"]["data.get"]["reason"] == "data_list_failed" def test_run_data_checks_saves_and_resumes_checkpoint(tmp_path: Path) -> None: checkpoint = tmp_path / "coverage.checkpoint.json" calls: list[str] = [] def fake_rpc(_url: str, _token: str, method: str, payload: dict[str, Any], _timeout: float) -> dict[str, Any]: calls.append(f"{method}:{payload.get('kind', '')}") if method == "metadata.objects.list": return {"status": "ok", "objects": [{"kind": payload["kind"], "name": payload["kind"]}]} if method == "data.schema": return {"status": "ok", "table": {"name": "_Data1"}, "fields": []} raise AssertionError(method) first, resumed = coverage.run_data_checks( "http://adapter", "upo_test", "secret", ["Catalog", "Document"], 10, False, 2, checkpoint, False, False, fake_rpc ) assert resumed == 0 assert set(first) == {"Catalog", "Document"} saved = json.loads(checkpoint.read_text(encoding="utf-8")) assert saved["include_reads"] is False assert set(saved["checks"]) == {"Catalog", "Document"} calls.clear() second, resumed = coverage.run_data_checks( "http://adapter", "upo_test", "secret", ["Catalog", "Document"], 10, False, 2, checkpoint, True, False, fake_rpc ) assert resumed == 2 assert second == first assert calls == [] def test_run_data_checks_can_retry_only_degraded_entries(tmp_path: Path) -> None: checkpoint = tmp_path / "coverage.checkpoint.json" checkpoint.write_text( json.dumps( { "schema": "onec_adapter_data_audit_checkpoint.v1", "base_url": "http://adapter", "base_id": "upo_test", "include_reads": False, "checks": { "Catalog": {"kind": "Catalog", "status": "ok"}, "Document": {"kind": "Document", "status": "degraded"}, }, } ), encoding="utf-8", ) calls: list[str] = [] def fake_rpc(_url: str, _token: str, method: str, payload: dict[str, Any], _timeout: float) -> dict[str, Any]: calls.append(method) if method == "metadata.objects.list": return {"status": "ok", "objects": [{"kind": payload["kind"], "name": "Продажа"}]} if method == "data.schema": return {"status": "ok", "table": {"name": "_Document1"}, "fields": []} raise AssertionError(method) checks, resumed = coverage.run_data_checks( "http://adapter", "upo_test", "secret", ["Catalog", "Document"], 10, False, 1, checkpoint, True, True, fake_rpc ) assert resumed == 1 assert checks["Catalog"]["status"] == "ok" assert checks["Document"]["status"] == "ok" assert calls == ["metadata.objects.list", "data.schema"] def test_checkpoint_rejects_schema_only_resume_for_full_reads(tmp_path: Path) -> None: checkpoint = tmp_path / "coverage.checkpoint.json" checkpoint.write_text( json.dumps( { "schema": "onec_adapter_data_audit_checkpoint.v1", "base_url": "http://adapter", "base_id": "upo_test", "include_reads": False, "checks": {}, } ), encoding="utf-8", ) with pytest.raises(ValueError, match="another data audit mode"): coverage.load_checkpoint(checkpoint, "http://adapter", "upo_test", True, True) def test_build_report_does_not_treat_unchecked_non_data_kind_as_list_failure() -> None: def fake_rpc(_url: str, _token: str, method: str, payload: dict[str, Any], _timeout: float) -> dict[str, Any]: if method == "metadata.adapter.audit": return { "status": "ok", "optional_deep_reads": [ {"kind": "DocumentJournal", "property": "column_types", "flag": "include_column_types=true"} ], "metadata_kinds": [ {"kind": "Catalog", "kind_ru": "Справочник", "count": 1, "capabilities": ["list"]}, {"kind": "CommonModule", "kind_ru": "ОбщийМодуль", "count": 1, "capabilities": ["list"]}, ], } if method == "metadata.objects.list": return {"status": "ok", "objects": [{"kind": payload["kind"], "name": "Товары"}]} if method == "data.schema": return {"status": "ok", "table": {"name": "_Reference1"}, "fields": []} raise AssertionError(method) report = coverage.build_report( "http://adapter", "upo_test", "secret", 10, True, True, workers=1, rpc_call=fake_rpc, ) assert report["status"] == "ok" assert report["list_failures"] == [] assert report["counts"]["data_kinds_checked"] == 1 assert report["optional_deep_reads"][0]["property"] == "column_types" common_module = next(row for row in report["matrix"] if row["kind"] == "CommonModule") assert "list_status" not in common_module