Implement authenticated alert receipt acknowledgments and audit delivery
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a0e6f1-443f-7783-9920-a16b2ffc467f
This commit is contained in:
parent
67283b66c2
commit
e7282e493d
25 changed files with 1881 additions and 0 deletions
48
tests/test_alert_audit_native.py
Normal file
48
tests/test_alert_audit_native.py
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
"""Opt in with RTEL_AUDIT_CORE_SOURCE; uses the real receiver, synthetic custody."""
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / 'scripts'))
|
||||
from alert_ack import Actor, Store
|
||||
|
||||
|
||||
@unittest.skipUnless(os.environ.get('RTEL_AUDIT_CORE_SOURCE'), 'set RTEL_AUDIT_CORE_SOURCE for real receiver check')
|
||||
class NativeAuditTests(unittest.TestCase):
|
||||
def test_real_receiver_accepts_and_deduplicates_after_lost_reply(self):
|
||||
sys.path.insert(0, os.environ['RTEL_AUDIT_CORE_SOURCE'])
|
||||
from audit_core.ingestion import IngestionApplication
|
||||
from audit_core.senders import SenderIdentity, SenderRegistry
|
||||
from audit_core.sqlite_backend import SQLiteAuditBackend
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
store = Store(Path(tmp) / 'ack.db')
|
||||
identity = store.receive({'version': '4', 'receiver': 'railiance-admin-email', 'alerts': [
|
||||
{'status': 'firing', 'fingerprint': '0123456789abcdef', 'startsAt': '2026-09-28T00:00:00Z',
|
||||
'labels': {'alertname': 'ControlledFailure', 'owner': 'railiance-telemetry'}}]}, 1790553600)[0]
|
||||
store.acknowledge(identity, Actor('https://issuer.example', 'fixture-human', 'tenant:platform',
|
||||
('railiance-admin',), 1790554500, 'c' * 32), 'fixture-decision', 1790553600)
|
||||
sender = SenderIdentity(name='railiance-telemetry', tokens=('fixture-only',),
|
||||
sources=frozenset({'railiance-telemetry'}), tenants=frozenset({'tenant:platform'}),
|
||||
evidence_kind='load-bearing', may_read=False)
|
||||
app = IngestionApplication(SQLiteAuditBackend(str(Path(tmp) / 'audit.db')), SenderRegistry([sender]))
|
||||
calls = []
|
||||
def send(event):
|
||||
raw = json.dumps(event).encode()
|
||||
env = {'REQUEST_METHOD': 'POST', 'PATH_INFO': '/v1/events',
|
||||
'CONTENT_LENGTH': str(len(raw)), 'wsgi.input': io.BytesIO(raw),
|
||||
'HTTP_AUTHORIZATION': 'Bearer fixture-only', 'HTTP_IDEMPOTENCY_KEY': event['id']}
|
||||
response = {}
|
||||
body = b''.join(app(env, lambda status, headers: response.update(status=int(status[:3]))))
|
||||
calls.append(response['status'])
|
||||
if len(calls) == 1:
|
||||
raise TimeoutError('simulated lost response')
|
||||
return response['status'], json.loads(body)
|
||||
store.drain(send)
|
||||
store = Store(Path(tmp) / 'ack.db')
|
||||
store.drain(send)
|
||||
self.assertEqual(calls, [202, 200])
|
||||
self.assertEqual(store.get(identity)['audit_status'], 'delivered')
|
||||
Loading…
Add table
Add a link
Reference in a new issue