from __future__ import annotations import json import tempfile import unittest from pathlib import Path from types import SimpleNamespace from typing import Optional from arr_processing.config import ResultVerificationConfig from arr_processing.runtime_writeback import ( AgentRuntimeWritebackAdapter, FrozenAgentResult, RuntimeWritebackError, ) from arr_web.agent_writeback_runtime import load_processor_policy PROJECT_ROOT = Path(__file__).resolve().parents[1] KEY = b"runtime-writeback-test-key-at-least-32-bytes" class Publisher: def __init__(self): self.calls = [] def publish(self, **kwargs): self.calls.append(kwargs) return SimpleNamespace(object_key="exchange/" + kwargs["file_handle"]) class Response: def __init__(self, status_code, payload=None): self.status_code = status_code self.payload = payload def json(self): if self.payload is None: raise ValueError("no json") return self.payload class Http: def __init__(self, responses): self.responses = list(responses) self.calls = [] def post(self, url, **kwargs): self.calls.append((url, kwargs)) return self.responses.pop(0) def frozen_payload(root: Path, *, external_path: Optional[Path] = None) -> bytes: daily = root / "7.27.xlsx" result = root / "result.json" structured = root / "structured-result.json" daily.write_bytes(b"xlsx") result.write_bytes(b'{"status":"success"}') structured.write_bytes(b'{"records":[]}') daily_path = external_path or daily payload = { "contract_version": "arr-opera-daily-agent-1", "job_id": "arr-job-runtime-001", "source_file_id": "arr-source-runtime-001", "status": "success", "business_date": "2026-07-27", "processor_result": { "version": "3.0", "status": "success", "business_date": "2026-07-27", "message": "ok", "metrics": {}, "outputs": {}, "errors": [], }, "files": { "daily_report": {"filename": daily_path.name, "local_path": str(daily_path)}, "result_json": {"filename": result.name, "local_path": str(result)}, "structured_result": { "filename": structured.name, "local_path": str(structured), }, "exception_report": None, }, } return json.dumps(payload, separators=(",", ":")).encode("utf-8") def failed_frozen_payload(root: Path) -> bytes: result = root / "result.json" structured = root / "structured-result.json" exception = root / "exception.xlsx" result.write_bytes(b'{"status":"failed"}') structured.write_bytes(b'{"records":[]}') exception.write_bytes(b"xlsx") payload = { "contract_version": "arr-opera-daily-agent-1", "job_id": "arr-job-runtime-002", "source_file_id": "arr-source-runtime-002", "status": "failed", "business_date": "", "processor_result": { "version": "3.0", "status": "failed", "business_date": None, "message": "input rejected", "metrics": {}, "outputs": {}, "errors": [], }, "files": { "daily_report": None, "result_json": {"filename": result.name, "local_path": str(result)}, "structured_result": { "filename": structured.name, "local_path": str(structured), }, "exception_report": { "filename": exception.name, "local_path": str(exception), }, }, } return json.dumps(payload, separators=(",", ":")).encode("utf-8") class RuntimeWritebackTests(unittest.TestCase): def test_publish_sign_and_retry_reuses_identical_callback_bytes(self) -> None: with tempfile.TemporaryDirectory() as temporary: root = Path(temporary) publisher = Publisher() http = Http( [ Response(503), Response( 200, { "ok": True, "data": { "status": "committed", "job_id": "arr-job-runtime-001", "business_date": "2026-07-27", "daily_version_id": 9, "version_no": 1, "callback_replayed": False, }, }, ), ] ) config = ResultVerificationConfig("runtime-key-1", KEY) adapter = AgentRuntimeWritebackAdapter( publisher=publisher, signer=config.signer(), http_client=http, callback_url="https://arr.example.test/api/integrations/super-agent/results", processor_version="3.0.0", rule_set_sha256="a" * 64, retry_delay_seconds=0, ) receipt = adapter.deliver( frozen_payload(root), output_root=root, attempt_no=1, remote_run_id="run-runtime-001", ) self.assertEqual(receipt["status"], "committed") self.assertEqual(len(publisher.calls), 3) self.assertEqual(http.calls[0][1]["content"], http.calls[1][1]["content"]) verified = config.verifier().verify(http.calls[0][1]["content"]) self.assertEqual(verified.result.job_id, "arr-job-runtime-001") self.assertEqual(verified.result.remote_run_id, "run-runtime-001") self.assertIsNone(verified.result.artifacts["exception_report"]) self.assertEqual( set(call["metadata"]["arr-role"] for call in publisher.calls), {"daily_report", "result_json", "structured_result_json"}, ) def test_output_paths_are_confined_to_the_approved_root(self) -> None: with tempfile.TemporaryDirectory() as temporary, tempfile.TemporaryDirectory() as other: root = Path(temporary) outside = Path(other) / "outside.xlsx" outside.write_bytes(b"xlsx") with self.assertRaises(RuntimeWritebackError) as caught: FrozenAgentResult.parse( frozen_payload(root, external_path=outside), output_root=root, ) self.assertEqual(caught.exception.code, "RUNTIME_OUTPUT_INVALID") def test_empty_profile_business_date_normalizes_to_null(self) -> None: with tempfile.TemporaryDirectory() as temporary: root = Path(temporary) result = FrozenAgentResult.parse( failed_frozen_payload(root), output_root=root, ) self.assertEqual(result.status, "failed") self.assertIsNone(result.business_date) def test_runtime_loads_the_same_allowlisted_processor_identity(self) -> None: policy = load_processor_policy(PROJECT_ROOT) self.assertEqual(policy.processor_version, "3.0.0") self.assertEqual(len(policy.rule_set_sha256), 64) if __name__ == "__main__": unittest.main()