Files
set50-system/backend/tests/test_dashboard.py
Kunthawat Greethong 12b34929d7 feat(factor): add energy_irpc (IRPC net margin) as 2nd Thai refiner signal
- new energy_irpc collector parsing IRPC performance-highlights table
  (net profit/EBITDA/ROE margins, latest period 3M26: +10.27%)
- factor energy_irpc_net_margin (sign +1) wired into refining_energy/
  exploration/utilities, extending the energy theme beyond TOP
- scheduler job + dashboard fetch + sources table row (now 9 sources)
- tests: parse (incl paren-negatives), value-key resolution, direction;
  suite 368 OK. Independent review passed: true
- Phase B feasibility: REIC/EPPO/NBTC/PTTEP are JS-rendered or anti-bot
  (recorded deferred in plan); IRPC was the clean server-rendered win
2026-08-29 11:13:17 +07:00

73 lines
2.9 KiB
Python

"""Tests for the real multi-theme dashboard assembly."""
from __future__ import annotations
import unittest
from unittest.mock import patch
from app.dashboard import RealDashboard, _auto_read, _zscore
class _FakeCache:
"""Minimal cache that invokes the fetcher each call."""
def __init__(self, data: dict):
self.data = data
def fetch_or_stale(self, key, fetcher):
val = self.data.get(key)
if val is not None:
return val
return fetcher()
class DashboardTest(unittest.TestCase):
def test_zscore_centers(self):
self.assertAlmostEqual(_zscore(1.0, 1.0, 1.0), 0.0)
self.assertAlmostEqual(_zscore(2.0, 1.0, 1.0), 1.0)
def test_auto_read_multisource(self):
auto = {"new_car_sales_yoy": 20.07, "total_vehicle_sales": 59000,
"vehicle_production": 120000, "auto_exports": 80000}
npl = {"pct_of_npls": 3.95, "npl_amount": 20602}
read = _auto_read(auto, npl, _FakeCache({}))
self.assertEqual(read["new_car_sales_yoy"], 20.07)
self.assertEqual(read["auto_npl_pct"], 3.95)
self.assertIn("thesis", read)
@patch("app.auto_credit.fetch_auto_credit")
@patch("app.auto_npl.fetch_auto_npl")
@patch("app.energy_thai.fetch_energy_thai")
@patch("app.energy_irpc.fetch_energy_irpc")
@patch("app.macro_thai.fetch_macro_thai")
@patch("app.bank_npl.fetch_bank_npl")
def test_build_returns_structure(self, bnpl, macro, energy, eirpc, npl, auto):
class _Factory:
def __init__(self, data): self._data = data
def to_dict(self): return self._data
macro.return_value = _Factory({
"private_consumption_yoy": 4.9, "headline_inflation_yoy": 1.95, "periods": {}})
energy.return_value = _Factory({
"quarterly": {"Q2/2026": {"net_profit": 8000.0, "ebitda": 9000.0}}})
eirpc.return_value = _Factory({"net_margin_pct": 10.27, "period": "3M26"})
auto.return_value = _Factory({
"new_car_sales_yoy": 20.07, "total_vehicle_sales": 59000})
npl.return_value = _Factory({
"pct_of_npls": 3.95, "npl_amount": 20602, "period": "Q2/2568"})
bnpl.return_value = _Factory({
"pct_of_npls": 1.07, "npl_amount": 5598, "period": ""})
cache = _FakeCache({})
dash = RealDashboard([], cache).build()
self.assertEqual(len(dash["themes"]), 13) # all SET50 themes
self.assertEqual(len(dash["sources"]), 9) # auto-derived from FACTORS (energy_irpc added)
self.assertIn("macro", dash)
self.assertIn("board", dash)
def test_auto_read_npl_piece(self):
read = _auto_read({"new_car_sales_yoy": -3.0, "total_vehicle_sales": 20000},
{"pct_of_npls": 6.0, "npl_amount": 90000}, _FakeCache({}))
self.assertEqual(read["new_car_sales_yoy"], -3.0)
self.assertEqual(read["auto_npl_pct"], 6.0)
if __name__ == "__main__":
unittest.main()