"""Tests for CBOR serialization of chain records.""" from __future__ import annotations import hashlib from soosef.federation.models import AttestationChainRecord, ChainState, EntropyWitnesses from soosef.federation.serialization import ( canonical_bytes, compute_record_hash, deserialize_record, serialize_record, ) def _make_record(**overrides) -> AttestationChainRecord: """Create a minimal test record with sensible defaults.""" defaults = { "version": 1, "record_id": b"\x01" * 16, "chain_index": 0, "prev_hash": ChainState.GENESIS_PREV_HASH, "content_hash": hashlib.sha256(b"test content").digest(), "content_type": "test/plain", "metadata": {}, "claimed_ts": 1_700_000_000_000_000, "entropy_witnesses": EntropyWitnesses( sys_uptime=12345.678, fs_snapshot=b"\xab" * 16, proc_entropy=256, boot_id="test-boot-id", ), "signer_pubkey": b"\x02" * 32, "signature": b"\x03" * 64, } defaults.update(overrides) return AttestationChainRecord(**defaults) def test_canonical_bytes_deterministic(): """Same record always produces the same canonical bytes.""" record = _make_record() b1 = canonical_bytes(record) b2 = canonical_bytes(record) assert b1 == b2 def test_canonical_bytes_excludes_signature(): """Canonical bytes must not include the signature field.""" record_a = _make_record(signature=b"\x03" * 64) record_b = _make_record(signature=b"\x04" * 64) assert canonical_bytes(record_a) == canonical_bytes(record_b) def test_canonical_bytes_sensitive_to_content(): """Different content_hash must produce different canonical bytes.""" record_a = _make_record(content_hash=hashlib.sha256(b"a").digest()) record_b = _make_record(content_hash=hashlib.sha256(b"b").digest()) assert canonical_bytes(record_a) != canonical_bytes(record_b) def test_serialize_deserialize_round_trip(): """A record survives serialization and deserialization intact.""" original = _make_record() data = serialize_record(original) restored = deserialize_record(data) assert restored.version == original.version assert restored.record_id == original.record_id assert restored.chain_index == original.chain_index assert restored.prev_hash == original.prev_hash assert restored.content_hash == original.content_hash assert restored.content_type == original.content_type assert restored.metadata == original.metadata assert restored.claimed_ts == original.claimed_ts assert restored.signer_pubkey == original.signer_pubkey assert restored.signature == original.signature # Entropy witnesses assert restored.entropy_witnesses is not None assert restored.entropy_witnesses.sys_uptime == original.entropy_witnesses.sys_uptime assert restored.entropy_witnesses.fs_snapshot == original.entropy_witnesses.fs_snapshot assert restored.entropy_witnesses.proc_entropy == original.entropy_witnesses.proc_entropy assert restored.entropy_witnesses.boot_id == original.entropy_witnesses.boot_id def test_serialize_includes_signature(): """Full serialization must include the signature.""" record = _make_record(signature=b"\xaa" * 64) data = serialize_record(record) restored = deserialize_record(data) assert restored.signature == b"\xaa" * 64 def test_compute_record_hash(): """Record hash is SHA-256 of canonical bytes.""" record = _make_record() expected = hashlib.sha256(canonical_bytes(record)).digest() assert compute_record_hash(record) == expected def test_record_hash_changes_with_content(): """Different records produce different hashes.""" a = _make_record(content_hash=hashlib.sha256(b"a").digest()) b = _make_record(content_hash=hashlib.sha256(b"b").digest()) assert compute_record_hash(a) != compute_record_hash(b) def test_metadata_preserved(): """Arbitrary metadata survives round-trip.""" meta = {"backfilled": True, "caption": "test photo", "tags": ["evidence", "urgent"]} record = _make_record(metadata=meta) data = serialize_record(record) restored = deserialize_record(data) assert restored.metadata == meta def test_empty_entropy_witnesses(): """Record with no entropy witnesses round-trips correctly.""" record = _make_record(entropy_witnesses=None) data = serialize_record(record) restored = deserialize_record(data) assert restored.entropy_witnesses is None