state-hub/tests/test_preflight_signing_rotation.py
tegwick e96ef197cf feat: add API-only preflight signing delivery and rotation acceptance
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a06ed7-828d-7ca0-a8d4-0c3e5a0c4102
2026-09-05 16:39:02 +02:00

38 lines
1.8 KiB
Python

"""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)