test_festival_detection.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. """节日检测流程集成测试(含代码兜底)。"""
  2. from __future__ import annotations
  3. import unittest
  4. from datetime import date
  5. from unittest.mock import patch
  6. from app.festival_demand.demand_generation import generate_demands_from_matches
  7. from app.festival_demand.detection import detect_active_festivals
  8. from app.festival_demand.exceptions import FestivalDemandError
  9. from app.festival_demand.fixed_dates import merge_festival_detections
  10. from app.festival_demand.types import FestivalDemandConfig
  11. def _test_config() -> FestivalDemandConfig:
  12. return FestivalDemandConfig(
  13. cron_hours="7",
  14. cron_minute=0,
  15. llm_model="test-model",
  16. llm_max_attempts=1,
  17. llm_retry_sleep_seconds=0.0,
  18. llm_max_tokens=100,
  19. llm_temperature=0.0,
  20. demand_pool_source_table="dwd_multi_demand_pool_di",
  21. demand_pool_strategy="去年同期阳历",
  22. output_strategy="去年同期阳历-节点事件",
  23. match_batch_size=200,
  24. )
  25. class FestivalDetectionIntegrationTest(unittest.TestCase):
  26. def test_code_fallback_when_llm_returns_empty(self) -> None:
  27. llm_result = {
  28. "date": "2026-08-03",
  29. "festivals": [],
  30. "festival_names": [],
  31. }
  32. with patch(
  33. "app.festival_demand.detection._detect_active_festivals_via_llm",
  34. return_value=llm_result,
  35. ):
  36. result = detect_active_festivals(date(2026, 8, 3), _test_config())
  37. self.assertIn("立秋", result["festival_names"])
  38. liqiu = next(item for item in result["festivals"] if item["name"] == "立秋")
  39. self.assertEqual(liqiu["detection_source"], "code")
  40. self.assertEqual(liqiu["event_type"], "节日/节气")
  41. self.assertEqual(liqiu["phase"], "prewarm")
  42. def test_code_fallback_when_llm_fails(self) -> None:
  43. with patch(
  44. "app.festival_demand.detection._detect_active_festivals_via_llm",
  45. side_effect=FestivalDemandError("llm unavailable"),
  46. ):
  47. result = detect_active_festivals(date(2026, 8, 3), _test_config())
  48. self.assertIn("立秋", result["festival_names"])
  49. def test_code_overrides_llm_for_same_event(self) -> None:
  50. llm_festivals = [
  51. {
  52. "name": "立秋",
  53. "kind": "non_statutory",
  54. "event_date": "2026-08-08",
  55. "festival_start": "2026-08-08",
  56. "festival_end": "2026-08-08",
  57. "phase": "prewarm",
  58. "days_to_festival": 5,
  59. "event_type": "节日/节气",
  60. "detection_source": "llm",
  61. }
  62. ]
  63. code_festivals = [
  64. {
  65. "name": "立秋",
  66. "kind": "non_statutory",
  67. "event_date": "2026-08-07",
  68. "festival_start": "2026-08-07",
  69. "festival_end": "2026-08-07",
  70. "phase": "prewarm",
  71. "days_to_festival": 4,
  72. "event_type": "节日/节气",
  73. "detection_source": "code",
  74. }
  75. ]
  76. merged = merge_festival_detections(llm_festivals, code_festivals)
  77. liqiu = next(item for item in merged if item["name"] == "立秋")
  78. self.assertEqual(liqiu["detection_source"], "code")
  79. self.assertEqual(liqiu["event_date"], "2026-08-07")
  80. def test_downstream_demand_generation_accepts_code_festival(self) -> None:
  81. matches = [
  82. {
  83. "demand_name": "贴秋膘",
  84. "festival_name": "立秋",
  85. "event_type": "节日/节气",
  86. }
  87. ]
  88. generated = generate_demands_from_matches(matches, query_date=date(2026, 8, 3))
  89. self.assertIn("立秋", generated["generated_demand_names"])
  90. self.assertIn("2026 立秋", generated["generated_demand_names"])
  91. self.assertIn("2026 立秋 贴秋膘", generated["generated_demand_names"])
  92. if __name__ == "__main__":
  93. unittest.main()