"""Single-key invalidation guarantees needed by the platform rotation fence.""" from datetime import datetime, timedelta, timezone import pytest from api.config import settings from api.services.repository_rename import ( RenamePreconditionFailed, RenameServiceUnavailable, _sign_preflight, _verify_preflight_token, ) def test_rotation_invalidates_predecessor_and_accepts_successor(monkeypatch): payload = {'expires_at': (datetime.now(timezone.utc) + timedelta(minutes=15)).isoformat()} monkeypatch.setattr(settings, 'repository_rename_preflight_secret', 'fixture-old-key') predecessor = _sign_preflight(payload) assert _verify_preflight_token(predecessor) == payload monkeypatch.setattr(settings, 'repository_rename_preflight_secret', 'fixture-new-key') with pytest.raises(RenamePreconditionFailed): _verify_preflight_token(predecessor) assert _verify_preflight_token(_sign_preflight(payload)) == payload def test_tamper_expiry_and_no_key_fail_closed(monkeypatch): monkeypatch.setattr(settings, 'repository_rename_preflight_secret', 'fixture-key') future = {'expires_at': (datetime.now(timezone.utc) + timedelta(minutes=15)).isoformat()} token = _sign_preflight(future) encoded, signature = token.split('.') with pytest.raises(RenamePreconditionFailed): _verify_preflight_token(encoded + '.' + ('A' if signature[0] != 'A' else 'B') + signature[1:]) past = {'expires_at': (datetime.now(timezone.utc) - timedelta(seconds=1)).isoformat()} with pytest.raises(RenamePreconditionFailed, match='expired'): _verify_preflight_token(_sign_preflight(past)) monkeypatch.setattr(settings, 'repository_rename_preflight_secret', None) with pytest.raises(RenameServiceUnavailable): _sign_preflight(future) with pytest.raises(RenameServiceUnavailable): _verify_preflight_token(token)