| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187 |
- """cn_holiday sync / read unit tests"""
- from datetime import date, datetime
- from unittest.mock import MagicMock, patch
- from app.services.cn_holiday import is_rest_day, list_day_flags
- from app.services.cn_holiday_sync import (
- _is_rest_from_type,
- is_year_calendar_complete,
- map_ailcc_day_row,
- seconds_until_next_local_midnight,
- sync_cn_holidays,
- )
- def test_type_rest_flags():
- assert _is_rest_from_type(1) == 1
- assert _is_rest_from_type(2) == 1
- assert _is_rest_from_type(3) == 1
- assert _is_rest_from_type(0) == 0
- assert _is_rest_from_type(4) == 0
- def test_map_row():
- row = map_ailcc_day_row(
- "2026-10-01",
- holiday={"holiday": True, "name": "国庆节(休)", "date": "2026-10-01"},
- type_info={"type": 2, "name": "国庆节(休)", "week": 4},
- )
- assert row["day_date"] == date(2026, 10, 1)
- assert row["day_type"] == 2
- assert row["is_rest_day"] == 1
- assert row["day_name"] == "国庆节(休)"
- assert row["week"] == 4
- assert row["raw_json"]
- def test_map_row_workday_makeup():
- row = map_ailcc_day_row(
- "2026-10-10",
- holiday=None,
- type_info={"type": 4, "name": "国庆节(班)", "week": 6},
- )
- assert row["day_type"] == 4
- assert row["is_rest_day"] == 0
- def test_map_row_infer_weekend_when_type_missing():
- # 2026-10-03 is Saturday
- row = map_ailcc_day_row("2026-10-03", holiday=None, type_info=None)
- assert row["day_type"] == 1
- assert row["is_rest_day"] == 1
- assert row["week"] == 6
- def test_is_rest_day_from_row():
- db = MagicMock()
- row = MagicMock()
- row.is_rest_day = 1
- row.day_type = 2
- db.query.return_value.filter.return_value.first.return_value = row
- assert is_rest_day(db, date(2026, 10, 1)) is True
- def test_is_rest_day_work_type():
- db = MagicMock()
- row = MagicMock()
- row.is_rest_day = 0
- row.day_type = 4
- db.query.return_value.filter.return_value.first.return_value = row
- assert is_rest_day(db, date(2026, 10, 10)) is False
- def test_is_rest_day_fallback_weekend():
- db = MagicMock()
- db.query.return_value.filter.return_value.first.return_value = None
- # Saturday
- assert is_rest_day(db, date(2026, 8, 8)) is True
- # Friday
- assert is_rest_day(db, date(2026, 8, 7)) is False
- def test_list_day_flags_fills_gaps():
- db = MagicMock()
- row = MagicMock()
- row.day_date = date(2026, 10, 1)
- row.day_type = 2
- row.is_rest_day = 1
- row.day_name = "国庆节(休)"
- db.query.return_value.filter.return_value.all.return_value = [row]
- flags = list_day_flags(db, date(2026, 10, 1), date(2026, 10, 3))
- assert len(flags) == 3
- assert flags[0] == {
- "dayDate": "2026-10-01",
- "dayType": 2,
- "isRestDay": True,
- "dayName": "国庆节(休)",
- }
- # 2026-10-02 Friday → work fallback
- assert flags[1]["dayDate"] == "2026-10-02"
- assert flags[1]["dayType"] == 0
- assert flags[1]["isRestDay"] is False
- # 2026-10-03 Saturday → rest fallback
- assert flags[2]["dayDate"] == "2026-10-03"
- assert flags[2]["dayType"] == 1
- assert flags[2]["isRestDay"] is True
- def test_seconds_until_next_local_midnight():
- now = datetime(2026, 8, 10, 23, 59, 0)
- assert seconds_until_next_local_midnight(now) == 60
- noon = datetime(2026, 8, 10, 12, 0, 0)
- assert seconds_until_next_local_midnight(noon) == 12 * 3600
- def test_is_year_calendar_complete_true():
- session = MagicMock()
- # 2026 is not leap → 365
- session.query.return_value.filter.return_value.count.return_value = 365
- assert is_year_calendar_complete(session, 2026) is True
- def test_is_year_calendar_complete_false():
- session = MagicMock()
- session.query.return_value.filter.return_value.count.return_value = 100
- assert is_year_calendar_complete(session, 2026) is False
- def test_is_year_calendar_complete_leap():
- session = MagicMock()
- session.query.return_value.filter.return_value.count.return_value = 366
- assert is_year_calendar_complete(session, 2024) is True
- session.query.return_value.filter.return_value.count.return_value = 365
- assert is_year_calendar_complete(session, 2024) is False
- def test_sync_cn_holidays_upsert_mocked():
- holiday_payload = {
- "code": 0,
- "holiday": {
- "2026-10-01": {"holiday": True, "name": "国庆节(休)", "date": "2026-10-01"},
- },
- "type": {
- "2026-10-01": {"type": 2, "name": "国庆节(休)", "week": 4},
- "2026-10-02": {"type": 2, "name": "国庆节(休)", "week": 5},
- },
- }
- session = MagicMock()
- session.query.return_value.filter.return_value.first.return_value = None
- # year incomplete → still fetch
- session.query.return_value.filter.return_value.count.return_value = 0
- class Factory:
- def __call__(self):
- return session
- with patch("app.services.cn_holiday_sync._fetch_batch", return_value=holiday_payload), patch(
- "app.services.cn_holiday_sync._years_to_sync", return_value=[2026]
- ), patch(
- "app.services.cn_holiday_sync._months_for_year", return_value=[(2026, 10)]
- ):
- n = sync_cn_holidays(Factory())
- assert n >= 1
- assert session.add.call_count >= 1 or session.merge.call_count >= 1
- session.commit.assert_called()
- session.close.assert_called()
- def test_sync_skips_complete_year():
- session = MagicMock()
- session.query.return_value.filter.return_value.count.return_value = 365
- class Factory:
- def __call__(self):
- return session
- with patch("app.services.cn_holiday_sync._fetch_batch") as fetch, patch(
- "app.services.cn_holiday_sync._years_to_sync", return_value=[2026]
- ):
- n = sync_cn_holidays(Factory())
- assert n == 0
- fetch.assert_not_called()
- session.commit.assert_called()
- session.close.assert_called()
|