feat: sync latest ARR implementation
This commit is contained in:
193
tests/test_arr_web_job_trace.py
Normal file
193
tests/test_arr_web_job_trace.py
Normal file
@@ -0,0 +1,193 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import unittest
|
||||
from datetime import datetime, timedelta, timezone
|
||||
|
||||
from arr_web.job_trace import build_job_trace
|
||||
|
||||
|
||||
class JobTraceTests(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
self.started = datetime(2026, 7, 29, 11, 0, tzinfo=timezone.utc)
|
||||
|
||||
def test_programmatic_success_exposes_persisted_safe_trace(self) -> None:
|
||||
finished = self.started + timedelta(seconds=8)
|
||||
trace = build_job_trace(
|
||||
{
|
||||
"job_id": "arrjob-trace-001",
|
||||
"run_status": "accepted",
|
||||
"public_status": "succeeded",
|
||||
"delivery_mode": "artifact_callback",
|
||||
"business_date": "2026-07-29",
|
||||
"created_at": self.started,
|
||||
"updated_at": finished,
|
||||
"validated_at": finished,
|
||||
"finished_at": finished,
|
||||
"filename": "source.xml",
|
||||
"byte_size": 2048,
|
||||
"source_sha256": "a" * 64,
|
||||
"requested_processor_version": "3.0.0",
|
||||
"delivered_processor_version": "3.0.0",
|
||||
"object_key": "must/not/escape",
|
||||
},
|
||||
attempts=[
|
||||
{
|
||||
"attempt_no": 1,
|
||||
"attempt_status": "succeeded",
|
||||
"remote_run_id": None,
|
||||
"created_at": self.started + timedelta(seconds=1),
|
||||
"started_at": self.started + timedelta(seconds=2),
|
||||
"finished_at": finished,
|
||||
}
|
||||
],
|
||||
deliveries=[
|
||||
{
|
||||
"delivery_key": "local-arrjob-trace-001-a1",
|
||||
"attempt_no": 1,
|
||||
"delivery_status": "committed",
|
||||
"result_status": "success",
|
||||
"processor_version": "3.0.0",
|
||||
"business_date": "2026-07-29",
|
||||
"daily_version_id": 41,
|
||||
"received_at": self.started + timedelta(seconds=5),
|
||||
"validated_at": self.started + timedelta(seconds=6),
|
||||
"committed_at": finished,
|
||||
}
|
||||
],
|
||||
versions=[
|
||||
{
|
||||
"daily_version_id": 41,
|
||||
"business_date": "2026-07-29",
|
||||
"version_no": 2,
|
||||
"version_status": "active",
|
||||
"result_delivery_mode": "artifact_callback",
|
||||
"source_rows": 20,
|
||||
"retained_rows": 17,
|
||||
"excluded_rate_code_rows": 1,
|
||||
"duplicate_rows": 2,
|
||||
"validation_failed_rows": 0,
|
||||
"price_unmatched_rows": 0,
|
||||
"created_at": self.started + timedelta(seconds=7),
|
||||
"validated_at": finished,
|
||||
"activated_at": finished,
|
||||
}
|
||||
],
|
||||
outbox_events=[
|
||||
{
|
||||
"event_key": "processing-run:1:accepted",
|
||||
"event_type": "arr.daily_version_committed",
|
||||
"publish_status": "pending",
|
||||
"publish_attempts": 0,
|
||||
"created_at": finished,
|
||||
"available_at": finished,
|
||||
"payload": {"must": "not escape"},
|
||||
}
|
||||
],
|
||||
refreshed_at=finished + timedelta(seconds=1),
|
||||
)
|
||||
|
||||
self.assertEqual(trace["trace_version"], "arr-job-trace-3")
|
||||
self.assertEqual(trace["job"]["status"], "succeeded")
|
||||
self.assertEqual(trace["job"]["current_stage"], "downstream")
|
||||
self.assertFalse(trace["job"]["active"])
|
||||
self.assertIsNone(trace["job"]["remote_run_id"])
|
||||
self.assertEqual(trace["job"]["version_no"], 2)
|
||||
self.assertEqual(trace["job"]["output_rows"], 17)
|
||||
codes = [item["code"] for item in trace["logs"]]
|
||||
for expected in (
|
||||
"UPLOAD_REGISTERED",
|
||||
"ATTEMPT_QUEUED",
|
||||
"PROCESSOR_STARTED",
|
||||
"ARTIFACT_RESULT_RECEIVED",
|
||||
"ARTIFACT_RESULT_VALIDATED",
|
||||
"ARTIFACT_RESULT_COMMITTED",
|
||||
"FINANCE_VERSION_VALIDATED",
|
||||
"FINANCE_VERSION_ACTIVATED",
|
||||
"DOWNSTREAM_EVENT_QUEUED",
|
||||
"JOB_SUCCEEDED",
|
||||
):
|
||||
self.assertIn(expected, codes)
|
||||
self.assertEqual(trace["evidence"]["artifact_deliveries"], 1)
|
||||
|
||||
rendered = json.dumps(trace, ensure_ascii=False)
|
||||
for private_value in (
|
||||
"must/not/escape",
|
||||
"must not escape",
|
||||
'"payload"',
|
||||
'"object_key"',
|
||||
"submission_grant",
|
||||
"Agent",
|
||||
"MCP",
|
||||
):
|
||||
self.assertNotIn(private_value, rendered)
|
||||
|
||||
def test_runtime_failure_is_located_at_processor_boundary(self) -> None:
|
||||
finished = self.started + timedelta(minutes=4)
|
||||
trace = build_job_trace(
|
||||
{
|
||||
"job_id": "arrjob-timeout-001",
|
||||
"run_status": "failed",
|
||||
"public_status": "failed",
|
||||
"delivery_mode": "artifact_callback",
|
||||
"failure_code": "PROCESSOR_TIMEOUT",
|
||||
"failure_message": "programmatic processing failed",
|
||||
"created_at": self.started,
|
||||
"updated_at": finished,
|
||||
"finished_at": finished,
|
||||
"filename": "source.xml",
|
||||
"byte_size": 99,
|
||||
"source_sha256": "b" * 64,
|
||||
},
|
||||
attempts=[
|
||||
{
|
||||
"attempt_no": 1,
|
||||
"attempt_status": "failed",
|
||||
"remote_run_id": None,
|
||||
"failure_code": "PROCESSOR_TIMEOUT",
|
||||
"failure_message": "programmatic processing failed",
|
||||
"created_at": self.started + timedelta(seconds=1),
|
||||
"started_at": self.started + timedelta(seconds=2),
|
||||
"finished_at": finished,
|
||||
}
|
||||
],
|
||||
refreshed_at=finished,
|
||||
)
|
||||
|
||||
self.assertEqual(trace["job"]["current_stage"], "processor")
|
||||
self.assertEqual(trace["job"]["failure"]["code"], "PROCESSOR_TIMEOUT")
|
||||
errors = [item for item in trace["logs"] if item["level"] == "error"]
|
||||
self.assertGreaterEqual(len(errors), 2)
|
||||
self.assertTrue(all(item["stage"] == "processor" for item in errors))
|
||||
|
||||
def test_active_run_ends_with_current_programmatic_state(self) -> None:
|
||||
trace = build_job_trace(
|
||||
{
|
||||
"job_id": "arrjob-running-001",
|
||||
"run_status": "running",
|
||||
"public_status": "running",
|
||||
"delivery_mode": "artifact_callback",
|
||||
"created_at": self.started,
|
||||
"updated_at": self.started + timedelta(seconds=4),
|
||||
"filename": "source.xml",
|
||||
"byte_size": 128,
|
||||
"source_sha256": "c" * 64,
|
||||
},
|
||||
attempts=[
|
||||
{
|
||||
"attempt_no": 1,
|
||||
"attempt_status": "running",
|
||||
"remote_run_id": None,
|
||||
"created_at": self.started + timedelta(seconds=1),
|
||||
"started_at": self.started + timedelta(seconds=2),
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
self.assertTrue(trace["job"]["active"])
|
||||
self.assertEqual(trace["job"]["current_stage"], "processor")
|
||||
self.assertEqual(trace["logs"][-1]["code"], "CURRENT_RUNNING")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main(verbosity=2)
|
||||
Reference in New Issue
Block a user