Files
jarca-ledger-mcp/tests/test_account_evaluation_display.py

251 lines
12 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from __future__ import annotations
import sqlite3
from streamlit.testing.v1 import AppTest
from jarca_ledger_mcp.application.monthly_settlement_service import (
create_monthly_settlement,
list_account_snapshots_for_settlement,
)
from jarca_ledger_mcp.infrastructure.sqlite.migrations import init_db
from dashboard.page_views.monthly_settlement import _account_evaluation_totals, _format_evaluation_total
def test_account_evaluation_totals_native_currency_null_and_zero():
result = _account_evaluation_totals([
{"account_id": "a1", "snapshot_id": "s1", "amount": 0, "currency": "KRW"},
{"account_id": "a2", "snapshot_id": "s2", "amount": -2500, "currency": "USD"},
{"account_id": "a3", "snapshot_id": "s3", "amount": None, "currency": "KRW"},
{"account_id": "a4", "snapshot_id": None, "amount": 999999, "currency": "KRW"},
])
assert result["totals"] == {"KRW": 0, "USD": -2500}
assert result["unknown_count"] == 1
assert result["registered_count"] == 3
assert result["unregistered_count"] == 1
assert _format_evaluation_total("KRW", 0) == "0원"
assert _format_evaluation_total("USD", -2500) == "-2,500 USD"
def test_account_evaluation_totals_missing_currency_is_separate():
result = _account_evaluation_totals([
{"account_id": "a1", "snapshot_id": "s1", "amount": 100, "currency": None},
{"account_id": "a2", "snapshot_id": None, "amount": None, "currency": "KRW"},
])
assert result["totals"] == {"미지정": 100}
def _account(conn, ident, name, currency="KRW"):
conn.execute(
"INSERT INTO accounts(id,name,currency,created_at,updated_at) VALUES(?,?,?,?,?)",
(ident, name, currency, "2026-10-01T00:00:00Z", "2026-10-01T00:00:00Z"),
)
def _snapshot(conn, ident, settlement, account, name, amount, currency="KRW"):
conn.execute(
"""INSERT INTO account_snapshot
(id,basis_date,source_type,source_id,name,amount,currency,monthly_settlement_id,created_at)
VALUES(?,?,?,?,?,?,?,?,?)""",
(ident, "2026-10-31", "account", account, name, amount, currency, settlement, "2026-10-31T00:00:00Z"),
)
def test_draft_uses_all_accounts_and_exact_monthly_snapshot(tmp_path):
path = tmp_path / "ledger.sqlite3"
init_db(str(path))
settlement = create_monthly_settlement("2026-10", db_path=str(path))["settlement_id"]
with sqlite3.connect(path) as conn:
_account(conn, "a1", "계좌1")
_account(conn, "a2", "계좌2")
_snapshot(conn, "s1", settlement, "a1", "계좌1", 0)
_snapshot(conn, "other", "other-month", "a2", "계좌2", 99)
conn.commit()
result = list_account_snapshots_for_settlement(settlement, db_path=str(path))
assert [(x["source_id"], x["amount"], x["display_status"]) for x in result["snapshots"]] == [("a1", 0, "확인"), ("a2", None, "미등록")]
assert result["count"] == 2 and result["registered_count"] == 1 and result["unregistered_count"] == 1
def test_closed_uses_historical_rows_only_and_preserves_null_zero_and_duplicate(tmp_path):
path = tmp_path / "ledger.sqlite3"
init_db(str(path))
settlement = create_monthly_settlement("2026-10", db_path=str(path))["settlement_id"]
with sqlite3.connect(path) as conn:
_account(conn, "old", "과거계좌", "USD")
_snapshot(conn, "s-null", settlement, "old", "보존이름", None, "USD")
_snapshot(conn, "s-zero", settlement, "old", "보존이름", 0, "USD")
conn.execute("UPDATE monthly_settlement SET status='closed', approved_by='test', approved_at='2026-10-31T00:00:00Z', approved_version=1, approved_fingerprint='fp', closed_by='test', closed_at='2026-10-31T00:00:00Z' WHERE id=?", (settlement,))
_account(conn, "new", "신규계좌")
conn.commit()
result = list_account_snapshots_for_settlement(settlement, db_path=str(path))
assert result["closed"] is True
assert [x["amount"] for x in result["snapshots"]] == [None, 0]
assert [x["display_status"] for x in result["snapshots"]] == ["미확인", "확인"]
assert all(x["name"] == "보존이름" for x in result["snapshots"])
assert all(x["source_id"] != "new" for x in result["snapshots"])
def test_monthly_renderer_opens_real_registration_dialog_from_actual_button(monkeypatch):
from jarca_ledger_mcp.adapters.streamlit import overview_gateway, settlement_gateway
monkeypatch.setattr(
overview_gateway,
"get_monthly_settlement_summary",
lambda *args, **kwargs: {
"success": True,
"month": "2026-10",
"selected_month": "2026-10",
"basis_date": "2026-10-31",
"actual_income": 0,
"actual_expense": 0,
"net_cashflow": 0,
"reports": {"cashflow": {}, "budget": {}, "assets": {}},
},
)
monkeypatch.setattr(
settlement_gateway,
"get_monthly_settlement_record",
lambda *args, **kwargs: {"success": True, "settlement": {"id": "ms-1", "month": "2026-10", "status": "draft"}},
)
monkeypatch.setattr(
settlement_gateway,
"list_account_snapshots_for_settlement",
lambda *args, **kwargs: {
"success": True,
"closed": False,
"count": 3,
"registered_count": 2,
"unregistered_count": 1,
"snapshots": [
{"account_id": "acct-1", "account_name": "주계좌", "amount": 1630800, "currency": "KRW", "basis_date": "2026-10-31", "display_status": "확인", "snapshot_id": "snap-1", "account_type": "bank", "account_liquidity_bucket": "bank"},
{"account_id": "acct-2", "account_name": "임시 주택담보대출", "amount": 1562300, "currency": "KRW", "basis_date": "2026-10-31", "display_status": "확인", "snapshot_id": "snap-2", "account_type": "loan", "account_liquidity_bucket": "loan"},
{"account_id": "acct-3", "account_name": "보조계좌", "amount": None, "currency": "USD", "basis_date": None, "display_status": "미등록", "snapshot_id": None},
],
},
)
app = AppTest.from_string(
"import streamlit as st\n"
"st.query_params.update(page='monthly_settlement', id='ms-1')\n"
"from dashboard.page_views.monthly_settlement import page_monthly_settlement\n"
"page_monthly_settlement(db_path='synthetic-unused.sqlite3')\n",
default_timeout=30,
).run()
assert not app.exception
assert any(metric.label == "자산 합계 (KRW)" and metric.value == "1,630,800원" for metric in app.metric)
assert any(metric.label == "부채 합계 (KRW)" and metric.value == "1,562,300원" for metric in app.metric)
assert any(metric.label == "순자산 (KRW)" and metric.value == "68,500원" for metric in app.metric)
metric_columns = [column for column in app.columns if column.metric]
assert len(metric_columns) == 3
assert [column.metric[0].label for column in metric_columns] == [
"자산 합계 (KRW)", "부채 합계 (KRW)", "순자산 (KRW)",
]
assert any("평가등록 2 / 전체 3" in caption.value and "미등록 1" in caption.value for caption in app.caption)
assert not [expander for expander in app.expander if expander.label == "필요 메모/근거"]
account_buttons = [button for button in app.button if button.label == "보조계좌"]
assert len(account_buttons) == 1
assert not [button for button in app.button if button.label == "평가 등록"]
dialog = account_buttons[0].click().run()
assert not dialog.exception
inputs = {widget.label: widget for widget in dialog.text_input}
assert inputs["계정"].value == "보조계좌"
assert inputs["계정"].disabled is True
assert inputs["통화"].value == "USD"
assert dialog.date_input[0].value.isoformat() == "2026-10-31"
assert "평가금액 (정수)" in inputs
assert dialog.text_area[0].label == "메모 (선택)"
def test_monthly_renderer_preserves_ledger_projection_and_source_choices(monkeypatch):
from jarca_ledger_mcp.adapters.streamlit import overview_gateway, settlement_gateway
monkeypatch.setattr(
overview_gateway,
"get_monthly_settlement_summary",
lambda *args, **kwargs: {
"success": True, "month": "2026-10", "selected_month": "2026-10",
"basis_date": "2026-10-31", "reports": {"cashflow": {}, "budget": {}, "assets": {}},
},
)
monkeypatch.setattr(
settlement_gateway,
"get_monthly_settlement_record",
lambda *args, **kwargs: {"success": True, "settlement": {"id": "ms-10", "month": "2026-10", "status": "draft"}},
)
monkeypatch.setattr(
settlement_gateway,
"list_account_snapshots_for_settlement",
lambda *args, **kwargs: {
"success": True, "closed": False, "count": 1, "registered_count": 0,
"unregistered_count": 1,
"snapshots": [{
"account_id": "acct-10", "account_name": "실제계정10", "amount": None,
"currency": "KRW", "basis_date": None, "display_status": "미등록",
"snapshot_id": None,
"previous_ledger": {"id": "prev-10", "transaction_date": "2026-10-30", "balance_after": 1234, "currency": "KRW"},
"next_ledger": {"id": "next-10", "transaction_date": "2026-11-01", "balance_after": 2345, "currency": "KRW"},
}],
},
)
app = AppTest.from_string(
"import streamlit as st\n"
"st.query_params.update(page='monthly_settlement', id='ms-10')\n"
"from dashboard.page_views.monthly_settlement import page_monthly_settlement\n"
"page_monthly_settlement(db_path='synthetic-unused.sqlite3')\n",
default_timeout=30,
).run()
assert not app.exception
dialog = [button for button in app.button if button.label == "실제계정10"][0].click().run()
assert not dialog.exception
source = dialog.selectbox[0]
assert len(source.options) == 3
assert source.value.startswith("이전 기록")
inputs = {widget.label: widget for widget in dialog.text_input}
assert inputs["평가금액 (정수)"].value == "1234"
assert inputs["평가금액 (정수)"].disabled is True
assert any(option.startswith("이후 기록") for option in source.options)
assert inputs["평가금액 (정수)"].disabled is True
def test_monthly_renderer_mixed_rows_keeps_snapshot_id_for_unlink(monkeypatch):
from jarca_ledger_mcp.adapters.streamlit import overview_gateway, settlement_gateway
monkeypatch.setattr(overview_gateway, "get_monthly_settlement_summary", lambda *args, **kwargs: {
"success": True, "month": "2026-10", "selected_month": "2026-10",
"basis_date": "2026-10-31", "reports": {"cashflow": {}, "budget": {}, "assets": {}},
})
monkeypatch.setattr(settlement_gateway, "get_monthly_settlement_record", lambda *args, **kwargs: {
"success": True, "settlement": {"id": "ms-mixed", "month": "2026-10", "status": "draft"},
})
monkeypatch.setattr(settlement_gateway, "list_account_snapshots_for_settlement", lambda *args, **kwargs: {
"success": True, "closed": False, "status": "draft", "count": 2,
"registered_count": 1, "unregistered_count": 1,
"snapshots": [
{"account_id": "acct-1", "account_name": "등록계정", "amount": 100,
"currency": "KRW", "basis_date": "2026-10-31", "display_status": "확인",
"snapshot_id": "snap-1"},
{"account_id": "acct-2", "account_name": "미등록계정", "amount": None,
"currency": "USD", "basis_date": None, "display_status": "미등록",
"snapshot_id": None},
],
})
app = AppTest.from_string(
"import streamlit as st\n"
"st.query_params.update(page='monthly_settlement', id='ms-mixed')\n"
"from dashboard.page_views.monthly_settlement import page_monthly_settlement\n"
"page_monthly_settlement(db_path='synthetic-unused.sqlite3')\n",
default_timeout=30,
).run()
assert not app.exception
unlink = [button for button in app.button if button.label == "×"]
assert len(unlink) == 1
assert unlink[0].help == "연결 해제"
dialog = unlink[0].click().run()
assert not dialog.exception
assert any("등록계정" in markdown.value for markdown in dialog.markdown)