Initial Jarca Ledger MCP implementation
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from jarca_ledger_mcp import service
|
||||
|
||||
|
||||
def test_health_check_initializes_db(tmp_path):
|
||||
db_path = tmp_path / "ledger.sqlite3"
|
||||
result = service.health_check(str(db_path))
|
||||
assert result["success"] is True
|
||||
assert result["schema_version"] == "6"
|
||||
assert db_path.exists()
|
||||
|
||||
|
||||
def test_account_transaction_cashflow_and_category_reports(tmp_path):
|
||||
db_path = str(tmp_path / "ledger.sqlite3")
|
||||
acct = service.create_ledger_account("신한카드", type="card", institution="신한", db_path=db_path)
|
||||
assert acct["success"] is True
|
||||
account_id = acct["account"]["id"]
|
||||
|
||||
income = service.create_ledger_transaction(
|
||||
"2026-06-01",
|
||||
"income",
|
||||
8_000_000,
|
||||
category="수입",
|
||||
merchant="회사",
|
||||
account_id=account_id,
|
||||
db_path=db_path,
|
||||
)
|
||||
assert income["success"] is True
|
||||
|
||||
expense = service.create_ledger_transaction(
|
||||
"2026-06-03",
|
||||
"expense",
|
||||
120_000,
|
||||
category="식비",
|
||||
subcategory="마트",
|
||||
merchant="이마트",
|
||||
account_id=account_id,
|
||||
person="공통",
|
||||
tags=["장보기"],
|
||||
db_path=db_path,
|
||||
)
|
||||
assert expense["success"] is True
|
||||
|
||||
fixed = service.create_ledger_transaction(
|
||||
"2026-06-05",
|
||||
"expense",
|
||||
900_000,
|
||||
category="교육",
|
||||
subcategory="학원",
|
||||
merchant="물리학원",
|
||||
person="고재성",
|
||||
is_fixed=True,
|
||||
db_path=db_path,
|
||||
)
|
||||
assert fixed["success"] is True
|
||||
|
||||
investment_in = service.create_ledger_transaction(
|
||||
"2026-06-06",
|
||||
"income",
|
||||
5_000_000,
|
||||
category="이체",
|
||||
subcategory="투자이동",
|
||||
merchant="토스증권 매도/이체입금",
|
||||
db_path=db_path,
|
||||
)
|
||||
assert investment_in["success"] is True
|
||||
|
||||
investment_out = service.create_ledger_transaction(
|
||||
"2026-06-07",
|
||||
"expense",
|
||||
3_000_000,
|
||||
category="이체",
|
||||
subcategory="투자이동",
|
||||
merchant="토스증권 매수/이체출금",
|
||||
is_fixed=True,
|
||||
db_path=db_path,
|
||||
)
|
||||
assert investment_out["success"] is True
|
||||
|
||||
cashflow = service.get_monthly_cashflow_report("2026-06", db_path)
|
||||
assert cashflow["success"] is True
|
||||
assert cashflow["income"] == 8_000_000
|
||||
assert cashflow["expenses"] == 1_020_000
|
||||
assert cashflow["net_cashflow"] == 6_980_000
|
||||
assert cashflow["by_type"]["income"] == 13_000_000
|
||||
assert cashflow["by_type"]["expense"] == 4_020_000
|
||||
assert cashflow["by_cashflow_kind"]["investment"] == 8_000_000
|
||||
|
||||
categories = service.get_category_spending_report(month="2026-06", db_path=db_path)
|
||||
assert categories["total_expense"] == 1_020_000
|
||||
assert categories["items"][0]["category"] == "교육"
|
||||
|
||||
fixed_report = service.get_fixed_cost_report("2026-06", db_path)
|
||||
assert fixed_report["total_fixed_cost"] == 900_000
|
||||
assert fixed_report["count"] == 1
|
||||
|
||||
|
||||
def test_budget_vs_actual_and_investable_cashflow(tmp_path):
|
||||
db_path = str(tmp_path / "ledger.sqlite3")
|
||||
service.create_ledger_budget("2026-06", "식비", 500_000, db_path=db_path)
|
||||
service.create_ledger_budget("2026-06", "교육", 800_000, person="고재준", db_path=db_path)
|
||||
service.create_ledger_transaction("2026-06-01", "income", 4_000_000, category="수입", db_path=db_path)
|
||||
service.create_ledger_transaction("2026-06-02", "expense", 550_000, category="식비", db_path=db_path)
|
||||
service.create_ledger_transaction("2026-06-03", "expense", 700_000, category="교육", person="고재준", db_path=db_path)
|
||||
service.create_ledger_transaction("2026-06-04", "expense", 2_000_000, category="이체", subcategory="투자이동", db_path=db_path)
|
||||
service.create_ledger_transaction("2026-06-05", "income", 1_000_000, category="이체", subcategory="투자이동", db_path=db_path)
|
||||
|
||||
report = service.get_budget_vs_actual_report("2026-06", db_path)
|
||||
assert report["success"] is True
|
||||
food = next(i for i in report["items"] if i["category"] == "식비")
|
||||
edu = next(i for i in report["items"] if i["category"] == "교육")
|
||||
assert food["status"] == "over"
|
||||
assert food["remaining"] == -50_000
|
||||
assert edu["status"] == "ok"
|
||||
assert edu["remaining"] == 100_000
|
||||
|
||||
investable = service.get_investable_cashflow_report("2026-06", minimum_cash_buffer=1_000_000, planned_extra_expense=500_000, db_path=db_path)
|
||||
assert investable["investable_cashflow"] == 1_250_000
|
||||
|
||||
|
||||
def test_duplicate_detection_and_soft_delete(tmp_path):
|
||||
db_path = str(tmp_path / "ledger.sqlite3")
|
||||
first = service.create_ledger_transaction("2026-06-10", "expense", 10_000, merchant="카페", account_id="card1", db_path=db_path)
|
||||
second = service.create_ledger_transaction("2026-06-10", "expense", 10_000, merchant="카페", account_id="card1", db_path=db_path)
|
||||
assert first["success"] is True
|
||||
assert second["success"] is False
|
||||
assert second["error_code"] == "duplicate_transaction"
|
||||
|
||||
tx_id = first["transaction"]["id"]
|
||||
deleted = service.delete_ledger_transaction(tx_id, db_path=db_path)
|
||||
assert deleted["success"] is True
|
||||
listed = service.list_ledger_transactions(include_deleted=False, db_path=db_path)
|
||||
assert listed["count"] == 0
|
||||
listed_all = service.list_ledger_transactions(include_deleted=True, db_path=db_path)
|
||||
assert listed_all["count"] == 1
|
||||
assert listed_all["transactions"][0]["status"] == "deleted"
|
||||
Reference in New Issue
Block a user