|
|
@@ -57,6 +57,35 @@ def test_composite_runtime_db_failure_blocks_local_export(tmp_path):
|
|
|
assert export.calls == []
|
|
|
|
|
|
|
|
|
+def test_composite_runtime_reads_primary_before_local_export(tmp_path):
|
|
|
+ primary = LocalRuntimeFileStore(tmp_path / "primary")
|
|
|
+ export = LocalRuntimeFileStore(tmp_path / "export")
|
|
|
+ primary.prepare_run("run_001")
|
|
|
+ export.prepare_run("run_001")
|
|
|
+ primary.write_json("run_001", "final_output.json", {"run_id": "run_001", "source": "db"})
|
|
|
+ export.write_json("run_001", "final_output.json", {"run_id": "run_001", "source": "local"})
|
|
|
+ primary.append_jsonl("run_001", "run_events.jsonl", [{"run_id": "run_001", "source": "db"}])
|
|
|
+ export.append_jsonl("run_001", "run_events.jsonl", [{"run_id": "run_001", "source": "local"}])
|
|
|
+ store = CompositeRuntimeStore(primary, export)
|
|
|
+
|
|
|
+ assert store.read_json("run_001", "final_output.json")["source"] == "db"
|
|
|
+ assert store.read_jsonl("run_001", "run_events.jsonl")[0]["source"] == "db"
|
|
|
+ assert store.file_status("run_001")["final_output.json"] is True
|
|
|
+
|
|
|
+
|
|
|
+def test_composite_runtime_falls_back_to_local_export_on_primary_read_failure(tmp_path):
|
|
|
+ primary = _ReadFailingRuntimeStore()
|
|
|
+ export = LocalRuntimeFileStore(tmp_path / "export")
|
|
|
+ export.prepare_run("run_001")
|
|
|
+ export.write_json("run_001", "final_output.json", {"run_id": "run_001", "source": "local"})
|
|
|
+ export.append_jsonl("run_001", "run_events.jsonl", [{"run_id": "run_001", "source": "local"}])
|
|
|
+ store = CompositeRuntimeStore(primary, export)
|
|
|
+
|
|
|
+ assert store.read_json("run_001", "final_output.json")["source"] == "local"
|
|
|
+ assert store.read_jsonl("run_001", "run_events.jsonl")[0]["source"] == "local"
|
|
|
+ assert store.file_status("run_001")["final_output.json"] is True
|
|
|
+
|
|
|
+
|
|
|
def test_run_service_success_records_run_policy_and_lifecycle_events(tmp_path):
|
|
|
runtime = _SpyRuntimeStore(tmp_path / "runtime")
|
|
|
demand_source = FakeDemandSource(real_source_payload(demand_content_id=123))
|
|
|
@@ -477,6 +506,17 @@ class _FakeRuntimeStore(_SpyRuntimeStore):
|
|
|
return self.run_dir(run_id) / filename
|
|
|
|
|
|
|
|
|
+class _ReadFailingRuntimeStore(_FakeRuntimeStore):
|
|
|
+ def read_json(self, run_id: str, filename: str) -> dict[str, Any]:
|
|
|
+ raise FileNotFoundError(filename)
|
|
|
+
|
|
|
+ def read_jsonl(self, run_id: str, filename: str) -> list[dict[str, Any]]:
|
|
|
+ raise FileNotFoundError(filename)
|
|
|
+
|
|
|
+ def file_status(self, run_id: str) -> dict[str, bool]:
|
|
|
+ raise FileNotFoundError(run_id)
|
|
|
+
|
|
|
+
|
|
|
class _MalformedQueryVariantClient:
|
|
|
def generate_variant(self, *, seed_term: str, evidence_context: dict[str, Any]):
|
|
|
return None
|