""" 成交跟进:工作日顺延、线路对负责人、未成交收下原因(不打外网)。 """ from __future__ import annotations import unittest from datetime import date, datetime from zoneinfo import ZoneInfo from agent.policy.deal_followup import ( SHANGHAI, air_route_keys, is_workday, leader_matches, next_followup_at, shift_workdays, workday_elapsed_hours, ) class TestWorkdays(unittest.TestCase): def test_skip_weekend(self) -> None: # 普通周末仍跳过。2026-11-13 周五 → 16、17、18 三个工作日,第四个是 19 日。 self.assertEqual(shift_workdays(date(2026, 11, 13), 3), date(2026, 11, 18)) self.assertEqual(shift_workdays(date(2026, 11, 13), 4), date(2026, 11, 19)) def test_sept20_makeup_pulls_followup_forward(self) -> None: # 2026-09-18 周五。9 月 20 日周日调休上班,第四个工作日是 23 日,不是 24 日。 self.assertEqual(shift_workdays(date(2026, 9, 18), 4), date(2026, 9, 23)) def test_skip_national_day(self) -> None: # 2026-09-30 周三。10 月 1–7 日放假,8 日上班,10 日周六调休。 # 节后第 1 个工作日 8 日,第 4 个是 12 日周一。 fourth = shift_workdays(date(2026, 9, 30), 4) self.assertEqual(fourth, date(2026, 10, 12)) def test_oct8_works_and_sept20_is_makeup(self) -> None: # 国办发明电〔2025〕7号:10 月 8 日正常上班,9 月 20 日周日调休上班。 self.assertTrue(is_workday(date(2026, 10, 8))) self.assertTrue(is_workday(date(2026, 9, 20))) self.assertFalse(is_workday(date(2026, 10, 7))) def test_next_followup_is_9am(self) -> None: when = next_followup_at(datetime(2026, 9, 18, 15, 30, tzinfo=SHANGHAI)) self.assertEqual(when.hour, 9) self.assertEqual(when.minute, 0) self.assertEqual(when.tzinfo, SHANGHAI) self.assertEqual(when.date(), date(2026, 9, 23)) def test_skip_weekend_keep_weekday_night(self) -> None: start = datetime(2026, 11, 13, 17, 0, tzinfo=SHANGHAI) # 周五 now = datetime(2026, 11, 16, 17, 0, tzinfo=SHANGHAI) # 周一 # 周五 17–24 = 7h,周末不算,周一 0–17 = 17h self.assertAlmostEqual(workday_elapsed_hours(start, now), 24.0, places=4) def test_rest_day_start_counts_from_next_workday(self) -> None: start = datetime(2026, 11, 14, 10, 0, tzinfo=SHANGHAI) # 周六 now = datetime(2026, 11, 16, 10, 0, tzinfo=SHANGHAI) # 周一 self.assertAlmostEqual(workday_elapsed_hours(start, now), 10.0, places=4) def test_fractional_quarter_hour(self) -> None: start = datetime(2026, 9, 18, 9, 0, tzinfo=SHANGHAI) now = datetime(2026, 9, 18, 9, 15, tzinfo=SHANGHAI) self.assertAlmostEqual(workday_elapsed_hours(start, now), 0.25, places=4) class TestAirRouteKeys(unittest.TestCase): def test_pair_and_dest(self) -> None: keys = air_route_keys({"起运港": "SZX", "目的港": "KUL"}) self.assertIn("SZX-KUL", keys) self.assertIn("KUL", keys) def test_chinese_city_projects_iata(self) -> None: keys = air_route_keys({"起运港": "深圳", "目的港": "吉隆坡"}) self.assertIn("SZX-KUL", keys) self.assertIn("KUL", keys) class TestLeaderMatch(unittest.TestCase): def test_sea_all_line(self) -> None: self.assertTrue( leader_matches( role_code="sea", routes="海运全线", is_leader=True, business_line="SEA", facts={}, ) ) self.assertFalse( leader_matches( role_code="sea", routes="海运全线", is_leader=False, business_line="SEA", facts={}, ) ) def test_land_route_category(self) -> None: self.assertTrue( leader_matches( role_code="land", routes="国内长途/零担,东南亚", is_leader=True, business_line="LAND", facts={"线路类别": "国内长途/零担"}, ) ) self.assertFalse( leader_matches( role_code="land", routes="东南亚", is_leader=True, business_line="LAND", facts={"线路类别": "国内长途/零担"}, ) ) def test_air_pair_dest_and_all(self) -> None: facts = {"起运港": "SZX", "目的港": "KUL"} self.assertTrue( leader_matches( role_code="air", routes="SZX-KUL", is_leader=True, business_line="AIR", facts=facts, ) ) self.assertTrue( leader_matches( role_code="air", routes="KUL", is_leader=True, business_line="AIR", facts=facts, ) ) self.assertTrue( leader_matches( role_code="air", routes="全线", is_leader=True, business_line="AIR", facts=facts, ) ) self.assertFalse( leader_matches( role_code="air", routes="外采-空运", is_leader=True, business_line="AIR", facts=facts, ) ) self.assertTrue( leader_matches( role_code="air", routes="外采-空运", is_leader=True, business_line="AIR", facts={**facts, "外采": "1"}, ) ) if __name__ == "__main__": unittest.main()