chore(jarca-ledger): finalize approved batch docs, dashboard, and CI smoke updates
This commit is contained in:
Executable
+118
@@ -0,0 +1,118 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Synthetic fixture DB builder for CI/AppTest/smoke checks.
|
||||
|
||||
The fixture intentionally contains only synthetic identifiers and summary data.
|
||||
It must never point at the operating ledger database.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from jarca_ledger_mcp import service
|
||||
from jarca_ledger_mcp.db import DEFAULT_DB_PATH, init_db
|
||||
|
||||
|
||||
def assert_ci_fixture_db_path(path: Path) -> Path:
|
||||
resolved = path.expanduser().resolve()
|
||||
if resolved == DEFAULT_DB_PATH.resolve():
|
||||
raise ValueError("refusing to use the operating ledger DB as a CI fixture")
|
||||
if resolved.exists() and resolved.is_dir():
|
||||
raise ValueError(f"fixture DB path is a directory: {resolved}")
|
||||
allowed_names = {"fixture.sqlite3", "ci-ledger-fixture.sqlite3", "ledger-fixture.sqlite3"}
|
||||
if resolved.name not in allowed_names:
|
||||
raise ValueError(f"CI fixture DB filename must be one of {sorted(allowed_names)}: {resolved.name}")
|
||||
return resolved
|
||||
|
||||
|
||||
def create_ci_fixture_db(db_path: str | Path) -> dict[str, Any]:
|
||||
path = assert_ci_fixture_db_path(Path(db_path))
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
if path.exists():
|
||||
path.unlink()
|
||||
init_db(str(path))
|
||||
service.seed_default_ledger_categories(db_path=str(path))
|
||||
service.seed_default_category_rules(db_path=str(path))
|
||||
|
||||
account = service.create_ledger_account(
|
||||
"CI Synthetic Checking",
|
||||
institution="CI Synthetic Bank",
|
||||
holder_name="CI Synthetic Holder",
|
||||
last_balance=1_250_000,
|
||||
last_synced_at="2026-06-30T23:59:00+09:00",
|
||||
statement_status="partial_import",
|
||||
liquidity_bucket="cash",
|
||||
memo="ci synthetic fixture account",
|
||||
db_path=str(path),
|
||||
)
|
||||
if not account.get("success"):
|
||||
raise RuntimeError(f"fixture account seed failed: {account.get('error_code')}")
|
||||
account_id = account["account"]["id"]
|
||||
|
||||
for account_spec in [
|
||||
dict(name="CI Synthetic Investment", type="investment", institution="CI Synthetic Securities", last_balance=5_000_000, liquidity_bucket="investment"),
|
||||
dict(name="CI Synthetic Loan", type="loan", institution="CI Synthetic Bank", last_balance=2_000_000, liquidity_bucket="loan"),
|
||||
dict(name="CI Synthetic Manual Asset", type="other", institution="Manual", liquidity_bucket="real_estate"),
|
||||
]:
|
||||
result = service.create_ledger_account(
|
||||
last_synced_at="2026-06-30T23:59:00+09:00",
|
||||
memo="ci synthetic fixture account",
|
||||
db_path=str(path),
|
||||
**account_spec,
|
||||
)
|
||||
if not result.get("success"):
|
||||
raise RuntimeError(f"fixture account seed failed: {result.get('error_code')}")
|
||||
|
||||
budget = service.create_ledger_budget("2026-06", "식비", 50_000, memo="ci synthetic budget", db_path=str(path))
|
||||
if not budget.get("success"):
|
||||
raise RuntimeError(f"fixture budget seed failed: {budget.get('error_code')}")
|
||||
|
||||
tx_specs: list[dict[str, Any]] = [
|
||||
dict(transaction_date="2026-06-25", type="income", amount=2_000_000, merchant="CI_SYNTHETIC_SALARY", category="수입", subcategory="급여", source="manual", memo="ci synthetic income"),
|
||||
dict(transaction_date="2026-06-26", type="expense", amount=35_000, merchant="CI_SYNTHETIC_DINING", category="식비", subcategory="외식", source="manual", memo="ci synthetic expense"),
|
||||
dict(transaction_date="2026-06-27", type="expense", amount=18_500, merchant="CI_SYNTHETIC_MART", category="", subcategory="", source="manual", memo="ci synthetic uncategorized", raw_summary="ci synthetic grocery"),
|
||||
dict(transaction_date="2026-06-28", type="expense", amount=4_800, merchant="CI_SYNTHETIC_CAFE", category="", subcategory="", source="manual", memo="ci synthetic uncategorized", raw_summary="ci synthetic cafe"),
|
||||
dict(transaction_date="2026-06-29", type="expense", amount=120_000, merchant="CI_SYNTHETIC_TRANSFER", category="이체", subcategory="확인필요", source="kb_bank_pdf", counterparty="CI_SYNTHETIC_TOSS", memo="cross_transfer_review: ci synthetic", raw_summary="ci synthetic transfer", balance_after=1_250_000),
|
||||
]
|
||||
transaction_ids: list[str] = []
|
||||
for index, spec in enumerate(tx_specs, start=1):
|
||||
result = service.create_ledger_transaction(
|
||||
account_id=account_id,
|
||||
external_id=f"ci-fixture-{index}",
|
||||
db_path=str(path),
|
||||
**spec,
|
||||
)
|
||||
if not result.get("success"):
|
||||
raise RuntimeError(f"fixture transaction seed failed: {result.get('error_code')}")
|
||||
transaction_ids.append(result["transaction"]["id"])
|
||||
|
||||
status = service.get_ledger_db_status(str(path), include_hash=False, include_integrity=True)
|
||||
if not status.get("success") or status.get("integrity_check") != "ok":
|
||||
raise RuntimeError(f"fixture health check failed: {status.get('error_code')}")
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"fixture_db": str(path),
|
||||
"account_count": 4,
|
||||
"transaction_count": len(transaction_ids),
|
||||
"integrity_check": status.get("integrity_check"),
|
||||
}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description="Create a synthetic Jarca Ledger CI fixture DB.")
|
||||
parser.add_argument("--db", required=True, help="Output fixture path, e.g. $RUNNER_TEMP/fixture.sqlite3")
|
||||
args = parser.parse_args()
|
||||
try:
|
||||
result = create_ci_fixture_db(args.db)
|
||||
except Exception as exc: # noqa: BLE001 - CLI wrapper reports deterministic failure
|
||||
print(json.dumps({"success": False, "error_code": "fixture_seed_failed", "error": str(exc)}, ensure_ascii=False))
|
||||
return 1
|
||||
print(json.dumps(result, ensure_ascii=False, indent=2))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user