Files
jarca-ledger-mcp/tests/test_settlement_draft_correction_contract.py
T

68 lines
4.3 KiB
Python

"""Acceptance contract for immutable, reason-linked settlement corrections."""
import sqlite3
from jarca_ledger_mcp import service as s
from jarca_ledger_mcp.db import init_db
def ok(r):
assert r['success'], r
return r
def counts(path):
with sqlite3.connect(path) as c:
return {table: c.execute(f'SELECT * FROM {table} ORDER BY rowid').fetchall()
for table in ('asset_snapshot_runs', 'asset_snapshots', 'settlement_drafts', 'settlement_draft_items')}
def finish(path, draft):
review = ok(s.review_settlement_draft(draft['id'], db_path=path))
approved = ok(s.approve_settlement_draft(draft['id'], draft['version'], review['review_fingerprint'], 'synthetic reviewer', 'approved synthetic correction', 'acceptance', db_path=path))
return ok(s.finalize_settlement_draft(draft['id'], approved['draft']['version'], review['review_fingerprint'], 'close-' + draft['id'], db_path=path))
def test_correction_links_reason_preserves_prior_rows_and_replays_after_later_correction(tmp_path):
path = str(tmp_path / 'correction.sqlite3')
init_db(path)
aid = ok(s.create_ledger_account('Synthetic cash', db_path=path))['account']['id']
first_value = ok(s.register_account_valuation(aid, '2026-09-30', 100, verification_status='verified', idempotency_key='v1', db_path=path))['valuation']['id']
original_draft = ok(s.create_settlement_draft('2026-09', 'synthetic', 'draft-1', db_path=path))['draft']
original = finish(path, original_draft)
original_rows = counts(path)
corrected_value = ok(s.correct_account_valuation(first_value, 125, 'synthetic valuation correction', idempotency_key='v2', db_path=path))['valuation']['id']
assert counts(path)['asset_snapshot_runs'] == original_rows['asset_snapshot_runs']
assert counts(path)['asset_snapshots'] == original_rows['asset_snapshots']
run_id = original['snapshot_run_id']
bad_requests = [('', 'reason'), (run_id, ''), ('does-not-exist', 'reason')]
before = counts(path)
for i, (parent, reason) in enumerate(bad_requests):
result = s.create_settlement_draft('2026-09', 'synthetic', f'invalid-{i}', correction_of_run_id=parent, correction_reason=reason, db_path=path)
assert not result['success']
assert counts(path) == before
ordinary = s.create_settlement_draft('2026-09', 'synthetic', 'ordinary-after-close', db_path=path)
assert not ordinary['success'] and ordinary['error_code'] == 'validation_error'
assert counts(path) == before
correction = ok(s.create_settlement_draft('2026-09', 'synthetic', 'correction-1', correction_of_run_id=run_id, correction_reason='synthetic close correction', db_path=path))['draft']
active_conflict = s.create_settlement_draft('2026-09', 'synthetic', 'active-conflict', correction_of_run_id=run_id, correction_reason='another linked correction', db_path=path)
assert not active_conflict['success'] and active_conflict['error_code'] == 'conflict'
assert counts(path)['settlement_drafts'] == before['settlement_drafts'] + [counts(path)['settlement_drafts'][-1]]
assert correction['correction_of_run_id'] == run_id
selected = ok(s.set_settlement_draft_item(correction['id'], 'account', aid, correction['version'], 'synthetic selector', valuation_id=corrected_value, db_path=path))['draft']
corrected = finish(path, selected)
assert corrected['correction']['correction_of_run_id'] == run_id
assert corrected['correction']['correction_reason'] == 'synthetic close correction'
for table, rows in original_rows.items():
assert counts(path)[table][:len(rows)] == rows
third_value = ok(s.correct_account_valuation(corrected_value, 150, 'second synthetic correction', idempotency_key='v3', db_path=path))['valuation']['id']
later = ok(s.create_settlement_draft('2026-09', 'synthetic', 'correction-2', correction_of_run_id=corrected['snapshot_run_id'], correction_reason='second synthetic close correction', db_path=path))['draft']
later = ok(s.set_settlement_draft_item(later['id'], 'account', aid, later['version'], 'synthetic selector', valuation_id=third_value, db_path=path))['draft']
finish(path, later)
before_replay = counts(path)
replay = s.finalize_settlement_draft(correction['id'], selected['version'], corrected['review_fingerprint'], 'close-' + correction['id'], db_path=path)
assert replay == corrected
assert counts(path) == before_replay