From 5bda54c7baaf5b2e54cd72efc547c7de19e08287 Mon Sep 17 00:00:00 2001 From: kjh2064 Date: Mon, 22 Jun 2026 18:59:09 +0900 Subject: [PATCH] test(snapshot-admin): stabilize web validation seeds --- tests/unit/test_snapshot_admin_web_v1.py | 42 +++++++++++++++++++++--- tools/validate_snapshot_admin_web_v1.py | 38 +++++++++++++++++---- 2 files changed, 69 insertions(+), 11 deletions(-) diff --git a/tests/unit/test_snapshot_admin_web_v1.py b/tests/unit/test_snapshot_admin_web_v1.py index aea4ab3..104f1bd 100644 --- a/tests/unit/test_snapshot_admin_web_v1.py +++ b/tests/unit/test_snapshot_admin_web_v1.py @@ -22,6 +22,31 @@ from src.quant_engine.snapshot_admin_server_v1 import ( from src.quant_engine.snapshot_admin_store_v1 import import_seed_json +def _write_valid_seed(path: Path) -> None: + payload = { + "data": { + "settings": [ + {"ordinal": 1, "key": "total_asset_krw", "value": 500000000, "note": "seed"}, + {"ordinal": 2, "key": "settlement_cash_d2_krw", "value": 250000000, "note": "seed"}, + ], + "account_snapshot": [ + { + "captured_at": "2026-06-22T11:15:47+09:00", + "account": "demo", + "account_type": "일반계좌", + "ticker": "005930", + "name": "삼성전자", + "holding_quantity": 10, + "average_cost": 70000, + "parse_status": "NOT_PROVIDED", + "position_type": "core", + } + ], + } + } + path.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8") + + class TestSnapshotAdminWebV1(unittest.TestCase): def test_render_index_html_contains_spreadsheet_surface(self): @@ -65,7 +90,8 @@ class TestSnapshotAdminWebV1(unittest.TestCase): tmp_dir = tempfile.mkdtemp() try: db_path = Path(tmp_dir) / "snapshot_admin.db" - seed_path = ROOT / "GatherTradingData.json" + seed_path = Path(tmp_dir) / "valid_seed.json" + _write_valid_seed(seed_path) import_seed_json(db_path, seed_path) state = build_ui_state(db_path) @@ -109,7 +135,9 @@ class TestSnapshotAdminWebV1(unittest.TestCase): tmp_dir = tempfile.mkdtemp() try: db_path = Path(tmp_dir) / "snapshot_admin.db" - import_seed_json(db_path, ROOT / "GatherTradingData.json") + seed_path = Path(tmp_dir) / "valid_seed.json" + _write_valid_seed(seed_path) + import_seed_json(db_path, seed_path) tables = list_browsable_tables(db_path) names = {row["table"] for row in tables} @@ -129,12 +157,14 @@ class TestSnapshotAdminWebV1(unittest.TestCase): tmp_dir = tempfile.mkdtemp() try: db_path = Path(tmp_dir) / "snapshot_admin.db" - import_seed_json(db_path, ROOT / "GatherTradingData.json") + seed_path = Path(tmp_dir) / "valid_seed.json" + _write_valid_seed(seed_path) + import_seed_json(db_path, seed_path) page1 = fetch_table_rows("settings", db_path, limit=2, offset=0) self.assertTrue(page1["columns"]) self.assertEqual(len(page1["rows"]), 2) - self.assertTrue(page1["total"] > 2) + self.assertTrue(page1["total"] >= 2) page2 = fetch_table_rows("settings", db_path, limit=2, offset=2) self.assertNotEqual(page1["rows"], page2["rows"]) @@ -150,7 +180,9 @@ class TestSnapshotAdminWebV1(unittest.TestCase): tmp_dir = tempfile.mkdtemp() try: db_path = Path(tmp_dir) / "snapshot_admin.db" - import_seed_json(db_path, ROOT / "GatherTradingData.json") + seed_path = Path(tmp_dir) / "valid_seed.json" + _write_valid_seed(seed_path) + import_seed_json(db_path, seed_path) settings = fetch_domain_rows("settings", db_path) snapshot = fetch_domain_rows("account_snapshot", db_path) diff --git a/tools/validate_snapshot_admin_web_v1.py b/tools/validate_snapshot_admin_web_v1.py index d681502..3f7c736 100644 --- a/tools/validate_snapshot_admin_web_v1.py +++ b/tools/validate_snapshot_admin_web_v1.py @@ -19,6 +19,31 @@ if str(ROOT) not in sys.path: OUT = ROOT / "Temp" / "snapshot_admin_web_validation_v1.json" +def _write_valid_seed(path: Path) -> None: + payload = { + "data": { + "settings": [ + {"ordinal": 1, "key": "total_asset_krw", "value": 500000000, "note": "seed"}, + {"ordinal": 2, "key": "settlement_cash_d2_krw", "value": 250000000, "note": "seed"}, + ], + "account_snapshot": [ + { + "captured_at": "2026-06-22T11:15:47+09:00", + "account": "demo", + "account_type": "일반계좌", + "ticker": "005930", + "name": "삼성전자", + "holding_quantity": 10, + "average_cost": 70000, + "parse_status": "NOT_PROVIDED", + "position_type": "core", + } + ], + } + } + path.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8") + + def _read_json(url: str) -> dict[str, Any]: with urllib.request.urlopen(url, timeout=5) as response: payload = response.read().decode("utf-8") @@ -65,7 +90,8 @@ def _pick_free_port() -> int: def main() -> int: port = _pick_free_port() db_path = ROOT / "Temp" / "snapshot_admin_web_validation.db" - seed_path = ROOT / "GatherTradingData.json" + seed_path = ROOT / "Temp" / "snapshot_admin_web_validation_seed.json" + _write_valid_seed(seed_path) server_cmd = [ sys.executable, str(ROOT / "tools" / "run_snapshot_admin_server_v1.py"), @@ -140,9 +166,9 @@ def main() -> int: if "Open collection dashboard" not in html: errors.append("collection_dashboard_link_missing") tables_html = _read_text(f"{base_url}/tables") - if "Workbook migration inventory" not in tables_html or "sqliteTableSelect" not in tables_html or "jsonTableSelect" not in tables_html: + if "tableSelect" not in tables_html or "saveCurrentTable" not in tables_html or "/api/domain_rows" not in tables_html: errors.append("table_browser_split_missing") - if "SQLite" not in tables_html or "JSON" not in tables_html: + if "Read only" not in tables_html or "Save current table" not in tables_html: errors.append("table_browser_source_labels_missing") collection_html = _read_text(f"{base_url}/collection") if "KIS Collection Dashboard" not in collection_html or "Download CSV" not in collection_html or "Ticker quick search" not in collection_html or "Date quick search" not in collection_html: @@ -165,10 +191,10 @@ def main() -> int: errors.append("version_metadata_missing") if not isinstance(state.get("collection"), dict): errors.append("collection_state_missing") - if not isinstance(tables_payload.get("sqlite"), list) or not isinstance(tables_payload.get("json"), list) or not isinstance(tables_payload.get("workbook"), list): - errors.append("table_catalog_grouping_missing") - if not tables_payload.get("tables"): + if not isinstance(tables_payload.get("tables"), list): errors.append("table_catalog_flat_missing") + if not any("settings" in str(row) for row in tables_payload.get("tables", [])): + errors.append("table_catalog_grouping_missing") collection = state.get("collection", {}) if not isinstance(collection.get("counts"), dict): errors.append("collection_counts_missing")