66 lines
2.7 KiB
Python
66 lines
2.7 KiB
Python
import unittest
|
|
|
|
from app.tourism import compute_tourism_signal
|
|
|
|
|
|
class TourismSignalTests(unittest.TestCase):
|
|
def test_theme_surprise_is_mean_of_standardized_observations(self):
|
|
snapshot = {
|
|
"as_of": "2026-08-21",
|
|
"source": {
|
|
"source_id": "fixture.tourism",
|
|
"source_url": "https://example.invalid/tourism",
|
|
"published_at": "2026-08-21T08:00:00Z",
|
|
"retrieved_at": "2026-08-21T08:05:00Z",
|
|
"vintage_id": "fixture-1",
|
|
},
|
|
"observations": [
|
|
{"metric_key": "arrivals_yoy", "value": 12, "expected": 8, "scale": 2, "unit": "percent"},
|
|
{"metric_key": "airport_passengers_yoy", "value": 8, "expected": 6, "scale": 2, "unit": "percent"},
|
|
],
|
|
"exposures": [],
|
|
}
|
|
result = compute_tourism_signal(snapshot)
|
|
self.assertAlmostEqual(result["theme_surprise"], 1.5)
|
|
self.assertEqual(result["data_quality"], "fixture")
|
|
|
|
def test_exposure_score_and_rank_are_deterministic(self):
|
|
snapshot = {
|
|
"as_of": "2026-08-21",
|
|
"source": {
|
|
"source_id": "fixture.tourism",
|
|
"source_url": "https://example.invalid/tourism",
|
|
"published_at": "2026-08-21T08:00:00Z",
|
|
"retrieved_at": "2026-08-21T08:05:00Z",
|
|
"vintage_id": "fixture-1",
|
|
},
|
|
"observations": [
|
|
{"metric_key": "arrivals_yoy", "value": 12, "expected": 8, "scale": 2, "unit": "percent"},
|
|
],
|
|
"exposures": [
|
|
{"symbol": "AOT", "coefficient": 1.0, "confidence": 0.95, "evidence": "airport"},
|
|
{"symbol": "MINT", "coefficient": 0.6, "confidence": 0.80, "evidence": "hotel"},
|
|
{"symbol": "PTT", "coefficient": -0.2, "confidence": 0.60, "evidence": "control"},
|
|
],
|
|
}
|
|
result = compute_tourism_signal(snapshot)
|
|
self.assertEqual([item["symbol"] for item in result["signals"]], ["AOT", "MINT", "PTT"])
|
|
self.assertEqual(result["signals"][0]["side"], "LONG")
|
|
self.assertEqual(result["signals"][-1]["side"], "SHORT")
|
|
self.assertGreater(result["signals"][0]["score"], result["signals"][1]["score"])
|
|
self.assertEqual(result["signals"][0]["reason_codes"], ["TOURISM_SURPRISE", "AIRPORT"])
|
|
|
|
def test_missing_scale_is_rejected(self):
|
|
snapshot = {
|
|
"as_of": "2026-08-21",
|
|
"source": {},
|
|
"observations": [{"metric_key": "arrivals_yoy", "value": 10, "expected": 8}],
|
|
"exposures": [],
|
|
}
|
|
with self.assertRaises(ValueError):
|
|
compute_tourism_signal(snapshot)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|