import datetime import hashlib import json import sys from pathlib import Path import pytest sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) from core.outbox_writer import _canonical_hash, write_event class TestCanonicalHash: def test_deterministic(self): event = {"b": 2, "a": 1} h1 = _canonical_hash(event) h2 = _canonical_hash(event) assert h1 == h2 def test_order_independent(self): h1 = _canonical_hash({"a": 1, "b": 2}) h2 = _canonical_hash({"b": 2, "a": 1}) assert h1 == h2 def test_is_sha256_hex(self): h = _canonical_hash({"key": "val"}) assert len(h) == 64 assert all(c in "0123456789abcdef" for c in h) def test_different_events_different_hash(self): h1 = _canonical_hash({"a": 1}) h2 = _canonical_hash({"a": 2}) assert h1 != h2 class TestWriteEvent: def _sample_event(self): return { "contractId": "test-contract-001", "eventType": "CONFIDENCE_COMPUTED", "ts": "2026-07-22T00:00:00Z", "environment": "dev", "stack": "s3", "score": 0.85, "band": "pass", "prev_event_hash": "GENESIS", } def test_write_event_with_mock_dynamodb(self): from moto import mock_aws import boto3 with mock_aws(): dyn = boto3.client("dynamodb", region_name="us-east-1") dyn.create_table( TableName="nova-outbox", KeySchema=[ {"AttributeName": "contractId", "KeyType": "HASH"}, {"AttributeName": "eventType#eventTs", "KeyType": "RANGE"}, ], AttributeDefinitions=[ {"AttributeName": "contractId", "AttributeType": "S"}, {"AttributeName": "eventType#eventTs", "AttributeType": "S"}, ], BillingMode="PAY_PER_REQUEST", ) event = self._sample_event() item = write_event(event, outbox_table="nova-outbox", region="us-east-1") assert item["contractId"]["S"] == "test-contract-001" assert item["prev_event_hash"]["S"] == "GENESIS" assert "hash" in item assert len(item["hash"]["S"]) == 64 assert "expire_at" in item def test_write_event_hash_matches_canonical(self): from moto import mock_aws import boto3 with mock_aws(): dyn = boto3.client("dynamodb", region_name="us-east-1") dyn.create_table( TableName="nova-outbox", KeySchema=[ {"AttributeName": "contractId", "KeyType": "HASH"}, {"AttributeName": "eventType#eventTs", "KeyType": "RANGE"}, ], AttributeDefinitions=[ {"AttributeName": "contractId", "AttributeType": "S"}, {"AttributeName": "eventType#eventTs", "AttributeType": "S"}, ], BillingMode="PAY_PER_REQUEST", ) event = self._sample_event() item = write_event(event, outbox_table="nova-outbox", region="us-east-1") expected_hash = _canonical_hash(event) assert item["hash"]["S"] == expected_hash def test_write_event_persists_to_dynamodb(self): from moto import mock_aws import boto3 with mock_aws(): dyn = boto3.client("dynamodb", region_name="us-east-1") dyn.create_table( TableName="nova-outbox", KeySchema=[ {"AttributeName": "contractId", "KeyType": "HASH"}, {"AttributeName": "eventType#eventTs", "KeyType": "RANGE"}, ], AttributeDefinitions=[ {"AttributeName": "contractId", "AttributeType": "S"}, {"AttributeName": "eventType#eventTs", "AttributeType": "S"}, ], BillingMode="PAY_PER_REQUEST", ) event = self._sample_event() write_event(event, outbox_table="nova-outbox", region="us-east-1") resp = dyn.get_item( TableName="nova-outbox", Key={ "contractId": {"S": "test-contract-001"}, "eventType#eventTs": {"S": "CONFIDENCE_COMPUTED#2026-07-22T00:00:00Z"}, }, ) assert "Item" in resp assert resp["Item"]["band"]["S"] == "pass"