from __future__ import annotations import argparse import json import sys from pathlib import Path ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT / "scripts")) from route_1c_question import route_question # noqa: E402 DEFAULT_INDEX = ROOT / "reports" / "1c-sql" / "upo" / "unified-object-route-index.json" CASES = [ { "id": "docs_only_form_open", "question": "Как работает событие ПриОткрытии формы?", "expected_route": "docs_rag", "expected_fact_paths": [], }, { "id": "current_fact_extension_attribute", "question": "Есть ли реквизит ДатаСоздания у документа ПриходнаяНакладная?", "expected_route": "current_config_fact", "expected_fact_paths": ["Документ.ПриходнаяНакладная.ДатаСоздания"], "expected_exists": {"Документ.ПриходнаяНакладная.ДатаСоздания": True}, }, { "id": "rag_example_requires_current_fact_check", "question": "В примере RAG есть реквизит Артикул у справочника Номенклатура. Напиши код для текущей базы.", "expected_route": "mixed_docs_and_current_config", "expected_fact_paths": ["Справочник.Номенклатура.Артикул"], "expected_risk": "example_is_not_current_fact", }, ] def fact_exists_by_path(route: dict) -> dict[str, bool | None]: result = {} for row in route.get("fact_checks") or []: path = row.get("path") if not path: continue if row.get("status") != "checked": result[path] = None else: result[path] = bool((row.get("result") or {}).get("exists")) return result def run_case(case: dict, *, index: Path, view: str) -> dict: route = route_question(case["question"], index_path=index, view=view) failures = [] decision = route.get("decision") or {} if decision.get("route") != case.get("expected_route"): failures.append({"code": "route_mismatch", "expected": case.get("expected_route"), "actual": decision.get("route")}) actual_paths = route.get("fact_paths") or [] expected_paths = case.get("expected_fact_paths") or [] if actual_paths != expected_paths: failures.append({"code": "fact_paths_mismatch", "expected": expected_paths, "actual": actual_paths}) expected_risk = case.get("expected_risk") if expected_risk: risks = {row.get("code") for row in route.get("source_risks") or []} if expected_risk not in risks: failures.append({"code": "risk_missing", "expected": expected_risk, "actual": sorted(risks)}) exists = fact_exists_by_path(route) for path, expected in (case.get("expected_exists") or {}).items(): if exists.get(path) is not expected: failures.append({"code": "fact_exists_mismatch", "path": path, "expected": expected, "actual": exists.get(path)}) return { "id": case["id"], "status": "passed" if not failures else "failed", "question": case["question"], "failures": failures, "route": { "decision": route.get("decision"), "source_risks": route.get("source_risks"), "fact_paths": route.get("fact_paths"), "fact_exists": exists, }, } def run_check(index: Path, *, view: str) -> dict: if not index.exists(): return { "schema": "onec_question_router_check.v1", "status": "failed", "error": f"route index not found: {index}", "cases": [], } results = [run_case(case, index=index, view=view) for case in CASES] return { "schema": "onec_question_router_check.v1", "status": "ok" if all(row["status"] == "passed" for row in results) else "failed", "index": str(index), "view": view, "case_count": len(results), "cases": results, } def main() -> int: parser = argparse.ArgumentParser(description="Check 1C question router behavior.") parser.add_argument("--index", type=Path, default=DEFAULT_INDEX) parser.add_argument("--view", choices=["effective", "base"], default="effective") parser.add_argument("--output", type=Path) parser.add_argument("--print", action="store_true") args = parser.parse_args() report = run_check(args.index, view=args.view) if args.output: args.output.parent.mkdir(parents=True, exist_ok=True) args.output.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") if args.print or not args.output: print(json.dumps(report, ensure_ascii=False, indent=2)) return 0 if report["status"] == "ok" else 1 if __name__ == "__main__": raise SystemExit(main())