Initial SQL-only 1C adapter baseline

This commit is contained in:
2026-07-22 03:03:47 +03:00
commit e2503b77e7
545 changed files with 184711 additions and 0 deletions
+163
View File
@@ -0,0 +1,163 @@
#!/usr/bin/env python3
from __future__ import annotations
import argparse
import json
import sys
from pathlib import Path
from typing import Any
from urllib.error import HTTPError, URLError
from urllib.request import Request, urlopen
DEFAULT_FORM_FILE = "f96a0c45-3eff-11f1-8287-005056b0d483__25c39fbf-35a4-4b43-8e3f-cd1f91082c88.0"
def request_json(
method: str,
base_url: str,
path: str,
*,
payload: dict[str, Any] | None = None,
timeout: float = 30.0,
) -> tuple[int, dict[str, Any] | str, bool]:
body = json.dumps(payload, ensure_ascii=False).encode("utf-8") if payload is not None else None
request = Request(
f"{base_url.rstrip('/')}{path}",
data=body,
method=method,
headers={"Content-Type": "application/json"},
)
try:
with urlopen(request, timeout=timeout) as response:
raw = response.read().decode("utf-8")
return response.status, json.loads(raw) if raw else {}, False
except HTTPError as exc:
raw = exc.read().decode("utf-8")
return exc.code, json.loads(raw) if raw else {}, True
except URLError as exc:
return 0, str(exc), True
def rpc(base_url: str, method: str, payload: dict[str, Any], timeout: float) -> dict[str, Any]:
status, response, is_error = request_json(
"POST",
base_url,
"/rpc",
payload={"method": method, "payload": payload},
timeout=timeout,
)
if is_error or status != 200 or not isinstance(response, dict):
raise AssertionError(f"{method} failed: status={status}, response={response}")
return response
def compact_smoke_result(smoke: dict[str, Any], *, max_failures: int) -> dict[str, Any]:
failures = []
for row in smoke.get("results") or []:
if not isinstance(row, dict) or row.get("status") == "verified":
continue
entry = row.get("entry") if isinstance(row.get("entry"), dict) else {}
prop = entry.get("property") if isinstance(entry.get("property"), dict) else {}
requested = entry.get("requested_target") if isinstance(entry.get("requested_target"), dict) else {}
effective = entry.get("effective_target") if isinstance(entry.get("effective_target"), dict) else {}
failures.append(
{
"status": row.get("status"),
"target": requested.get("name") or requested.get("path"),
"requested_section": requested.get("section"),
"effective_section": effective.get("section"),
"property": prop.get("presentation") or prop.get("canonical_property") or prop.get("property"),
"old": prop.get("old"),
"test_value": prop.get("test_value"),
"diagnostics": row.get("diagnostics"),
}
)
if len(failures) >= max_failures:
break
return {
"schema": "onec_write_matrix_smoke_cli_summary.v1",
"status": smoke.get("status"),
"counts": smoke.get("counts"),
"adapter_report_path": smoke.get("path"),
"failures": failures,
}
def main() -> int:
parser = argparse.ArgumentParser(description="Build and optionally smoke-test saved-state form write matrix.")
parser.add_argument("--base-url", default="http://docker-gpu.cin.su:8011", help="1C REST adapter URL.")
parser.add_argument("--base-id", default="upo_test", help="Configured adapter base id.")
parser.add_argument("--table", default="ConfigCASSave", help="Saved-state SQL table.")
parser.add_argument("--file-name", default=DEFAULT_FORM_FILE, help="Saved-state form file name.")
parser.add_argument("--timeout", type=float, default=30.0, help="Request timeout in seconds.")
parser.add_argument("--max-candidates", type=int, default=50, help="Maximum safe write candidates to smoke.")
parser.add_argument("--learning-id", default="upo-test-write-matrix", help="Adapter-side write-learning id.")
parser.add_argument("--build-only", action="store_true", help="Only build the matrix; do not run apply_and_rollback smoke.")
parser.add_argument("--report", type=Path, help="Write full JSON report to file.")
parser.add_argument("--max-failures", type=int, default=20, help="Maximum failures to include in console summary.")
args = parser.parse_args()
base_payload = {
"base_id": args.base_id,
"table": args.table,
"file_name": args.file_name,
"timeout_seconds": int(args.timeout),
}
report: dict[str, Any] = {
"schema": "onec_write_matrix_smoke_cli_report.v1",
"base_url": args.base_url,
"base_id": args.base_id,
"table": args.table,
"file_name": args.file_name,
"passed": False,
}
try:
status, health, is_error = request_json("GET", args.base_url, f"/health?base_id={args.base_id}", timeout=args.timeout)
if is_error or status != 200 or not isinstance(health, dict) or health.get("status") != "ok":
raise AssertionError(f"health failed: status={status}, response={health}")
report["health"] = {"status": health.get("status"), "live_sql": health.get("live_sql")}
matrix = rpc(args.base_url, "metadata.form.write_matrix.build", base_payload, args.timeout)
report["matrix"] = matrix
if matrix.get("status") != "ok":
raise AssertionError(f"matrix build failed: {matrix.get('status')}")
if not args.build_only:
smoke_payload = {
**base_payload,
"allow_sql_saved_state_apply": True,
"allow_sql_saved_state_rollback": True,
"max_candidates": args.max_candidates,
"learning_id": args.learning_id,
}
smoke = rpc(args.base_url, "metadata.form.write_matrix.smoke", smoke_payload, max(args.timeout, args.max_candidates * args.timeout))
report["smoke"] = smoke
report["passed"] = smoke.get("status") == "ok"
else:
report["passed"] = True
except Exception as exc:
report["error"] = str(exc)
if args.report:
args.report.parent.mkdir(parents=True, exist_ok=True)
args.report.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
print(json.dumps(report, ensure_ascii=False, indent=2), file=sys.stderr)
return 1
if args.report:
args.report.parent.mkdir(parents=True, exist_ok=True)
args.report.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
summary = {
"schema": "onec_write_matrix_cli_summary.v1",
"passed": report["passed"],
"matrix_counts": (report.get("matrix") or {}).get("counts"),
"smoke": compact_smoke_result(report.get("smoke") or {}, max_failures=args.max_failures) if not args.build_only else None,
"report": str(args.report) if args.report else None,
}
print(json.dumps(summary, ensure_ascii=False, indent=2))
return 0 if report["passed"] else 1
if __name__ == "__main__":
raise SystemExit(main())