Files
cadSet/text-to-cad/skills/cad-router/tests/test_model_spec.py
T
2026-07-23 15:12:28 +08:00

110 lines
3.9 KiB
Python

from __future__ import annotations
import importlib.util
import json
import tempfile
import unittest
from pathlib import Path
from build123d import Box, Cylinder, Pos, export_step, import_step
SCRIPT_PATH = Path(__file__).resolve().parents[1] / "scripts" / "model_spec.py"
MODULE_SPEC = importlib.util.spec_from_file_location("cad_router_model_spec", SCRIPT_PATH)
assert MODULE_SPEC is not None and MODULE_SPEC.loader is not None
MODEL_SPEC = importlib.util.module_from_spec(MODULE_SPEC)
MODULE_SPEC.loader.exec_module(MODEL_SPEC)
class ModelSpecTests(unittest.TestCase):
def test_import_step_exact_base_round_trip(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
source = root / "teacher.step"
export_step(Box(30, 20, 10) - Pos(0, 0, -1) * Cylinder(3, 12), source)
result = MODEL_SPEC.import_step_model(
source,
root / "task",
"round_trip",
)
verification = MODEL_SPEC.verify_model(
Path(result["model_spec"])
)
self.assertTrue(verification["valid"])
self.assertEqual(
verification["checks"][0]["check"],
"exact_base_geometry_signature",
)
self.assertTrue((root / "task" / "round_trip.step").is_file())
self.assertTrue((root / "task" / "round_trip.stl").is_file())
def test_parameterized_cut_rebuilds_from_json(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
source = root / "source.step"
export_step(Box(40, 30, 12), source)
imported = MODEL_SPEC.import_step_model(
source,
root / "task",
"parameterized_cut",
)
spec_path = Path(imported["model_spec"])
payload = json.loads(spec_path.read_text(encoding="utf-8"))
payload["parameters"] = {
"bore_diameter": {
"value": 6,
"unit": "mm",
"role": "central_bore_diameter",
},
"bore_length": {
"value": 16,
"unit": "mm",
"role": "through_cut_length",
},
}
payload["modifications"] = [
{
"id": "central_bore",
"operation": "cut_cylinder",
"axis": "z",
"center": [0, 0, 0],
"diameter": {"parameter": "bore_diameter"},
"length": {"parameter": "bore_length"},
"enabled": True,
}
]
spec_path.write_text(
json.dumps(payload, ensure_ascii=False, indent=2) + "\n",
encoding="utf-8",
)
first = MODEL_SPEC.build_model(spec_path)
first_volume = first["facts"]["volume_mm3"]
MODEL_SPEC.set_parameter(spec_path, "bore_diameter", 12)
second = MODEL_SPEC.build_model(spec_path)
self.assertLess(second["facts"]["volume_mm3"], first_volume)
rebuilt = import_step(second["step"])
self.assertGreater(len(rebuilt.faces()), 0)
def test_exact_base_rejects_changed_source(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
source = root / "source.step"
export_step(Box(10, 10, 10), source)
imported = MODEL_SPEC.import_step_model(
source,
root / "task",
"checksum_guard",
)
export_step(Box(12, 10, 10), root / "task" / "source.step")
with self.assertRaises(MODEL_SPEC.ModelSpecError):
MODEL_SPEC.build_model(Path(imported["model_spec"]))
if __name__ == "__main__":
unittest.main()