from __future__ import annotations import importlib.util import json import math import tempfile import unittest from pathlib import Path from uuid import uuid4 from core.software_contracts import SoftwareContractError, get_contract ROOT = Path(__file__).resolve().parents[1] WORKER_PATH = ROOT / "windows-node" / "adapters" / "cad.drawing.author@v1" / "worker.py" SPEC = importlib.util.spec_from_file_location("zcbot_cad2d_worker", WORKER_PATH) assert SPEC and SPEC.loader worker = importlib.util.module_from_spec(SPEC) SPEC.loader.exec_module(worker) def request() -> dict: return { "schema_version": 1, "inputs": [], "outputs": [], "operation": {"drawing": { "type": "floor_plan", "recipe_version": 1, "title": "中文平面图", "unit": "mm", "sheet": {"size": "A3", "orientation": "landscape", "scale": 50}, "walls": [ {"id":"wall1","start":[0,0],"end":[10000,0],"thickness":240,"kind":"exterior"}, {"id":"wall2","start":[10000,0],"end":[10000,7000],"thickness":240,"kind":"exterior"}, {"id":"wall3","start":[10000,7000],"end":[0,7000],"thickness":240,"kind":"exterior"}, {"id":"wall4","start":[0,7000],"end":[0,0],"thickness":240,"kind":"exterior"}, ], "openings": [{"id":"door1","wall_id":"wall1","offset":800,"width":900,"type":"single_door","hinge":"start","swing":"in"}], "areas": [{"id":"area1","boundary":[[0,0],[10000,0],[10000,7000],[0,7000],[0,0]],"name":"实验室","label_position":[5000,3500],"show_area":True}], "objects": [{"id":"bench1","type":"lab_bench","position":[5000,1800],"label":"试验台"}], "dimensions": [{"id":"dim1","type":"horizontal","start":[0,0],"end":[10000,0],"offset":-600}], "notes": [{"id":"note1","position":[7000,6000],"text":"尺寸单位:mm"}], }}, } class Cad2dContractTests(unittest.TestCase): def test_contract_is_runtime_only_fixed_output_workspace(self) -> None: contract = get_contract("cad.drawing.author@v1") normalized, digest = contract.normalize_request(request()) self.assertEqual(contract.display_name, "二维 CAD 工程制图") self.assertEqual(contract.output_namespace, "cad2d") self.assertFalse(contract.default_enrollment) self.assertEqual(contract.input_policy["max_count"], 0) self.assertEqual(contract.workspace.state_output, "drawing_dxf") self.assertEqual(contract.workspace.preview_outputs, ("drawing_png",)) self.assertEqual(contract.feature(normalized), "floor_plan") self.assertEqual(len(digest), 64) self.assertEqual(set(contract.expected_outputs(normalized)), { "drawing_dxf", "drawing_svg", "drawing_png", "drawing_pdf", "drawing_recipe", "drawing_manifest", "validation_report", "provenance", }) def test_contract_rejects_inputs_scripts_paths_fonts_layers_and_nonfinite(self) -> None: contract = get_contract("cad.drawing.author@v1") mutations = [("script", "x"), ("path", "C:/secret"), ("font", "Arial"), ("layer", "custom")] for key, value in mutations: value_request = request(); value_request["operation"]["drawing"][key] = value with self.subTest(key), self.assertRaises(SoftwareContractError): contract.normalize_request(value_request) value_request = request(); value_request["operation"]["drawing"]["walls"][0]["start"][0] = math.inf with self.assertRaises(SoftwareContractError): contract.normalize_request(value_request) value_request = request(); value_request["inputs"] = [{"key":"x","artifact_id":str(uuid4())}] with self.assertRaises(SoftwareContractError): contract.normalize_request(value_request) def test_geometry_gates_and_warnings(self) -> None: value = request()["operation"]["drawing"] duplicate = json.loads(json.dumps(value)); duplicate["notes"][0]["id"] = "wall1" with self.assertRaisesRegex(worker.DrawingError, "CAD2D_DUPLICATE_ID"): worker._validate_and_normalize(duplicate) outside = json.loads(json.dumps(value)); outside["openings"][0]["offset"] = 9900 with self.assertRaisesRegex(worker.DrawingError, "CAD2D_OPENING_OUTSIDE_WALL"): worker._validate_and_normalize(outside) overlap = json.loads(json.dumps(value)); overlap["openings"].append({"id":"door2","wall_id":"wall1","offset":1000,"width":900,"type":"opening"}) with self.assertRaisesRegex(worker.DrawingError, "CAD2D_OPENINGS_OVERLAP"): worker._validate_and_normalize(overlap) unclosed = json.loads(json.dumps(value)); unclosed["areas"][0]["boundary"][-1] = [1,0] with self.assertRaisesRegex(worker.DrawingError, "CAD2D_AREA_NOT_CLOSED"): worker._validate_and_normalize(unclosed) collision = json.loads(json.dumps(value)); collision["objects"][0]["position"] = [1000,400] _, warnings, _ = worker._validate_and_normalize(collision) self.assertIn("CAD2D_DOOR_SWING_OBJECT_COLLISION", {item["code"] for item in warnings}) def test_worker_writes_reopenable_fixed_package_deterministically(self) -> None: manifests = [] for _ in range(3): with tempfile.TemporaryDirectory() as directory: root = Path(directory); (root / "request").mkdir() (root / "request" / "request.json").write_text(json.dumps({"job_id":str(uuid4()),"lease_id":str(uuid4()),"request_digest":"a"*64,"request":request()}, ensure_ascii=False), encoding="utf-8") artifacts = worker.run(root) self.assertEqual({item["filename"] for item in artifacts}, set(worker.OUTPUT_MEDIA)) reopened = worker._reopen_dxf(root / "output" / "drawing.dxf") self.assertEqual(reopened["version"], "R2013") self.assertEqual(reopened["layers"], sorted(worker.LAYERS)) report = json.loads((root / "output" / "validation-report.json").read_text(encoding="utf-8")) self.assertTrue(report["passed"]) self.assertGreater((root / "output" / "drawing.png").stat().st_size, 1000) manifests.append(json.loads((root / "output" / "drawing-manifest.json").read_text(encoding="utf-8"))) self.assertEqual(manifests[0], manifests[1]) self.assertEqual(manifests[1], manifests[2]) if __name__ == "__main__": unittest.main()