from dataclasses import replace import io import json import os from pathlib import Path import sqlite3 import sys import tempfile import unittest from urllib.parse import urlencode sys.path.insert(0, str(Path(__file__).resolve().parents[1] / 'scripts')) from alert_ack import Actor, Application, Store, occurrence NOW = 1790553600 # 2026-09-28 UTC START = '2026-09-28T00:00:00Z' class AcknowledgmentTests(unittest.TestCase): def setUp(self): self.temp = tempfile.TemporaryDirectory() self.addCleanup(self.temp.cleanup) self.path = Path(self.temp.name) / 'ack.db' self.store = Store(self.path) self.actor = Actor('https://issuer.example', 'test-human', 'tenant:platform', ('railiance-admin',), NOW + 900, 'c' * 32) self.decision = {'id': 'test-decision', 'effect': 'allow', 'expires_at': NOW + 30} self.app = Application(self.store, 'https://telemetry.example', 'w' * 32, lambda env: self.actor, lambda *args: self.decision, clock=lambda: NOW) self.alert = {'status': 'firing', 'fingerprint': '0123456789abcdef', 'startsAt': START, 'labels': {'alertname': 'TestFailure', 'owner': 'railiance-telemetry'}} self.payload = {'version': '4', 'receiver': 'railiance-admin-email', 'alerts': [self.alert]} self.identity = occurrence(self.alert['fingerprint'], START) self.query = urlencode({'fingerprint': self.alert['fingerprint'], 'starts_at': START}) def request(self, method='GET', path='/ack/alerts', body=b'', **extra): env = {'REQUEST_METHOD': method, 'PATH_INFO': path, 'QUERY_STRING': self.query, 'CONTENT_LENGTH': str(len(body)), 'wsgi.input': io.BytesIO(body), 'HTTP_ORIGIN': 'https://telemetry.example'} env.update(extra) result = {} raw = b''.join(self.app(env, lambda status, headers: result.update(status=status, headers=headers))) return result['status'], raw.decode() def receive(self): self.store.receive(self.payload, NOW) def post(self, **kw): return self.request('POST', body=urlencode({'csrf': self.actor.csrf}).encode(), **kw) def test_scanner_get_does_not_acknowledge(self): self.receive() status, page = self.request() self.assertEqual(status, '200 OK') self.assertIn('Acknowledge receipt', page) self.assertIsNone(self.store.get(self.identity)['occurred_at']) with self.store.connect() as db: self.assertEqual(db.execute('SELECT COUNT(*) FROM outbox').fetchone()[0], 0) def test_click_commits_ack_and_audit_once_across_restart(self): self.receive() self.assertIn('Audit delivery pending', self.post()[1]) self.store = Store(self.path) self.app.store = self.store self.post() with self.store.connect() as db: rows = db.execute('SELECT body FROM outbox').fetchall() self.assertEqual(len(rows), 1) event = json.loads(rows[0]['body']) self.assertEqual(event['data']['actor_subject'], 'test-human') self.assertEqual(event['data']['role'], 'railiance-admin') self.assertEqual(event['subject'], 'alert:' + self.identity) self.assertEqual(event['type'], 'telemetry.alert.acknowledged') def test_failed_outbox_rolls_back_ack(self): self.receive() with self.store.connect() as db: db.execute("CREATE TRIGGER simulate_full BEFORE INSERT ON outbox BEGIN SELECT RAISE(ABORT, 'full'); END") self.assertEqual(self.post()[0], '503 Service Unavailable') self.assertIsNone(self.store.get(self.identity)['occurred_at']) def test_wrong_actor_role_tenant_service_expiry_denied(self): self.receive() original = self.actor for change in ({'roles': ()}, {'tenant': 'tenant:other'}, {'principal_type': 'service'}, {'expires_at': NOW}): self.actor = replace(original, **change) self.assertIn(self.post()[0], ('401 Unauthorized', '403 Forbidden')) self.assertIsNone(self.store.get(self.identity)['occurred_at']) def test_email_or_headers_do_not_grant_role(self): self.receive() self.actor = replace(self.actor, roles=()) self.assertEqual(self.post(HTTP_X_EMAIL='bernd.worsch@gmail.com', HTTP_X_ROLE='railiance-admin')[0], '403 Forbidden') def test_origin_csrf_and_expired_pdp_denied(self): self.receive() self.assertEqual(self.post(HTTP_ORIGIN='https://attacker.example')[0], '403 Forbidden') self.assertEqual(self.request('POST', body=b'csrf=wrong')[0], '403 Forbidden') self.decision['expires_at'] = NOW self.assertEqual(self.post()[0], '403 Forbidden') self.assertIsNone(self.store.get(self.identity)['occurred_at']) def test_webhook_auth_and_retry_and_batch_rollback(self): raw = json.dumps(self.payload).encode() self.assertEqual(self.request('POST', '/webhook', raw)[0], '401 Unauthorized') for _ in range(2): self.assertEqual(self.request('POST', '/webhook', raw, HTTP_AUTHORIZATION='Bearer ' + 'w' * 32)[0], '200 OK') changed = json.loads(raw) changed['alerts'][0]['labels']['alertname'] = 'Different' with self.assertRaises(ValueError): self.store.receive(changed, NOW) self.assertEqual(self.store.get(self.identity)['alertname'], 'TestFailure') def test_new_firing_occurrence_requires_new_ack(self): self.receive() self.post() self.alert['startsAt'] = '2026-09-27T23:59:59Z' other = self.store.receive(self.payload, NOW)[0] self.assertNotEqual(other, self.identity) self.assertIsNone(self.store.get(other)['occurred_at']) def test_lost_audit_receipt_replays_original_event(self): self.receive() self.post() sent = [] def lost(event): sent.append(event) raise TimeoutError() self.store.drain(lost) def duplicate(event): self.assertEqual(event, sent[0]) return 200, {'status': 'duplicate', 'reference': 'audit:' + event['id']} self.store.drain(duplicate) self.assertEqual(self.store.get(self.identity)['audit_status'], 'delivered') self.assertIn('Audit record archived', self.request()[1]) def test_audit_refusal_retained_and_wrong_receipt_not_accepted(self): self.receive() self.post() self.store.drain(lambda event: (202, {'status': 'accepted', 'reference': 'wrong'})) self.assertEqual(self.store.get(self.identity)['audit_status'], 'pending') self.store.drain(lambda event: (403, {})) self.assertEqual(self.store.get(self.identity)['audit_status'], 'blocked') self.assertIn('Audit delivery pending', self.request()[1]) self.assertEqual(self.store.audit_debt(), {'blocked': 1}) with self.store.connect() as db: event = json.loads(db.execute('SELECT body FROM outbox').fetchone()[0]) self.assertTrue(self.store.requeue(event['id'])) self.store.drain(lambda value: (202, {'status': 'accepted', 'reference': 'audit:' + value['id']})) self.assertEqual(self.store.audit_debt(), {}) def test_unsafe_database_refused(self): os.chmod(self.path, 0o644) with self.assertRaises(ValueError): Store(self.path) if __name__ == '__main__': unittest.main()