fix: preserve schema 15 compatibility for v0.1.8
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import sqlite3
|
||||
from pathlib import Path
|
||||
|
||||
@@ -35,8 +34,8 @@ def test_actual_v14_fixture_is_tag_ddl_and_migrates_preserving_rows(tmp_path):
|
||||
init_db(str(path))
|
||||
with sqlite3.connect(path) as db:
|
||||
assert db.execute("SELECT value FROM schema_meta WHERE key='schema_version'").fetchone()[0] == "15"
|
||||
assert db.execute("SELECT amount, closed_at FROM account_snapshot WHERE id='s1'").fetchone() == (100000, "2026-01-31T10:00:00")
|
||||
assert db.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='asset_snapshot_runs'").fetchone() is None
|
||||
assert db.execute("SELECT amount, closed_at FROM asset_snapshots WHERE id='s1'").fetchone() == (100000, "2026-01-31T10:00:00")
|
||||
assert db.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='asset_snapshot_runs'").fetchone() is not None
|
||||
assert db.execute("PRAGMA foreign_key_check").fetchall() == []
|
||||
assert db.execute("PRAGMA integrity_check").fetchone()[0] == "ok"
|
||||
assert _rows(path, "accounts") == before_accounts
|
||||
@@ -47,9 +46,9 @@ def test_actual_v14_migration_is_idempotent_and_snapshot_unchanged(tmp_path):
|
||||
path = tmp_path / "ledger.sqlite3"
|
||||
_v14_db(path)
|
||||
init_db(str(path))
|
||||
first = _rows(path, "account_snapshot")
|
||||
first = _rows(path, "asset_snapshots")
|
||||
init_db(str(path))
|
||||
assert _rows(path, "account_snapshot") == first
|
||||
assert _rows(path, "asset_snapshots") == first
|
||||
|
||||
|
||||
def test_migration_failure_rolls_back_legacy_renames_and_metadata(tmp_path, monkeypatch):
|
||||
@@ -75,3 +74,48 @@ def test_new_init_has_same_contract(tmp_path):
|
||||
assert db.execute("SELECT value FROM schema_meta WHERE key='schema_version'").fetchone()[0] == "15"
|
||||
cols = {r[1] for r in db.execute("PRAGMA table_info(account_snapshot)")}
|
||||
assert {"monthly_settlement_id", "verification_status", "evidence_kind"} <= cols
|
||||
|
||||
|
||||
def test_populated_v14_upgrade_preserves_evidence_corrections_and_cashflow(tmp_path):
|
||||
path = tmp_path / "populated.sqlite3"
|
||||
_v14_db(path)
|
||||
with sqlite3.connect(path) as db:
|
||||
db.execute("INSERT INTO manual_assets(id,name,valuation_amount,created_at,updated_at) VALUES('manual','House',500,'t','t')")
|
||||
db.execute("INSERT INTO account_valuations(id,account_id,basis_date,amount,recorded_at) VALUES('v1','a1','2026-01-31',100,'t')")
|
||||
db.execute("INSERT INTO account_valuations(id,account_id,basis_date,amount,recorded_at,supersedes_id,correction_reason) VALUES('v2','a1','2026-01-31',110,'t','v1','correct')")
|
||||
db.execute("UPDATE asset_snapshots SET snapshot_run_id='run1' WHERE id='s1'")
|
||||
# A direct asset close can have no settlement_month. A correction shares
|
||||
# its basis date, but its frozen cashflow must never merge with run1.
|
||||
db.execute("INSERT INTO asset_snapshot_runs(id,basis_date,close_status,created_at,correction_of_run_id,correction_reason) VALUES('run2','2026-01-31','conditional','t','run1','correct')")
|
||||
for run, amount in [("run1", 10), ("run2", 20)]:
|
||||
db.execute("INSERT INTO settlement_cashflow_snapshots VALUES(?,?,'income','','',?,1,'KRW')", ("cf-" + run, run, amount))
|
||||
db.execute("CREATE INDEX custom_valuation_amount ON account_valuations(amount)")
|
||||
tables = ("accounts", "transactions", "manual_assets", "account_valuations", "asset_snapshots", "asset_snapshot_runs")
|
||||
with sqlite3.connect(path) as db:
|
||||
columns = {table: ",".join(row[1] for row in db.execute(f"PRAGMA table_info({table})")) for table in tables}
|
||||
before = {table: db.execute(f"SELECT {columns[table]} FROM {table} ORDER BY id").fetchall() for table in tables}
|
||||
init_db(str(path))
|
||||
init_db(str(path))
|
||||
with sqlite3.connect(path) as db:
|
||||
assert {table: db.execute(f"SELECT {columns[table]} FROM {table} ORDER BY id").fetchall() for table in tables} == before
|
||||
with sqlite3.connect(path) as db:
|
||||
assert db.execute("SELECT settlement_run_id,monthly_settlement_id,amount FROM settlement_cashflow_snapshots ORDER BY id").fetchall() == [("run1", None, 10), ("run2", None, 20)]
|
||||
assert db.execute("SELECT COUNT(*) FROM monthly_settlement").fetchone() == (0,)
|
||||
assert db.execute("SELECT COUNT(*) FROM account_snapshot").fetchone() == (0,)
|
||||
assert db.execute("SELECT name FROM sqlite_master WHERE name='custom_valuation_amount'").fetchone()
|
||||
assert db.execute("PRAGMA integrity_check").fetchone() == ("ok",)
|
||||
assert db.execute("PRAGMA foreign_key_check").fetchall() == []
|
||||
|
||||
|
||||
def test_orphan_cashflow_migration_rolls_back_without_deleting_legacy_data(tmp_path):
|
||||
path = tmp_path / "orphan.sqlite3"
|
||||
_v14_db(path)
|
||||
with sqlite3.connect(path) as db:
|
||||
db.execute("INSERT INTO settlement_cashflow_snapshots VALUES('orphan','missing','income','','',1,1,'KRW')")
|
||||
before = _rows(path, "settlement_cashflow_snapshots")
|
||||
with pytest.raises(sqlite3.IntegrityError):
|
||||
init_db(str(path))
|
||||
assert _rows(path, "settlement_cashflow_snapshots") == before
|
||||
with sqlite3.connect(path) as db:
|
||||
assert db.execute("SELECT value FROM schema_meta WHERE key='schema_version'").fetchone() == ("14",)
|
||||
assert not db.execute("SELECT name FROM sqlite_master WHERE name='account_snapshot'").fetchone()
|
||||
|
||||
Reference in New Issue
Block a user