From 977d9a92589eb602da6d36202ac8d0a7180dfc57 Mon Sep 17 00:00:00 2001 From: gilesb Date: Mon, 12 Jan 2026 12:05:13 +0000 Subject: [PATCH] Fix artifact dict key mismatch (hash -> cid) Template runs/detail.html expects artifact.cid but code provided artifact.hash, causing UndefinedError when viewing run details. - Change run_service.get_run_artifacts to return 'cid' key - Change runs.py router inline artifact creation to use 'cid' key - Add regression tests for artifact data structure Co-Authored-By: Claude Opus 4.5 --- app/routers/runs.py | 2 +- app/services/run_service.py | 2 +- tests/test_run_artifacts.py | 57 +++++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 tests/test_run_artifacts.py diff --git a/app/routers/runs.py b/app/routers/runs.py index 736479d..0ca5f81 100644 --- a/app/routers/runs.py +++ b/app/routers/runs.py @@ -258,7 +258,7 @@ async def get_run( except Exception: pass artifacts.append({ - "hash": output_cid, + "cid": output_cid, "step_name": "Output", "media_type": media_type or "application/octet-stream", }) diff --git a/app/services/run_service.py b/app/services/run_service.py index a0ad549..8eda7aa 100644 --- a/app/services/run_service.py +++ b/app/services/run_service.py @@ -547,7 +547,7 @@ class RunService: path = self.cache.get_by_cid(cid) if path and path.exists(): return { - "hash": cid, + "cid": cid, "size_bytes": path.stat().st_size, "media_type": detect_media_type(path), "role": role, diff --git a/tests/test_run_artifacts.py b/tests/test_run_artifacts.py new file mode 100644 index 0000000..f028385 --- /dev/null +++ b/tests/test_run_artifacts.py @@ -0,0 +1,57 @@ +""" +Tests for run artifacts data structure. + +Bug found 2026-01-12: runs/detail.html template expects artifact.cid +but the route provides artifact.hash, causing UndefinedError. +""" + +import pytest +from pathlib import Path + + +class TestArtifactDataStructure: + """Tests for artifact dict keys matching template expectations.""" + + def test_template_expects_cid_key(self) -> None: + """Template uses artifact.cid - verify this expectation.""" + template_path = Path('/home/giles/art/art-celery/app/templates/runs/detail.html') + content = template_path.read_text() + + # Template uses artifact.cid in multiple places + assert 'artifact.cid' in content, "Template should reference artifact.cid" + + def test_run_service_artifacts_have_cid_key(self) -> None: + """ + Regression test: get_run_artifacts must return dicts with 'cid' key. + + Bug: Service returned artifacts with 'hash' key but template expected 'cid'. + Fix: Changed service to use 'cid' key for consistency. + """ + # Read the run_service.py file and check it uses 'cid' not 'hash' + service_path = Path('/home/giles/art/art-celery/app/services/run_service.py') + content = service_path.read_text() + + # Find the get_artifact_info function and check it returns 'cid' + # The function should have: "cid": cid, not "hash": cid + assert '"cid": cid' in content or "'cid': cid" in content, \ + "get_run_artifacts should return artifacts with 'cid' key, not 'hash'" + + def test_router_artifacts_have_cid_key(self) -> None: + """ + Regression test: inline artifact creation in router must use 'cid' key. + + Bug: Router created artifacts with 'hash' key but template expected 'cid'. + """ + router_path = Path('/home/giles/art/art-celery/app/routers/runs.py') + content = router_path.read_text() + + # Check that artifacts.append uses 'cid' key + # Should have: "cid": output_cid, not "hash": output_cid + # Count occurrences of the patterns + hash_pattern_count = content.count('"hash": output_cid') + cid_pattern_count = content.count('"cid": output_cid') + + assert cid_pattern_count > 0, \ + "Router should create artifacts with 'cid' key" + assert hash_pattern_count == 0, \ + "Router should not use 'hash' key for artifacts (template expects 'cid')"