Spaces:
Running
Running
| """Phase A4 — codegen integration, offline (SPEC_OPTION_A.md §10). | |
| Exercises app.codegen_assemble (the backend post-processing layer) with CANNED | |
| model build_spec output — no live Modal. The full server-side path | |
| (build_spec text -> kernel -> repair -> assembler -> contract) must yield JSON | |
| that passes the existing frontend contract and the metric gate. Also covers | |
| passthrough, primitive fallback, and model-free regeneration. The LIVE model | |
| call is FLAGGED for the user (not tested here). | |
| """ | |
| import importlib.util | |
| import json | |
| import sys | |
| from pathlib import Path | |
| import pytest | |
| ROOT = Path(__file__).resolve().parent.parent | |
| if str(ROOT) not in sys.path: | |
| sys.path.insert(0, str(ROOT)) | |
| import app # noqa: E402 | |
| from geometry import metrics # noqa: E402 | |
| _spec = importlib.util.spec_from_file_location("schema_mod", ROOT / "tests" / "test_schema.py") | |
| _schema = importlib.util.module_from_spec(_spec) | |
| _spec.loader.exec_module(_schema) | |
| validate_shape = _schema.validate_shape | |
| FIX = ROOT / "tests" / "fixtures" / "codegen" | |
| SPECS = {"octahedron": "spec_octahedron.json", | |
| "cube": "spec_cube.json", | |
| "cylindrical_vent": "spec_cylindrical_vent.json"} | |
| def raw_spec(name): | |
| return (FIX / SPECS[name]).read_text() | |
| def regularity(name): | |
| return json.loads(raw_spec(name))["regularity"] | |
| # --------------------------------------------------------------------------- # | |
| # full codegen path on canned build_spec output # | |
| # --------------------------------------------------------------------------- # | |
| def test_codegen_assemble_produces_valid_contract(name): | |
| out = app.codegen_assemble(raw_spec(name)) | |
| contract = json.loads(out) | |
| assert validate_shape(contract) == [] | |
| verts = contract["geometry"]["vertices"] | |
| faces = [f["indices"] for f in contract["geometry"]["faces"]] | |
| ok, fails = metrics.passes(verts, faces, regularity(name)) | |
| assert ok, fails | |
| # --------------------------------------------------------------------------- # | |
| # passthrough of non-build_spec responses # | |
| # --------------------------------------------------------------------------- # | |
| def test_guided_response_passthrough(): | |
| raw = json.dumps({"type": "guided_response", "message": "hi", "suggestions": ["cube"]}) | |
| assert json.loads(app.codegen_assemble(raw)) == json.loads(raw) | |
| def test_error_passthrough(): | |
| raw = json.dumps({"error": "endpoint_unreachable"}) | |
| assert json.loads(app.codegen_assemble(raw)) == json.loads(raw) | |
| def test_malformed_becomes_error(): | |
| assert json.loads(app.codegen_assemble("not json at all"))["error"] == "model_invalid_json" | |
| def test_non_buildspec_dict_passthrough(): | |
| raw = json.dumps({"shape_id": "x", "geometry": {"vertices": [], "faces": []}}) | |
| assert json.loads(app.codegen_assemble(raw)) == json.loads(raw) | |
| # --------------------------------------------------------------------------- # | |
| # generator failure -> primitive fallback # | |
| # --------------------------------------------------------------------------- # | |
| def test_broken_generator_falls_back_to_primitive(): | |
| spec = json.loads(raw_spec("octahedron")) | |
| spec["generator"]["code"] = "def build(p):\n raise ValueError('boom')\n" | |
| out = app.codegen_assemble(json.dumps(spec)) | |
| contract = json.loads(out) | |
| assert validate_shape(contract) == [] # recovered via primitives.REGISTRY['octahedron'] | |
| assert contract["shape_id"] == "octahedron" | |
| def test_broken_generator_unknown_shape_errors(): | |
| spec = json.loads(raw_spec("octahedron")) | |
| spec["shape_id"] = "no_such_primitive" | |
| spec["generator"]["code"] = "def build(p):\n raise ValueError('boom')\n" | |
| out = app.codegen_assemble(json.dumps(spec)) | |
| assert json.loads(out)["error"] == "model_invalid_json" | |
| # --------------------------------------------------------------------------- # | |
| # caching for model-free regeneration # | |
| # --------------------------------------------------------------------------- # | |
| def test_assemble_caches_spec_for_regen(): | |
| app._SPEC_CACHE.pop("k1", None) | |
| app.codegen_assemble(raw_spec("cylindrical_vent"), cache_key="k1") | |
| assert "k1" in app._SPEC_CACHE | |
| # Model-free regen: re-run the cached generator with more vanes. | |
| from geometry import assembler | |
| contract, _ = assembler.build(app._SPEC_CACHE["k1"], {"vanes": 12, "height": 2.0}) | |
| assert validate_shape(contract) == [] | |
| assert len([c for c in contract["components"] if c["id"].startswith("vane_")]) == 12 | |
| # --------------------------------------------------------------------------- # | |
| # model-free regeneration helper (_regen_modelfree) # | |
| # --------------------------------------------------------------------------- # | |
| def test_regen_modelfree_good_spec(): | |
| spec = json.loads(raw_spec("cylindrical_vent")) | |
| out = app._regen_modelfree(spec, {"vanes": 10, "height": 2.0}) | |
| contract = json.loads(out) | |
| assert validate_shape(contract) == [] | |
| assert len([c for c in contract["components"] if c["id"].startswith("vane_")]) == 10 | |
| def test_regen_modelfree_broken_generator_falls_back_to_primitive(): | |
| spec = json.loads(raw_spec("octahedron")) | |
| spec["generator"]["code"] = "def build(p):\n raise ValueError('x')\n" | |
| out = app._regen_modelfree(spec, None) # shape_id 'octahedron' is a primitive | |
| assert out is not None and validate_shape(json.loads(out)) == [] | |
| def test_regen_modelfree_broken_unknown_returns_none(): | |
| spec = json.loads(raw_spec("octahedron")) | |
| spec["shape_id"] = "no_such_primitive" | |
| spec["generator"]["code"] = "def build(p):\n raise ValueError('x')\n" | |
| assert app._regen_modelfree(spec, None) is None | |
| # --------------------------------------------------------------------------- # | |
| # default path is unchanged when the flag is off # | |
| # --------------------------------------------------------------------------- # | |
| def test_codegen_flag_defaults_off(): | |
| assert app.CODEGEN is False | |