from pathlib import Path from tempfile import TemporaryDirectory from app.cad_agent.adapters.sqlite_repository import SqliteTaskRepository from app.cad_agent.domain.state import transition from app.services.agent_service import AgentService def test_result_payload_contains_all_published_artifacts() -> None: payload = AgentService._result_payload("cad_abcdefghijkl", { "lifecycle": "completed", "published_revision": "rev_prefix", "revisions": [{ "revision_id": "rev_prefix", "cdsl_path": "revisions/rev_prefix/model.cdsl.json", "step_path": "revisions/rev_prefix/model.step", "glb_path": "revisions/rev_prefix/model.glb", "report_path": "revisions/rev_prefix/rebuild-report.json", }], }) assert payload == { "taskId": "cad_abcdefghijkl", "revisionId": "rev_prefix", "cdslPath": "revisions/rev_prefix/model.cdsl.json", "stepPath": "revisions/rev_prefix/model.step", "glbPath": "revisions/rev_prefix/model.glb", "reportPath": "revisions/rev_prefix/rebuild-report.json", "summary": "CDSL CAD model", "referenceIds": [], "engine": "cdsl_only", "lifecycle": "completed", } def test_best_effort_projection_retains_prefix_artifact_paths() -> None: with TemporaryDirectory() as temporary: repository = SqliteTaskRepository(Path(temporary) / "state.sqlite3") state = repository.create_task("cad_abcdefghijkl", "make a plate") state = transition(state, "analysis_written", requirements_path="documents/requirements-analysis.json") assert repository.compare_and_swap(state) state = transition(state, "authoring_written", authoring_path="documents/authoring-cdsl-attempt-01.json") assert repository.compare_and_swap(state) state = transition(state, "compiled", runtime_cdsl_path="documents/runtime-cdsl-attempt-01.json") assert repository.compare_and_swap(state) state = transition(state, "repair_required", active_revision="rev_prefix") assert repository.compare_and_swap(state, events=[{ "event": "build_failed", "revision_id": "rev_prefix", "paths": { "model.cdsl.json": "revisions/rev_prefix/model.cdsl.json", "model.step": "revisions/rev_prefix/model.step", "model.glb": "revisions/rev_prefix/model.glb", "rebuild-report.json": "revisions/rev_prefix/rebuild-report.json", }, }]) state = transition(state, "repair_started", repair_count=1) assert repository.compare_and_swap(state) state = transition(state, "repair_required") assert repository.compare_and_swap(state) state = transition(state, "repair_started", repair_count=2) assert repository.compare_and_swap(state) state = transition(state, "publish_best_effort") assert repository.compare_and_swap(state, events=[{"event": "repair_budget_exhausted"}]) state = transition(state, "published") assert repository.compare_and_swap(state) projection = repository.get_task_projection("cad_abcdefghijkl") assert projection is not None result = AgentService._result_payload("cad_abcdefghijkl", projection) assert result is not None assert result["revisionId"] == "rev_prefix" assert result["stepPath"] == "revisions/rev_prefix/model.step" def test_result_payload_rejects_incomplete_artifact_sets() -> None: assert AgentService._result_payload("cad_abcdefghijkl", { "published_revision": "rev_prefix", "revisions": [{"revision_id": "rev_prefix", "step_path": "revisions/rev_prefix/model.step"}], }) is None