#!/usr/bin/env python3 """invest_ledger_check.py 단위 테스트 (stdlib unittest).""" import importlib.util import sys import tempfile import unittest from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parent)) _SPEC = importlib.util.spec_from_file_location( "ilc", str(Path(__file__).with_name("invest_ledger_check.py"))) ilc = importlib.util.module_from_spec(_SPEC) _SPEC.loader.exec_module(ilc) import datetime as dt HEADER = ("| 날짜 | 매수/매도 | 종목 | 수량 | 단가 | 수수료 | 체결환율 | 금액(원) | 계좌 | 근거(링크) | 규칙체크 |\n" "|---|---|---|---|---|---|---|---|---|---|---|\n") def ledger(rows: str) -> str: return ("# 원장\n## 현재 포지션 / Open Positions\n| a |\n|---|\n" "## 거래 내역 / Trade Log\n" + HEADER + rows + "\n## 규칙 위반 이력 / Rule-check Findings\n## 손익 요약 / P&L Summary\n") TODAY = dt.date(2026, 6, 10) def mk_evidence(root: Path, rel: str, date_str: str): p = root / (rel + ".md") p.parent.mkdir(parents=True, exist_ok=True) p.write_text(f"---\ntitle: x\ndate: {date_str}\n---\n# t\n", encoding="utf-8") class TestCheck(unittest.TestCase): def _run(self, rows, root, **kw): return ilc.run_check(ledger(rows), root, kw.get("daily_max_h", 24), kw.get("research_max_d", 90), kw.get("weekly_cap"), TODAY) def test_clean_row_passes(self): with tempfile.TemporaryDirectory() as d: root = Path(d) mk_evidence(root, "raw/invest-research/2026-06-08-x", "2026-06-08") row = "| 2026-06-10 | 매수 | SCHD | 2 | 27.5 | 0.1 | 1380 | 76000 | ISA | [[raw/invest-research/2026-06-08-x]] | ✅ |" self.assertEqual(self._run(row, root), 0) def test_no_evidence_flags(self): with tempfile.TemporaryDirectory() as d: row = "| 2026-06-10 | 매수 | SCHD | 2 | 27.5 | 0.1 | 1380 | 76000 | ISA | 감으로 | ✅ |" self.assertEqual(self._run(row, Path(d)), 1) def test_missing_evidence_target_flags(self): with tempfile.TemporaryDirectory() as d: row = "| 2026-06-10 | 매수 | SCHD | 2 | 27.5 | 0.1 | 1380 | 76000 | ISA | [[raw/invest-research/ghost]] | ✅ |" self.assertEqual(self._run(row, Path(d)), 1) def test_stale_daily_evidence_flags(self): with tempfile.TemporaryDirectory() as d: root = Path(d) mk_evidence(root, "raw/invest-daily/2026-06-06", "2026-06-06") # 4d > 24h row = "| 2026-06-10 | 매도 | SCHD | 1 | 27.5 | 0.1 | 1380 | 38000 | ISA | [[raw/invest-daily/2026-06-06]] | ✅ |" self.assertEqual(self._run(row, root), 1) def test_fresh_research_passes_but_old_flags(self): with tempfile.TemporaryDirectory() as d: root = Path(d) mk_evidence(root, "raw/invest-research/2026-01-01-x", "2026-01-01") # 160d > 90d row = "| 2026-06-10 | 매수 | SCHD | 2 | 27.5 | 0.1 | 1380 | 76000 | ISA | [[raw/invest-research/2026-01-01-x]] | ✅ |" self.assertEqual(self._run(row, root), 1) def test_bad_schema_flags(self): with tempfile.TemporaryDirectory() as d: row = "| 2026-06-10 | 매수 | SCHD | 2 |" self.assertEqual(self._run(row, Path(d)), 1) def test_weekly_cap(self): with tempfile.TemporaryDirectory() as d: root = Path(d) mk_evidence(root, "raw/invest-research/2026-06-08-x", "2026-06-08") ev = "[[raw/invest-research/2026-06-08-x]]" rows = "\n".join( f"| 2026-06-{day} | 매수 | SCHD | 1 | 27.5 | 0.1 | 1380 | 38000 | ISA | {ev} | ✅ |" for day in ("08", "09", "10")) # 같은 ISO 주 3건 self.assertEqual(self._run(rows, root, weekly_cap=2), 1) self.assertEqual(self._run(rows, root, weekly_cap=3), 0) class TestReport(unittest.TestCase): def test_aggregates(self): import io import contextlib rows = ("| 2026-06-10 | 매수 | SCHD | 2 | 27.5 | 100 | 1380 | 76000 | ISA | [[raw/invest-research/x]] | ✅ |\n" "| 2026-06-09 | 매수 | SCHD | 1 | 30 | 50 | 1380 | 41400 | ISA | [[raw/invest-research/x]] | ✅ |") buf = io.StringIO() with contextlib.redirect_stdout(buf): rc = ilc.run_report(ledger(rows), TODAY) out = buf.getvalue() self.assertEqual(rc, 0) self.assertIn("매수 합(원): 117,400", out) self.assertIn("누적 수수료: 150", out) self.assertIn("순수량 3", out) # SCHD 2+1 self.assertIn("28.33", out) # (2*27.5 + 1*30)/3 매수가중 if __name__ == "__main__": unittest.main(verbosity=2)