82 lines
3.6 KiB
Python
82 lines
3.6 KiB
Python
import math
|
|
|
|
import pytest
|
|
|
|
from app.cad_agent.application.authoring_contract import AuthoringDocument
|
|
from app.cad_agent.application.authoring_compiler import AuthoringCompiler, AuthoringCompileError
|
|
|
|
|
|
def test_model_cannot_submit_runtime_identity():
|
|
with pytest.raises(ValueError, match="AUTHOR_FORBIDDEN_FIELD"):
|
|
AuthoringDocument.model_validate({"feature_id": "feature_001", "bodies": []})
|
|
|
|
|
|
@pytest.mark.parametrize("field", ["task_id", "revision_id", "stable_id", "selector_tokens", "selector token", "host_face", "mirror_plane"])
|
|
def test_model_cannot_submit_any_internal_identity_variant(field):
|
|
with pytest.raises(ValueError, match="AUTHOR_FORBIDDEN_FIELD"):
|
|
AuthoringDocument.model_validate({field: "internal", "bodies": []})
|
|
|
|
|
|
def test_authoring_rejects_non_mm_and_non_finite_values():
|
|
with pytest.raises(ValueError):
|
|
AuthoringDocument.model_validate({"units": "in", "bodies": []})
|
|
with pytest.raises(ValueError, match="non-finite"):
|
|
AuthoringDocument.model_validate({"bodies": [{"name": "main", "features": [{"name": "base", "operation": "box_add", "params": {"length_mm": math.nan}}]}]})
|
|
|
|
|
|
@pytest.mark.parametrize("sketch", [
|
|
{
|
|
"workplane": {"origin_mm": [0, 0, 0], "x_dir": [1, 0, 0], "normal": [0, 0, 1]},
|
|
"profiles": [{"type": "circle", "diameter_mm": 20}],
|
|
},
|
|
{
|
|
"workplane": {"origin_mm": [0, 0, 0], "x_dir": [1, 0, 0], "normal": [0, 0, 1]},
|
|
"profile": {"type": "circle", "radius_mm": 10},
|
|
},
|
|
])
|
|
def test_authoring_rejects_runtime_or_ambiguous_sketch_shapes(sketch):
|
|
with pytest.raises(ValueError):
|
|
AuthoringDocument.model_validate({
|
|
"bodies": [{"name": "main", "features": [{
|
|
"name": "base", "operation": "extrude_add_blind", "params": {"distance_mm": 8}, "sketch": sketch,
|
|
}]}],
|
|
})
|
|
|
|
|
|
def test_authoring_rejects_selector_fields_that_are_not_declarative_source_intent():
|
|
with pytest.raises(ValueError):
|
|
AuthoringDocument.model_validate({
|
|
"bodies": [{"name": "main", "features": [{
|
|
"name": "base", "operation": "cylinder_add", "params": {},
|
|
"selectors": [{"kind": "face", "source": "other.top_planar_face", "role": "host_face"}],
|
|
}]}],
|
|
})
|
|
|
|
|
|
def test_compiler_assigns_deterministic_ids_and_orders_dependencies():
|
|
document = {"bodies": [{"name": "main", "features": [
|
|
{"name": "hole", "operation": "hole_blind", "depends_on": ["base"]},
|
|
{"name": "base", "operation": "extrude_add_blind"},
|
|
]}]}
|
|
runtime, audit = AuthoringCompiler(lambda operation: {"atomic_id": operation}).compile(document)
|
|
assert [item["id"] for item in runtime["features"]] == ["feature_002", "feature_001"]
|
|
assert runtime["features"][1]["depends_on"] == ["feature_002"]
|
|
assert audit["feature_ids"] == {"hole": "feature_001", "base": "feature_002"}
|
|
|
|
|
|
def test_compiler_rejects_unknown_operation():
|
|
with pytest.raises(AuthoringCompileError) as error:
|
|
AuthoringCompiler(lambda _: (_ for _ in ()).throw(ValueError("unknown"))).compile({
|
|
"bodies": [{"name": "main", "features": [{"name": "base", "operation": "bad"}]}]
|
|
})
|
|
assert error.value.code == "OPERATION_UNSUPPORTED"
|
|
|
|
|
|
def test_compiler_preserves_the_forbidden_field_error_code():
|
|
with pytest.raises(AuthoringCompileError) as error:
|
|
AuthoringCompiler(lambda operation: {"atomic_id": operation}).compile({
|
|
"bodies": [{"name": "main", "features": [{"name": "base", "operation": "box_add", "params": {}}]}],
|
|
"feature_id": "feature_001",
|
|
})
|
|
assert error.value.code == "AUTHOR_FORBIDDEN_FIELD"
|