from datetime import datetime, timedelta from pathlib import Path from sqlalchemy import create_engine from sqlalchemy.orm import Session from app.models import ( BlumTradingPowerScore, DashboardSnapshot, LearningBenchmarkComparison, LearningRun, LearningStrengthWeaknessMap, TradingCapitalCycle, TradingGame, TradingGameTrade, TradingIntelligenceMetric, TradeLearningEvidence, ) from app.services.dashboard_snapshots import DashboardSnapshotService from app.services.learning_summary import LearningSummaryService, build_truth_panel, safe_progress, summarize_benchmarks def test_safe_progress_handles_partial_data(): assert safe_progress(None, 10000) is None assert safe_progress(50, 100) == 0.5 assert safe_progress(200, 100) == 1 def test_summarize_benchmarks_returns_partial_status(): payload = summarize_benchmarks([]) assert payload["status"] == "initializing" assert payload["major_benchmarks"]["SPY"]["result_label"] == "insufficient_sample" def test_truth_panel_is_honest_when_power_missing(): rows = build_truth_panel(None, summarize_benchmarks([]), ["No benchmark summary."], []) assert rows[0].startswith("Not enough precomputed evidence") assert any("No benchmark summary" in row for row in rows) def test_learning_summary_returns_partial_payload_without_precomputed_rows(): engine = create_engine("sqlite:///:memory:", future=True) for model in [ BlumTradingPowerScore, DashboardSnapshot, LearningBenchmarkComparison, LearningRun, LearningStrengthWeaknessMap, TradingCapitalCycle, TradingGame, TradingGameTrade, TradingIntelligenceMetric, TradeLearningEvidence, ]: model.__table__.create(bind=engine) with Session(engine) as db: payload = LearningSummaryService().summary(db) assert payload["status"] == "initializing" assert "trading_power_score" in payload["missing_sections"] assert "trading_intelligence_metrics" in payload["missing_sections"] assert payload["backend_training_status"]["frontend_policy"] == "read_only_snapshot_observer" assert "snapshots" in payload assert payload["truth_panel"][0].startswith("Not enough precomputed evidence") def test_dashboard_snapshot_round_trip_and_stale_flag(): engine = create_engine("sqlite:///:memory:", future=True) DashboardSnapshot.__table__.create(bind=engine) service = DashboardSnapshotService() with Session(engine) as db: written = service.write(db, "learning_summary", {"status": "ready"}, ttl_seconds=1) assert written["status"] == "ready" latest = service.latest(db, "learning_summary") assert latest["payload"]["status"] == "ready" row = db.get(DashboardSnapshot, 1) row.expires_at = datetime.utcnow() - timedelta(seconds=1) db.commit() stale = service.latest(db, "learning_summary") assert stale["status"] == "stale" def test_learning_page_keeps_heavy_work_out_of_initial_render(): page = Path(__file__).resolve().parents[2] / "frontend" / "app" / "learning" / "page.tsx" text = page.read_text() assert text.strip() == 'export { default } from "../training-ground/page";' training_page = Path(__file__).resolve().parents[2] / "frontend" / "app" / "training-ground" / "page.tsx" training_text = training_page.read_text() initial_effect = training_text.split("useEffect(() => {", 1)[1].split("}, []);", 1)[0] assert "api.traderTrainingGround()" in initial_effect assert initial_effect.count("api.") == 1 assert "api.tradingGameLedger" not in training_text assert "api.alphaRecoveryDashboard()" not in training_text assert "api.metaCognitionSummary()" not in training_text assert "recalculateLearningTradingPower" not in text assert "recalculateDecisionSuperiority" not in text assert "recalculateBusinessQuality" not in text assert "recalculatePortfolioIntelligence" not in text assert "recalculateAlphaRecovery" not in text assert "metaCognitionRecalculate" not in text def test_deep_diagnostics_uses_human_readable_renderer_not_raw_json_dump(): page = Path(__file__).resolve().parents[2] / "frontend" / "components" / "DiagnosticPanelRenderer.tsx" text = page.read_text() assert "compactPreview" not in text assert "JSON.stringify(compactPreview" not in text assert "DiagnosticPanelRenderer" in text assert "Show raw JSON" in text def test_diagnostic_panel_renderer_hides_raw_json_by_default(): renderer = Path(__file__).resolve().parents[2] / "frontend" / "components" / "DiagnosticPanelRenderer.tsx" text = renderer.read_text() assert "export function DiagnosticPanelRenderer" in text assert "function ReliabilityByRegimeRenderer" in text assert "function EnsembleStatusRenderer" in text assert "export function SampleSizeWarning" in text assert "export function WeightDistributionTable" in text assert "Show raw JSON" in text assert "Hide raw JSON" in text assert "open &&