Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 308701@bnt-lap001 Assistant-Session: 5548e447-444c-4dcc-bed1-05c16eebfe56
102 lines
3.8 KiB
Python
102 lines
3.8 KiB
Python
"""Synthetic tests for the NK-WP-0035-T04 receiver readback helper; no network, no tokens."""
|
|
import importlib.util
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
SOURCE = Path(__file__).parents[1] / 'receiver_readback.py'
|
|
spec = importlib.util.spec_from_file_location('receiver_readback', SOURCE)
|
|
rb = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(rb)
|
|
|
|
CAPTURE = Path(__file__).parents[3] / 'docs/evidence/2026-10-15-cadence-adoption/hourly-observation-capture'
|
|
|
|
|
|
def fake_receiver(counts, present, source='qonto-assistant', tenant='tenant:friendly:binky', recon_status=200,
|
|
basis='independent_reader'):
|
|
calls = []
|
|
|
|
def get(path):
|
|
calls.append(path)
|
|
if path.startswith('/v1/reconciliation?'):
|
|
if recon_status != 200:
|
|
return recon_status, {'error': 'source_not_allowed'}
|
|
return 200, {'counts': [{'class': k, 'count': v} for k, v in counts.items()], 'basis': basis}
|
|
event_id = path.rsplit('/', 1)[1]
|
|
if event_id in present:
|
|
return 200, {'source': source, 'tenant': tenant, 'id': event_id}
|
|
return 404, {'error': 'not_found'}
|
|
return get, calls
|
|
|
|
|
|
def test_retained_capture_loads():
|
|
request, captured = rb.load_capture(CAPTURE)
|
|
assert len(request['event_ids']) == 3
|
|
assert sum(captured.values()) == 3
|
|
|
|
|
|
def test_agreement():
|
|
request, captured = rb.load_capture(CAPTURE)
|
|
get, calls = fake_receiver(dict(captured), set(request['event_ids']))
|
|
receipt = rb.readback(request, captured, get)
|
|
assert receipt['verdict'] == 'agree' and receipt['findings'] == []
|
|
assert 'stream_instance_id=' in calls[0] and len(calls) == 4
|
|
assert receipt['token_retained'] is False and receipt['payloads_retained'] is False
|
|
|
|
|
|
@pytest.mark.parametrize('case', ['missing_event', 'count_differs', 'extra_class', 'forbidden', 'wrong_tenant'])
|
|
def test_divergence(case):
|
|
request, captured = rb.load_capture(CAPTURE)
|
|
counts, present = dict(captured), set(request['event_ids'])
|
|
kwargs = {}
|
|
if case == 'missing_event':
|
|
present.discard(request['event_ids'][0])
|
|
elif case == 'count_differs':
|
|
cls = next(iter(counts))
|
|
counts[cls] += 1
|
|
elif case == 'extra_class':
|
|
counts['audit.allow'] = 1
|
|
elif case == 'forbidden':
|
|
kwargs['recon_status'] = 403
|
|
else:
|
|
kwargs['tenant'] = 'tenant:other'
|
|
get, _ = fake_receiver(counts, present, **kwargs)
|
|
receipt = rb.readback(request, captured, get)
|
|
assert receipt['verdict'] == 'diverge' and receipt['findings']
|
|
|
|
|
|
def test_token_file_must_be_private(tmp_path):
|
|
token = tmp_path / 'token'
|
|
token.write_text('abc.def.ghi\n')
|
|
os.chmod(token, 0o644)
|
|
with pytest.raises(ValueError):
|
|
rb.read_token(token)
|
|
os.chmod(token, 0o600)
|
|
assert rb.read_token(token) == 'abc.def.ghi'
|
|
token.write_text('two tokens\n')
|
|
with pytest.raises(ValueError):
|
|
rb.read_token(token)
|
|
|
|
|
|
def test_receipt_never_contains_token(tmp_path, monkeypatch):
|
|
token = tmp_path / 'token'
|
|
token.write_text('SENTINEL-TOKEN-VALUE')
|
|
os.chmod(token, 0o600)
|
|
request, captured = rb.load_capture(CAPTURE)
|
|
get, _ = fake_receiver(dict(captured), set(request['event_ids']))
|
|
monkeypatch.setattr(rb, 'http_get', lambda base, tok: get)
|
|
out = tmp_path / 'receipt.json'
|
|
assert rb.main(['--capture-dir', str(CAPTURE), '--token-file', str(token), '--output', str(out)]) == 0
|
|
assert 'SENTINEL' not in out.read_text()
|
|
assert json.loads(out.read_text())['verdict'] == 'agree'
|
|
|
|
|
|
def test_missing_independent_basis_diverges():
|
|
request, captured = rb.load_capture(CAPTURE)
|
|
get, _ = fake_receiver(captured, set(request['event_ids']), basis=None)
|
|
receipt = rb.readback(request, captured, get)
|
|
assert receipt['verdict'] == 'diverge'
|
|
assert receipt['receiver_basis'] is None
|
|
assert any('basis' in f for f in receipt['findings'])
|