from __future__ import annotations import argparse import json import sys from pathlib import Path from typing import Any ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT)) sys.path.insert(0, str(ROOT / "plugins" / "1c")) sys.path.insert(0, str(ROOT / "plugins" / "1c" / "connector")) import adapter_1c_server as adapter_server # noqa: E402 def require(condition: bool, message: str, failures: list[str]) -> None: if not condition: failures.append(message) def patch_adapter_reads() -> None: adapter_server.read_storage_file_bytes = lambda base_id, table, file_name, timeout_seconds=30: (b"fake", {"database": "fake"}, None) adapter_server.payload_text_from_bytes = lambda data: { "status": "ok", "text": "Процедура Проверка()\nКонецПроцедуры", } adapter_server.extension_module_owner_payload = lambda base_id, module_id, table, timeout_seconds=30: None adapter_server.cached_module_owner_payload = lambda base_id, module_id: None def read_origin(method: str, module_ref: str) -> dict[str, Any]: result = adapter_server.call_method( method, { "base_id": "upo_test", "module_ref": module_ref, "include_text": True, "max_chars": 200, }, ) origin = result.get("origin") if isinstance(result.get("origin"), dict) else {} return {"result": result, "origin": origin} def check_code_search_origin(failures: list[str]) -> None: adapter_server.search_modules = lambda payload: { "status": "ok", "source": {"kind": "live_metadata"}, "matches": [ { "snippet": {"text": "Процедура Проверка()", "offset": 0}, "owner": {"status": "unresolved", "kind": None, "name": None}, "origin": { "source": "cas_reference", "status": "owner_unresolved", "write_surface": "requires_owner_resolution", }, "module": {"name": "Модуль БСЛ"}, "read_selector": {"base_id": "upo_test", "module_ref": "ConfigCAS:object-module"}, } ], "counts": {"matches": 1, "complete": True, "scan_limit_hit": False}, "diagnostics": {}, } result = adapter_server.call_method("code.search", {"base_id": "upo_test", "query": "Проверка"}) items = result.get("items") if isinstance(result.get("items"), list) else [] origin = items[0].get("origin") if items and isinstance(items[0], dict) and isinstance(items[0].get("origin"), dict) else {} require(result.get("schema") == "onec_code_search.v1", "code.search must return code search schema", failures) require(bool(items), "code.search must return patched item", failures) require(origin.get("source") == "cas_reference", "code.search item must preserve modules.search origin", failures) require(origin.get("write_surface") == "requires_owner_resolution", "code.search item origin must keep write_surface", failures) def run_checks() -> dict[str, Any]: patch_adapter_reads() failures: list[str] = [] config = read_origin("modules.read", "Config:object-module") require(config["result"].get("status") == "ok", "Config module_ref must read in patched contract", failures) require(config["origin"].get("source") == "configuration", "Config module_ref must expose configuration origin", failures) require(config["origin"].get("write_surface") == "base_saved_state", "Config origin must point writes to base saved-state", failures) save = read_origin("modules.read", "ConfigSave:object-module") require(save["result"].get("status") == "ok", "ConfigSave module_ref must read in patched contract", failures) require(save["origin"].get("source") == "saved_state", "ConfigSave module_ref must expose saved_state origin", failures) require(save["origin"].get("write_surface") == "base_saved_state", "ConfigSave origin must point writes to base saved-state", failures) cas = read_origin("modules.read", "ConfigCAS:object-module") require(cas["result"].get("status") == "ok", "ConfigCAS module_ref must read in patched contract", failures) require(cas["origin"].get("source") == "cas_reference", "ConfigCAS fallback must not pretend extension/base owner", failures) require(cas["origin"].get("status") == "owner_unresolved", "ConfigCAS fallback must require owner resolution", failures) require(cas["origin"].get("write_surface") == "requires_owner_resolution", "ConfigCAS fallback must block direct write routing", failures) code_save = read_origin("code.read", "ConfigSave:object-module") require(code_save["result"].get("schema") == "onec_code_read.v1", "code.read must wrap modules.read as onec_code_read", failures) require(code_save["origin"].get("source") == "saved_state", "code.read must preserve modules.read origin", failures) require(code_save["origin"].get("write_surface") == "base_saved_state", "code.read origin must keep write_surface", failures) check_code_search_origin(failures) return { "schema": "onec_module_origin_contract_check.v1", "status": "ok" if not failures else "failed", "failures": failures, "checks": { "config_origin": "Config module_ref exposes configuration origin", "configsave_origin": "ConfigSave module_ref exposes saved_state origin", "configcas_owner_required": "ConfigCAS module_ref requires owner resolution", "code_read_preserves_origin": "code.read preserves origin evidence from modules.read", "code_search_preserves_origin": "code.search preserves origin evidence from modules.search", }, } def main() -> int: parser = argparse.ArgumentParser(description="Check modules.read origin/provenance contract invariants.") parser.add_argument("--print", action="store_true") args = parser.parse_args() report = run_checks() if args.print or report["status"] != "ok": print(json.dumps(report, ensure_ascii=False, indent=2)) else: print("1C module origin contract status: ok") return 0 if report["status"] == "ok" else 1 if __name__ == "__main__": raise SystemExit(main())