from __future__ import annotations import json import sys from pathlib import Path from typing import Any ROOT = Path(__file__).resolve().parents[2] sys.path.insert(0, str(ROOT)) import scripts.check_1c_metadata_kind_fixtures as fixtures # noqa: E402 MANIFEST = ROOT / "config" / "1c_metadata_kind_fixtures.json" def test_fixture_manifest_is_valid_and_forbids_direct_sql() -> None: manifest = fixtures.load_json(MANIFEST) assert fixtures.validate_manifest(manifest) == [] assert manifest["policy"]["direct_sql_write_allowed"] is False assert manifest["designer"] == { "required_version": "8.5.1.1236", "access_mode": "operating_system_integrated_only", "credential_arguments_allowed": False, "export_script": "scripts/export_1c_extension_sources.ps1", } assert manifest["external_reference"]["usage"] == "external_reference_only_not_vendored" assert len(manifest["external_reference"]["commit"]) == 40 assert {item["kind"] for item in manifest["fixtures"]} == { "CalculationRegister", "Sequence", "Interface", } def test_offline_check_does_not_claim_live_readiness() -> None: result = fixtures.check_manifest(MANIFEST) assert result["passed"] is True assert result["status"] == "manifest_valid" assert result["counts"] == {"findings": 0, "fixtures": 3, "ready": 0, "gaps": 0} assert {item["status"] for item in result["fixtures"]} == {"not_checked"} def test_live_check_distinguishes_ready_missing_and_unconfigured() -> None: calls: list[tuple[str, dict[str, Any]]] = [] def fake_rpc(_url: str, method: str, payload: dict[str, Any], _timeout: float, _token: str) -> dict[str, Any]: calls.append((method, payload)) if method == "extension.objects.find": return { "status": "ok", "objects": [ { "kind": "CalculationRegister", "name": "tt_РегистрРасчета1", "guid": "a4dfe8f1-2ce8-43c9-8b41-3deb7238c5d5", "activation_state": "saved_only", "extension_guid": "fb26cf42-7609-11f1-828f-005056b0d483", }, { "kind": "Catalog", "name": "tt_Последовательность1", "guid": "11111111-1111-1111-1111-111111111111", "activation_state": "saved_only", }, ], } return {"status": "ok"} result = fixtures.check_manifest(MANIFEST, live=True, rpc_call=fake_rpc) by_kind = {item["kind"]: item for item in result["fixtures"]} assert by_kind["CalculationRegister"]["status"] == "fixture_ready" assert by_kind["Sequence"]["status"] == "fixture_missing" assert by_kind["Interface"]["status"] == "target_not_configured" assert [item["method"] for item in by_kind["CalculationRegister"]["smoke"]] == [ "metadata.object.get", "metadata.object.properties", "metadata.object.attributes", "metadata.object.related", ] extension_find_calls = [payload for method, payload in calls if method == "extension.objects.find"] assert len(extension_find_calls) == 1 calc_find_payload = extension_find_calls[0] assert "kind" not in calc_find_payload assert calc_find_payload["use_cache"] is True assert calc_find_payload["full_scan"] is False assert calc_find_payload["refresh_cache"] is False assert result["passed"] is True assert result["status"] == "gaps_found" def test_require_ready_fails_when_a_fixture_is_missing() -> None: def fake_rpc(_url: str, method: str, _payload: dict[str, Any], _timeout: float, _token: str) -> dict[str, Any]: if method == "metadata.objects.list": return {"status": "not_found", "objects": []} return {"status": "not_found", "objects": []} result = fixtures.check_manifest( MANIFEST, live=True, require_ready=True, base_overrides={"interface_legacy_base": "legacy_test"}, rpc_call=fake_rpc, ) assert result["passed"] is False assert result["counts"]["ready"] == 0 assert result["counts"]["gaps"] == 3 def test_live_check_reuses_extension_scan_transport_failure() -> None: calls = 0 def fake_rpc(_url: str, _method: str, _payload: dict[str, Any], _timeout: float, _token: str) -> dict[str, Any]: nonlocal calls calls += 1 raise TimeoutError("scan timed out") result = fixtures.check_manifest(MANIFEST, live=True, rpc_call=fake_rpc) by_kind = {item["kind"]: item for item in result["fixtures"]} assert calls == 1 assert by_kind["CalculationRegister"]["error"] == "TimeoutError" assert "probe_error_reused" not in by_kind["CalculationRegister"] assert by_kind["Sequence"]["error"] == "TimeoutError" assert by_kind["Sequence"]["probe_error_reused"] is True def test_manifest_rejects_credential_like_keys(tmp_path: Path) -> None: value = fixtures.load_json(MANIFEST) value["fixtures"][0]["password"] = "must-not-be-here" path = tmp_path / "manifest.json" path.write_text(json.dumps(value, ensure_ascii=False), encoding="utf-8") result = fixtures.check_manifest(path) assert result["passed"] is False assert any(item["code"] == "credential_key_in_manifest" for item in result["findings"]) def test_manifest_requires_safe_designer_access_and_pinned_reference() -> None: value = fixtures.load_json(MANIFEST) value["designer"]["access_mode"] = "interactive_credentials" value["external_reference"]["commit"] = "main" findings = fixtures.validate_manifest(value) assert {item["code"] for item in findings} >= { "unsafe_designer_access_mode", "invalid_external_reference_commit", } def test_designer_export_script_has_no_user_or_credential_parameters() -> None: script = (ROOT / "scripts" / "export_1c_extension_sources.ps1").read_text(encoding="utf-8") assert "/WA+" in script assert "/N" not in script assert "/P" not in script assert "UserName" not in script assert "Credential" not in script assert "/DumpConfigToFiles" in script assert "-Extension" in script