from __future__ import annotations from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey import pytest from yovision_brain.integration.event_export import SQLiteReplayCache from yovision_brain.integration.machine_identity import ( KeyRecord, MachineIdentityError, Registry, Signer, Verifier, ) def test_replay_cache_rejects_same_jti_after_connector_restart(tmp_path) -> None: now = 1_800_000_000 private_key = Ed25519PrivateKey.generate() registry = Registry( [ KeyRecord( principal="yv:brain:node-a", key_id="brain-key-0001", public_key=private_key.public_key(), audience="yovision-sense", scopes=frozenset({"events:ingest"}), ) ] ) token = Signer( "yv:brain:node-a", "brain-key-0001", private_key, clock=lambda: now, ).mint("yovision-sense", ["events:ingest"], "POST", "/v1/events", b"{}") database = tmp_path / "machine-replay.sqlite3" first_process = Verifier(registry, SQLiteReplayCache(database), clock=lambda: now) claims = first_process.verify( token, "yovision-sense", "events:ingest", "POST", "/v1/events", b"{}" ) assert claims.iss == "yv:brain:node-a" restarted_process = Verifier(registry, SQLiteReplayCache(database), clock=lambda: now) with pytest.raises(MachineIdentityError, match="machine_token_replayed") as caught: restarted_process.verify( token, "yovision-sense", "events:ingest", "POST", "/v1/events", b"{}" ) assert caught.value.code == "machine_token_replayed" def test_replay_cache_atomically_reuses_expired_identity(tmp_path) -> None: cache = SQLiteReplayCache(tmp_path / "machine-replay.sqlite3") assert cache.consume("yv:brain:node-a", "jti-one", expires_at=110, now=100) assert not cache.consume("yv:brain:node-a", "jti-one", expires_at=110, now=101) assert cache.consume("yv:brain:node-a", "jti-one", expires_at=130, now=110)