"""Tests for the Thailand rates/credit/retail/confidence collector + themes.""" from __future__ import annotations import unittest from app import te_thailand, themes from app.te_thailand import ThaiFactorsSnapshot, parse_te_thailand_html def _row(label, value, prev, unit, period): return (f"{label}{value}{prev}" f"{unit}{period}") def _table(*rows): return f"{''.join(rows)}
" _IR_HTML = _table( _row("Interest Rate", "1.00", "1.00", "percent", "Aug 2026"), _row("Loans to Non Financial Corporations", "10553097.00", "10488479.00", "THB Million", "Jun 2026"), _row("Banks Balance Sheet", "40475793.00", "40559803.00", "THB Million", "Jun 2026"), ) _CC_HTML = _table( _row("Consumer Confidence", "51.80", "50.70", "points", "Jul 2026"), _row("Retail Sales YoY", "-14.50", "-20.00", "percent", "May 2026"), _row("Consumer Credit", "5285734.00", "5296795.00", "THB Million", "Jun 2025"), _row("Households Debt to GDP", "87.50", "87.30", "percent of GDP", "Dec 2025"), _row("Consumer Spending", "1771591.00", "1730973.00", "THB Million", "Jun 2026"), ) _HOUSING_HTML = _table( _row("Housing Index", "162.70", "162.40", "points", "Jun 2026"), _row("Residential Property Prices", "1.26", "0.63", "Percent", "Mar 2026"), _row("Housing Starts", "4093.00", "6520.00", "units", "Apr 2026"), ) _BIZCONF_HTML = _table( _row("Business Confidence", "46.70", "46.10", "points", "Jul 2026"), _row("Leading Economic Index", "166.19", "163.56", "points", "Jun 2026"), ) class TeThailandParseTest(unittest.TestCase): def test_parses_all_indicators(self): snap = parse_te_thailand_html(_IR_HTML, _CC_HTML, _HOUSING_HTML, _BIZCONF_HTML) self.assertIsInstance(snap, ThaiFactorsSnapshot) self.assertEqual(snap.interest_rate_pct, 1.0) self.assertEqual(snap.loans_to_fin_corp, 10553097.0) self.assertEqual(snap.consumer_credit_thbmn, 5285734.0) self.assertEqual(snap.household_debt_gdp_pct, 87.5) self.assertEqual(snap.retail_sales_yoy, -14.5) self.assertEqual(snap.consumer_confidence, 51.8) self.assertEqual(snap.consumer_spending, 1771591.0) self.assertEqual(snap.property_prices_yoy, 1.26) self.assertEqual(snap.business_confidence, 46.7) self.assertEqual(snap.periods["interest_rate_pct"], "Aug 2026") self.assertEqual(snap.periods["property_prices_yoy"], "Mar 2026") def test_to_dict_full(self): d = parse_te_thailand_html(_IR_HTML, _CC_HTML, _HOUSING_HTML, _BIZCONF_HTML).to_dict() self.assertIn("interest_rate_pct", d) self.assertIn("retail_sales_yoy", d) self.assertIn("property_prices_yoy", d) self.assertIn("business_confidence", d) self.assertIn("source", d) def test_missing_values_raise(self): # neither page yields a usable series -> collector fails loudly empty = "
x1
" with self.assertRaises(te_thailand.ThaiFactorsError): parse_te_thailand_html(empty, empty) def test_every_factor_value_key_resolves(self): from app import factors keys = set(ThaiFactorsSnapshot().to_dict().keys()) for fkey, fact in factors.FACTORS.items(): if fact.get("fetch") != "te_thailand": continue self.assertIn( fact.get("value_key"), keys, f"factor {fkey!r} value_key not emitted by te_thailand", ) class TeThailandThemeDirectionTest(unittest.TestCase): """New factors must move theme surprises the intended direction (sign fix).""" @staticmethod def _fetched(): base = { "macro_thai": { "private_consumption_yoy": 4.9, "private_investment_yoy": 18.1, "headline_inflation_yoy": 1.95, "core_inflation_yoy": 1.0, "unemployment_pct": 1.0, "manufacturing_yoy": -3.1, "tourists_ytd_mn": 16.2, }, "auto_credit": {"new_car_sales_yoy": 20.07, "vehicle_production": 117383.0, "auto_exports": 81526.0}, "auto_npl": {"pct_of_npls": 3.0}, "bank_npl": {"pct_of_npls": 1.0}, "energy_thai": {"quarterly": {"Q1/2026": {"net_profit": 19481.0, "sales": 114809.0}}}, "thai_trade": {"current_account_usdm": 500.0, "exports_usdm": 34000.0, "imports_usdm": 38000.0}, # te_thailand factors — neutral-ish values "te_thailand": { "interest_rate_pct": 1.5, "loans_to_fin_corp": 10000000.0, "consumer_credit_thbmn": 5000000.0, "household_debt_gdp_pct": 85.0, "retail_sales_yoy": 0.0, "consumer_confidence": 50.0, "property_prices_yoy": 0.0, "business_confidence": 50.0, }, } return base def _surprises(self, te): d = self._fetched() d["te_thailand"] = te return themes.compute_theme_surprises(d) def test_higher_retail_sales_raises_retail(self): low = self._surprises({**self._fetched()["te_thailand"], "retail_sales_yoy": -15.0}) high = self._surprises({**self._fetched()["te_thailand"], "retail_sales_yoy": 8.0}) self.assertGreater(high["retail"], low["retail"]) def test_higher_rate_and_loans_raise_banks(self): low = self._surprises({**self._fetched()["te_thailand"], "interest_rate_pct": 0.5, "loans_to_fin_corp": 8500000.0}) high = self._surprises({**self._fetched()["te_thailand"], "interest_rate_pct": 2.5, "loans_to_fin_corp": 12000000.0}) self.assertGreater(high["banks"], low["banks"]) def test_higher_household_debt_lowers_nonbank(self): low = self._surprises({**self._fetched()["te_thailand"], "household_debt_gdp_pct": 80.0}) high = self._surprises({**self._fetched()["te_thailand"], "household_debt_gdp_pct": 92.0}) self.assertLess(high["nonbank_finance"], low["nonbank_finance"]) def test_higher_consumer_confidence_raises_retail(self): low = self._surprises({**self._fetched()["te_thailand"], "consumer_confidence": 42.0}) high = self._surprises({**self._fetched()["te_thailand"], "consumer_confidence": 60.0}) self.assertGreater(high["retail"], low["retail"]) def test_higher_property_prices_raise_property(self): low = self._surprises({**self._fetched()["te_thailand"], "property_prices_yoy": -4.0}) high = self._surprises({**self._fetched()["te_thailand"], "property_prices_yoy": 4.0}) self.assertGreater(high["property"], low["property"]) def test_higher_business_confidence_raises_telecom(self): low = self._surprises({**self._fetched()["te_thailand"], "business_confidence": 42.0}) high = self._surprises({**self._fetched()["te_thailand"], "business_confidence": 58.0}) self.assertGreater(high["telecom_it"], low["telecom_it"]) if __name__ == "__main__": unittest.main()