test(validation): 토큰 위생 및 플랫폼 통합 검증 체계 고도화
Snapshot Admin Web Validation / validate-snapshot-admin-smoke (push) Has been cancelled
Snapshot Admin Web Validation / validate-snapshot-admin-full (push) Has been cancelled
Quant Engine CI/CD Pipeline / validate-core (pull_request) Has been cancelled
Quant Engine CI/CD Pipeline / validate-ui-and-storage (pull_request) Has been cancelled
WBS-9.3 - NULL Policy CI Gate / NULL Policy Validation (pull_request) Has been cancelled
Snapshot Admin Web Validation / validate-snapshot-admin-smoke (push) Has been cancelled
Snapshot Admin Web Validation / validate-snapshot-admin-full (push) Has been cancelled
Quant Engine CI/CD Pipeline / validate-core (pull_request) Has been cancelled
Quant Engine CI/CD Pipeline / validate-ui-and-storage (pull_request) Has been cancelled
WBS-9.3 - NULL Policy CI Gate / NULL Policy Validation (pull_request) Has been cancelled
This commit is contained in:
@@ -4,6 +4,7 @@ import json
|
||||
import sys
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
if str(ROOT) not in sys.path:
|
||||
@@ -57,6 +58,14 @@ class TestSnapshotAdminWebV1(unittest.TestCase):
|
||||
self.assertIn("/api/account_snapshot/save", html)
|
||||
self.assertIn("opsBanner", html)
|
||||
self.assertIn("bannerApprovalSummary", html)
|
||||
self.assertIn("heroNextAction", html)
|
||||
self.assertIn("heroPrimaryAction", html)
|
||||
self.assertIn("heroCollection", html)
|
||||
self.assertIn("approvalPanel", html)
|
||||
self.assertIn("collectionPanel", html)
|
||||
self.assertIn("selectionPanel", html)
|
||||
self.assertIn("open when you need row-level operations", html)
|
||||
self.assertIn("open", html)
|
||||
self.assertIn("snapshot-panel", html)
|
||||
self.assertIn("selected-field", html)
|
||||
self.assertIn("settingsCountChip", html)
|
||||
@@ -68,6 +77,7 @@ class TestSnapshotAdminWebV1(unittest.TestCase):
|
||||
self.assertIn("Export approval packet", html)
|
||||
self.assertIn("Selection Inspector", html)
|
||||
self.assertIn("Recent row history", html)
|
||||
self.assertIn("Recent change summary", html)
|
||||
self.assertIn("Save view", html)
|
||||
self.assertIn("Apply TSV to selection", html)
|
||||
self.assertIn("Ctrl+S", html)
|
||||
@@ -80,26 +90,238 @@ class TestSnapshotAdminWebV1(unittest.TestCase):
|
||||
self.assertIn("/collection", html)
|
||||
self.assertIn("Open collection dashboard", html)
|
||||
|
||||
def test_render_home_html_contains_role_based_entrances(self):
|
||||
from src.quant_engine.snapshot_admin_server_v1 import render_home_html
|
||||
|
||||
html = render_home_html()
|
||||
self.assertIn("Snapshot Admin Home", html)
|
||||
self.assertIn("1. Workspace", html)
|
||||
self.assertIn("2. Collection", html)
|
||||
self.assertIn("3. Tables", html)
|
||||
self.assertIn("Open workspace", html)
|
||||
self.assertIn("Open collection", html)
|
||||
self.assertIn("Open tables", html)
|
||||
self.assertIn("/workspace", html)
|
||||
self.assertIn("/tables", html)
|
||||
|
||||
def test_render_tables_html_contains_table_group_summary(self):
|
||||
html = render_tables_html()
|
||||
self.assertIn("Snapshot Admin — Table Browser", html)
|
||||
self.assertIn("DB별 수정, JSON별 검토, 수집 증빙 확인", html)
|
||||
self.assertIn("DB 먼저 / JSON은 증빙", html)
|
||||
self.assertIn("tablePurposeNavigator", html)
|
||||
self.assertIn("DB 먼저", html)
|
||||
self.assertIn("JSON은 증빙", html)
|
||||
self.assertIn("Edit only canonical rows", html)
|
||||
self.assertIn("DB tables", html)
|
||||
self.assertIn("Editable source of truth.", html)
|
||||
self.assertIn("Workbook sheets", html)
|
||||
self.assertIn("Derived report evidence.", html)
|
||||
self.assertIn("tablesVersionTop", html)
|
||||
self.assertIn("tablesVersionBottom", html)
|
||||
self.assertIn("final_updated_at=", html)
|
||||
self.assertIn("tablesCoverageWarning", html)
|
||||
self.assertIn("workbookSheetSelect", html)
|
||||
self.assertIn("Workbook sheets only", html)
|
||||
self.assertIn("workbookSheetMeta", html)
|
||||
self.assertIn("workbookSheetSurfaceMeta", html)
|
||||
self.assertIn("workbookSheetDetail", html)
|
||||
self.assertIn("workbookSheetPreview", html)
|
||||
self.assertIn("tableSelect", html)
|
||||
self.assertIn("DB Table", html)
|
||||
self.assertIn("tableGroupSummary", html)
|
||||
self.assertIn("tableSourceSummary", html)
|
||||
self.assertIn("Registry details", html)
|
||||
self.assertIn("History details", html)
|
||||
self.assertIn("Workspace", html)
|
||||
self.assertIn("Collection", html)
|
||||
self.assertIn("Strategy", html)
|
||||
self.assertIn("JSON", html)
|
||||
self.assertIn("tableWorkspaceSection", html)
|
||||
self.assertIn("tableJsonSection", html)
|
||||
self.assertIn("tableEditState", html)
|
||||
self.assertIn("bg-danger-lt", html)
|
||||
self.assertIn("Save applies only to the canonical workspace DB.", html)
|
||||
self.assertIn("Derived JSON Evidence Preview", html)
|
||||
self.assertIn("jsonReportStatus", html)
|
||||
self.assertIn("jsonReportFocus", html)
|
||||
self.assertIn("jsonReportStats", html)
|
||||
self.assertIn("jsonReportDetail", html)
|
||||
self.assertIn("Purpose: edit canonical workspace rows only.", html)
|
||||
self.assertIn("Collection purpose: monitor collector runs and snapshots.", html)
|
||||
self.assertIn("Latest run summary loads from `/api/state`.", html)
|
||||
self.assertIn("Purpose: inspect DB-backed JSON evidence and row payloads.", html)
|
||||
self.assertIn("Workspace tables are editable only when the table is in the canonical workspace DB.", html)
|
||||
self.assertIn("DB별 / JSON별 조회 기준", html)
|
||||
self.assertIn("This page is intentionally a triage surface, not a generic table dump.", html)
|
||||
self.assertIn("read-only because this table belongs to collector or strategy storage", html)
|
||||
self.assertIn("Table Browser", html)
|
||||
self.assertIn("Save changes", html)
|
||||
self.assertIn("Clear filters", html)
|
||||
self.assertIn("• current", html)
|
||||
self.assertIn("workbookRegistrySummary", html)
|
||||
self.assertIn("dbRegistryBody", html)
|
||||
self.assertIn("Derived report registry", html)
|
||||
self.assertIn("DB tables", html)
|
||||
self.assertIn("workbookPurposeFilters", html)
|
||||
self.assertIn("Recorded surface", html)
|
||||
self.assertIn("History details", html)
|
||||
self.assertIn("historyChangeBody", html)
|
||||
self.assertIn("historyRunBody", html)
|
||||
self.assertIn("Derived JSON evidence view", html)
|
||||
self.assertIn("Derived JSON Evidence Preview", html)
|
||||
self.assertIn("JSON evidence", html)
|
||||
|
||||
def test_render_tables_html_exposes_workbook_registry_surface(self):
|
||||
html = render_tables_html()
|
||||
self.assertIn("Workbook sheets", html)
|
||||
self.assertIn("XLSX:", html)
|
||||
self.assertIn("JSON evidence:", html)
|
||||
self.assertIn("JSON role:", html)
|
||||
self.assertIn("Unmapped:", html)
|
||||
self.assertIn("Purpose filter:", html)
|
||||
self.assertIn("destination:", html)
|
||||
self.assertIn("recorded surface:", html)
|
||||
self.assertIn("Selected sheet:", html)
|
||||
self.assertIn("surface:", html)
|
||||
self.assertIn("version=", html)
|
||||
self.assertIn("No workbook sheet selected.", html)
|
||||
self.assertIn("tableSelect", html)
|
||||
self.assertIn("DB tables only", html)
|
||||
self.assertIn("Registry details", html)
|
||||
|
||||
def test_workbook_registry_maps_xlsx_sheets_to_recording_surfaces(self):
|
||||
from src.quant_engine.snapshot_admin_server_v1 import load_workbook_sheet_registry
|
||||
|
||||
registry = load_workbook_sheet_registry()
|
||||
self.assertEqual(registry["sheet_count"], 19)
|
||||
entries = {row["sheet"]: row for row in registry["entries"]}
|
||||
self.assertEqual(entries["settings"]["kind"], "workspace_db")
|
||||
self.assertEqual(entries["account_snapshot"]["kind"], "workspace_db")
|
||||
self.assertEqual(entries["data_feed"]["kind"], "collector_db")
|
||||
self.assertEqual(entries["settings"]["purpose"], "workspace_edit")
|
||||
self.assertEqual(entries["data_feed"]["purpose"], "collector_run")
|
||||
self.assertEqual(registry["json_role"], "derived_report_evidence")
|
||||
for sheet in [
|
||||
"sector_universe_refresh_audit",
|
||||
"daily_history",
|
||||
"event_calendar",
|
||||
"pa1_feedback",
|
||||
"alpha_history",
|
||||
"backdata_feature_bank",
|
||||
"sell_priority",
|
||||
"harness_context",
|
||||
"monthly_history",
|
||||
"sector_flow_history",
|
||||
"sector_universe",
|
||||
"core_satellite",
|
||||
"universe",
|
||||
"event_risk",
|
||||
"macro",
|
||||
"sector_flow",
|
||||
]:
|
||||
self.assertEqual(entries[sheet]["kind"], "json_payload")
|
||||
self.assertEqual(entries[sheet]["destination"], "GatherTradingData.json")
|
||||
self.assertEqual(entries[sheet]["source_role"], "derived_report_evidence")
|
||||
self.assertEqual(entries["sector_universe_refresh_audit"]["kind"], "json_payload")
|
||||
self.assertEqual(entries["daily_history"]["kind"], "json_payload")
|
||||
self.assertEqual(entries["sector_universe_refresh_audit"]["purpose"], "refresh_audit")
|
||||
self.assertEqual(entries["daily_history"]["purpose"], "history_ledger")
|
||||
self.assertEqual(entries["event_calendar"]["purpose"], "audit_history")
|
||||
self.assertEqual(entries["alpha_history"]["purpose"], "analysis_report")
|
||||
self.assertEqual(entries["harness_context"]["purpose"], "execution_context")
|
||||
self.assertEqual(entries["monthly_history"]["purpose"], "history_ledger")
|
||||
self.assertEqual(entries["sector_universe"]["purpose"], "universe_registry")
|
||||
self.assertEqual(entries["event_risk"]["purpose"], "macro_risk_context")
|
||||
self.assertEqual(entries["sector_flow"]["purpose"], "flow_leadership")
|
||||
self.assertNotIn("sector_universe_refresh_audit", registry["unmapped_sheets"])
|
||||
self.assertNotIn("daily_history", registry["unmapped_sheets"])
|
||||
|
||||
def test_render_collection_html_contains_dashboard_surface(self):
|
||||
html = render_collection_html()
|
||||
self.assertIn("KIS Collection Dashboard", html)
|
||||
self.assertIn("/api/state", html)
|
||||
self.assertIn("/api/collection/run", html)
|
||||
self.assertIn("Collect now", html)
|
||||
self.assertIn("collectionModeInput", html)
|
||||
self.assertIn("collectionAccountInput", html)
|
||||
self.assertIn("collectionRunLog", html)
|
||||
self.assertIn("collectionRunBanner", html)
|
||||
self.assertIn("collectionModeBadge", html)
|
||||
self.assertIn("collectionLiveStatus", html)
|
||||
self.assertIn("collectionProgressChip", html)
|
||||
self.assertIn("collectionStageChip", html)
|
||||
self.assertIn("collectionResultChip", html)
|
||||
self.assertIn("collectionTrendSummary", html)
|
||||
self.assertIn("collectionTrendChart", html)
|
||||
self.assertIn("Auto refreshes every 15 seconds", html)
|
||||
self.assertIn("older → newer", html)
|
||||
self.assertIn("Snapshots / run", html)
|
||||
self.assertIn("Errors / run", html)
|
||||
self.assertIn("[RUN]", html)
|
||||
self.assertIn("[SNAPSHOT]", html)
|
||||
self.assertIn("[ERROR]", html)
|
||||
self.assertIn("live source: active | KIS rows=", html)
|
||||
self.assertIn("collectionDetailAnchor", html)
|
||||
self.assertIn("collectionRunLogAnchor", html)
|
||||
self.assertIn("Live KIS on", html)
|
||||
self.assertIn("live source: unknown", html)
|
||||
self.assertNotIn('value="mock"', html)
|
||||
self.assertIn("Download raw JSON", html)
|
||||
self.assertIn("Download CSV", html)
|
||||
self.assertIn("Filter runs / snapshots / errors", html)
|
||||
self.assertIn("Unified activity timeline", html)
|
||||
self.assertIn("date", html)
|
||||
self.assertIn("Ticker quick search", html)
|
||||
self.assertIn("Date quick search", html)
|
||||
|
||||
def test_run_collection_job_returns_progress_payload(self):
|
||||
import tempfile
|
||||
import shutil
|
||||
from src.quant_engine.snapshot_admin_server_v1 import KIS_COLLECTION_DB, KIS_COLLECTION_REPORT, run_collection_job
|
||||
|
||||
tmp_dir = tempfile.mkdtemp()
|
||||
try:
|
||||
sqlite_db = Path(tmp_dir) / "kis_data_collection.db"
|
||||
output_json = Path(tmp_dir) / "kis_data_collection_v1.json"
|
||||
output_json.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"generated_at": "2026-06-24T10:00:00+09:00",
|
||||
"row_count": 1,
|
||||
"source_counts": {"kis_open_api": 1},
|
||||
},
|
||||
ensure_ascii=False,
|
||||
indent=2,
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
fake_proc = Mock(returncode=0, stdout="ok", stderr="")
|
||||
with patch("src.quant_engine.snapshot_admin_server_v1.subprocess.run", return_value=fake_proc) as run_mock:
|
||||
payload = run_collection_job(
|
||||
sqlite_db=sqlite_db,
|
||||
input_json=Path(tmp_dir) / "GatherTradingData.json",
|
||||
output_json=output_json,
|
||||
kis_account="real",
|
||||
include_live_kis=True,
|
||||
allow_naver_fallback=False,
|
||||
)
|
||||
|
||||
self.assertEqual(payload["status"], "PASS")
|
||||
self.assertEqual(payload["returncode"], 0)
|
||||
self.assertEqual(payload["stdout"], "ok")
|
||||
self.assertIn("started_at", payload)
|
||||
self.assertIn("finished_at", payload)
|
||||
self.assertIn("elapsed_ms", payload)
|
||||
self.assertEqual(payload["summary"]["row_count"], 1)
|
||||
self.assertEqual(payload["summary"]["source_counts"]["kis_open_api"], 1)
|
||||
self.assertEqual(payload["state"]["db_path"], str(sqlite_db))
|
||||
run_mock.assert_called_once()
|
||||
self.assertTrue(str(KIS_COLLECTION_DB).endswith("kis_data_collection.db"))
|
||||
self.assertTrue(str(KIS_COLLECTION_REPORT).endswith("kis_data_collection_v1.json"))
|
||||
finally:
|
||||
shutil.rmtree(tmp_dir, ignore_errors=True)
|
||||
|
||||
def test_build_ui_state_exposes_expected_columns(self):
|
||||
import tempfile
|
||||
import shutil
|
||||
|
||||
Reference in New Issue
Block a user