from __future__ import annotations import sqlite3 from jarca_ledger_mcp.application.account_valuation_service import ( list_account_valuations, register_account_valuation, ) from jarca_ledger_mcp.application.assets_service import ( create_manual_asset, list_manual_assets, preview_asset_snapshot_close, ) from jarca_ledger_mcp.application.settlement_draft_service import create_settlement_draft from jarca_ledger_mcp.infrastructure.sqlite.migrations import init_db def _tables(path): with sqlite3.connect(path) as conn: return {r[0] for r in conn.execute("select name from sqlite_master where type='table'")} EXPECTED_TABLES = { # Sourced from schema.py plus migration-created settlement draft tables. "schema_meta", "accounts", "transactions", "budgets", "import_batches", "category_rules", "ledger_categories", "account_snapshot", "settlement_cashflow_snapshots", "review_decisions", "reconciliation_sessions", "monthly_settlement", "settlement_drafts", "settlement_draft_items", } def test_fresh_init_has_no_account_valuations_and_exact_schema_tables(tmp_path): path = tmp_path / "fresh.sqlite3" init_db(str(path)) tables = _tables(path) assert "account_valuations" not in tables assert tables == EXPECTED_TABLES def test_init_is_idempotent(tmp_path): path = tmp_path / "repeat.sqlite3" assert init_db(str(path)) == str(path) assert init_db(str(path)) == str(path) assert _tables(path) == EXPECTED_TABLES def test_legacy_nonempty_account_valuations_row_is_dropped_only(tmp_path): path = tmp_path / "legacy.sqlite3" init_db(str(path)) with sqlite3.connect(path) as conn: conn.execute("create table account_valuations (id text primary key, account_id text, amount integer)") conn.execute("insert into account_valuations values ('legacy-1','acct-1',123)") conn.execute("insert into accounts (id,name,type,active,created_at,updated_at) values ('acct-1','Keep me','bank',1,'t','t')") conn.commit() init_db(str(path)) with sqlite3.connect(path) as conn: assert conn.execute("select count(*) from accounts where id='acct-1'").fetchone()[0] == 1 assert conn.execute("select name from sqlite_master where type='table' and name='account_valuations'").fetchone() is None def test_unsupported_valuation_operations_do_not_create_or_access_db(tmp_path): path = tmp_path / "must-not-exist.sqlite3" result = register_account_valuation("acct", "2026-09-30", 1, db_path=str(path)) listed = list_account_valuations("acct", db_path=str(path)) assert result["success"] is False and result["error_code"] == "validation_error" assert listed["success"] is False and listed["error_code"] == "db_error" assert not path.exists() def test_active_account_preview_reports_missing_without_sql_error(tmp_path): path = tmp_path / "preview.sqlite3" init_db(str(path)) with sqlite3.connect(path) as conn: conn.execute("insert into accounts (id,name,type,active,created_at,updated_at) values ('acct-1','Active','bank',1,'t','t')") conn.commit() result = preview_asset_snapshot_close("2026-09-30", db_path=str(path)) assert result["success"] is True assert result["close_status"] in {"incomplete_draft", "complete_exact"} assert not any(w.get("code") == "db_error" for w in result["warnings"]) def test_active_account_draft_returns_review_blocker_without_sql_error(tmp_path): path = tmp_path / "draft.sqlite3" init_db(str(path)) with sqlite3.connect(path) as conn: conn.execute("insert into accounts (id,name,type,active,created_at,updated_at) values ('acct-1','Active','bank',1,'t','t')") conn.commit() result = create_settlement_draft("2026-09", "tester", "draft-key", db_path=str(path)) assert result["success"] is True assert result.get("review", {}).get("approval_required") is True assert not any("sql" in str(v).lower() for v in result.get("review", {}).values()) def test_manual_asset_flow_is_unsupported_after_removal(tmp_path): path = tmp_path / "manual.sqlite3" init_db(str(path)) created = create_manual_asset("Emergency fund", 100000, "2026-09-30", "cash", db_path=str(path)) listed = list_manual_assets(db_path=str(path)) assert created == {"success": False, "error_code": "unsupported", "error": "manual assets are no longer supported"} assert listed == {"success": False, "error_code": "unsupported", "error": "manual assets are no longer supported"}