fix: preserve schema 15 compatibility for v0.1.8

This commit is contained in:
2026-10-17 18:27:53 +09:00
parent a97548c36b
commit 1134f058cc
61 changed files with 2650 additions and 644 deletions
+25 -11
View File
@@ -150,7 +150,7 @@ def test_mid_close_failure_rolls_back_all_settlement_state(tmp_path, failure_poi
review = review_result
ok(api("approve_settlement_draft")(draft["id"], draft["version"], review["review_fingerprint"], "synthetic-reviewer", "approved", "ref", db_path=path))
with sqlite3.connect(path) as db:
target = {"header":"asset_snapshot_runs", "assets":"account_snapshot", "cashflow":"settlement_cashflow_snapshots", "completion":"settlement_drafts"}[failure_point]
target = {"header":"asset_snapshot_runs", "assets":"asset_snapshots", "cashflow":"settlement_cashflow_snapshots", "completion":"settlement_drafts"}[failure_point]
# Missing future table is an explicit RED assertion, not a SQL error masquerading as rollback.
assert db.execute("SELECT 1 FROM sqlite_master WHERE type='table' AND name=?", (target,)).fetchone(), f"required comprehensive table missing: {target}"
event = "UPDATE" if failure_point == "completion" else "INSERT"
@@ -160,7 +160,7 @@ def test_mid_close_failure_rolls_back_all_settlement_state(tmp_path, failure_poi
assert failed.get("success") is False
with sqlite3.connect(path) as db:
assert db.execute("SELECT COUNT(*) FROM asset_snapshot_runs").fetchone()[0] == 0
assert db.execute("SELECT COUNT(*) FROM account_snapshot").fetchone()[0] == 0
assert db.execute("SELECT COUNT(*) FROM asset_snapshots").fetchone()[0] == 0
assert db.execute("SELECT COUNT(*) FROM settlement_cashflow_snapshots").fetchone()[0] == 0
assert db.execute("SELECT status FROM settlement_drafts WHERE id=?", (draft["id"],)).fetchone()[0] == "active"
@@ -176,7 +176,7 @@ def test_close_rejects_stored_detail_sum_tampering(tmp_path):
result = close_draft(path, draft)
assert result.get("success") is False, result
with sqlite3.connect(path) as db:
for table in ("asset_snapshot_runs", "account_snapshot", "settlement_cashflow_snapshots"):
for table in ("asset_snapshot_runs", "asset_snapshots", "settlement_cashflow_snapshots"):
assert db.execute(f"SELECT COUNT(*) FROM {table}").fetchone()[0] == 0
assert db.execute("SELECT status FROM settlement_drafts WHERE id=?", (draft["id"],)).fetchone()[0] == "active"
@@ -324,6 +324,25 @@ def test_legacy_reader_preserves_unrecorded_currency_and_marks_legacy(tmp_path):
assert all(value is None for value in result["totals"].values())
def test_month_lookup_ignores_later_direct_asset_correction(tmp_path):
path = str(tmp_path / "reader-interleaved-runs.sqlite3")
_seed_reader_run(path, True, {"transactions": [], "assets": []})
with sqlite3.connect(path) as conn:
conn.execute(
"""INSERT INTO asset_snapshot_runs
(id,basis_date,close_status,correction_of_run_id,correction_reason,created_at)
VALUES ('direct-correction','2026-09-30','conditional','synthetic-run','asset correction','2026-10-01')"""
)
monthly = ok(service.get_monthly_settlement_snapshot(month="2026-09", db_path=path))
exact = ok(service.get_monthly_settlement_snapshot(run_id="direct-correction", db_path=path))
assert monthly["run_id"] == "synthetic-run"
assert monthly["is_comprehensive"] is True
assert exact["run_id"] == "direct-correction"
assert exact["legacy"] is True
def test_legacy_reader_accepts_original_header_without_migration(tmp_path):
from jarca_ledger_mcp.application.monthly_settlement_service import get_monthly_settlement_snapshot
path = str(tmp_path / "reader-old-columns.sqlite3")
@@ -368,7 +387,7 @@ def test_close_rejects_persisted_header_and_asset_tampering(tmp_path, target):
ok(service.approve_settlement_draft(draft["id"], draft["version"], review["review_fingerprint"], "reviewer", "synthetic", "ref", db_path=path))
if target.startswith("asset_"):
column, expression = ("amount", "amount+1") if target == "asset_amount" else ("currency", "'USD'")
trigger = f"CREATE TRIGGER synthetic_tamper AFTER INSERT ON account_snapshot BEGIN UPDATE account_snapshot SET {column}={expression} WHERE id=NEW.id; END"
trigger = f"CREATE TRIGGER synthetic_tamper AFTER INSERT ON asset_snapshots BEGIN UPDATE asset_snapshots SET {column}={expression} WHERE id=NEW.id; END"
else:
column, expression = {"header_total": ("income_total", "income_total+1"), "header_source": ("source_snapshot_json", "'{}'"), "header_policy": ("cashflow_policy_json", "'{\"savings_categories\":[{\"category\":\"Other\",\"subcategory\":\"Pair\"}],\"category_decisions\":[]}'"), "header_version": ("aggregation_version", "'unknown-version'")}[target]
trigger = f"CREATE TRIGGER synthetic_tamper AFTER INSERT ON settlement_cashflow_snapshots BEGIN UPDATE asset_snapshot_runs SET {column}={expression} WHERE id=NEW.settlement_run_id; END"
@@ -377,7 +396,7 @@ def test_close_rejects_persisted_header_and_asset_tampering(tmp_path, target):
result = service.finalize_settlement_draft(draft["id"], draft["version"], review["review_fingerprint"], "tamper-close", db_path=path)
assert result["success"] is False, "persisted header/detail must match the approved result before commit"
with sqlite3.connect(path) as conn:
for table in ("asset_snapshot_runs", "account_snapshot", "settlement_cashflow_snapshots"):
for table in ("asset_snapshot_runs", "asset_snapshots", "settlement_cashflow_snapshots"):
assert conn.execute(f"SELECT COUNT(*) FROM {table}").fetchone()[0] == 0
assert conn.execute("SELECT status,final_run_id,final_response FROM settlement_drafts WHERE id=?", (draft["id"],)).fetchone() == ("active", "", "")
@@ -672,12 +691,7 @@ def test_schema_eleven_legacy_reader_returns_empty_evidence_without_writes(tmp_p
from test_legacy_snapshot_migration import _legacy_closed_db
path, _ = _legacy_closed_db(tmp_path)
with sqlite3.connect(path) as conn:
header = conn.execute(
"SELECT id, close_status, correction_of_run_id, correction_reason FROM asset_snapshot_runs"
).fetchone()
assert tuple(header) == (
"legacy-run-correction", "legacy_read_only", "legacy-initial", "legacy correction"
)
assert conn.execute("SELECT COUNT(*) FROM asset_snapshot_runs").fetchone()[0] == 0
before = list(conn.iterdump())
exact = ok(service.get_monthly_settlement_snapshot(run_id="legacy-run-correction", db_path=path))
latest = ok(service.get_monthly_settlement_snapshot(month="2026-06", db_path=path))