"""Unit tests for the pure month-end book-close calendar engine. These lock the engine to the PingCAP finance team's live close checklist: the June 2026 close (target WD5 = Mon Jul 6, with Sat Jul 4 worked as WD4) and the rolled-forward July 2026 close (target WD5 = Thu Aug 6, with Sat Aug 1 worked as WD1). Both were read directly off the production tool. No DB / fixtures — the engine is pure, so these run anywhere. """ from __future__ import annotations from datetime import date import pytest from app.domains.finance.close_calendar import ( KIND_HOLIDAY, KIND_MAKEUP, KIND_WEEKEND_OFF, KIND_WEEKEND_WORK, KIND_WORKDAY, ONGOING, compute_close_window, default_anchors, parse_period, ) from app.domains.finance.close_calendar_defaults import ( DEFAULT_HOLIDAYS, DEFAULT_MAKEUP_WORKDAYS, DEFAULT_TARGET_WORKDAY, DEFAULT_WORK_WEEKEND_DAY, ) _HOLIDAYS = {date.fromisoformat(d) for d in DEFAULT_HOLIDAYS} _MAKEUP = {date.fromisoformat(d) for d in DEFAULT_MAKEUP_WORKDAYS} def _window(period: str, **kw): return compute_close_window( period, target_workday=kw.pop("target_workday", DEFAULT_TARGET_WORKDAY), work_weekend_day=kw.pop("work_weekend_day", DEFAULT_WORK_WEEKEND_DAY), holidays=kw.pop("holidays", _HOLIDAYS), makeup_workdays=kw.pop("makeup_workdays", _MAKEUP), **kw, ) # ── parsing / anchors ── def test_parse_period_ok_and_bad(): assert parse_period("2026-06") == (2026, 6) for bad in ("2026-13", "2026/06", "26-6", "", "abcd-ef"): with pytest.raises(ValueError): parse_period(bad) def test_default_anchors(): m1, p1 = default_anchors("2026-06") assert m1 == date(2026, 6, 30) assert p1 == date(2026, 7, 1) # December rolls the year for Day+1. m1d, p1d = default_anchors("2026-12") assert m1d == date(2026, 12, 31) assert p1d == date(2027, 1, 1) # ── the two authoritative real-world cases ── def test_june_2026_close_matches_production(): """June 2026: close = Mon Jul 6 (WD5); Sat Jul 4 worked as WD4.""" w = _window("2026-06") assert w.close_date == date(2026, 7, 6) assert w.anchor_plus_1 == date(2026, 7, 1) # Authoritative anchor panel: Day+2=Jul2, Day+5=Jul6, Day+8=Jul9. assert w.deadline_for("Day+1") == date(2026, 7, 1) assert w.deadline_for("Day+2") == date(2026, 7, 2) assert w.deadline_for("Day+3") == date(2026, 7, 3) assert w.deadline_for("Day+4") == date(2026, 7, 4) # Saturday worked assert w.deadline_for("Day+5") == date(2026, 7, 6) # close assert w.deadline_for("Day+8") == date(2026, 7, 9) # Sat Jul 4 is a worked weekend day; Sun Jul 5 is off. assert date(2026, 7, 4) in w.weekend_work_dates kinds = {wd.day: wd.kind for wd in w.days} assert kinds[date(2026, 7, 4)] == KIND_WEEKEND_WORK assert kinds[date(2026, 7, 5)] == KIND_WEEKEND_OFF assert kinds[date(2026, 7, 6)] == KIND_WORKDAY def test_july_2026_rollforward_matches_production(): """July 2026 close (rolled forward): close = Thu Aug 6; Sat Aug 1 = WD1.""" w = _window("2026-07") assert w.close_date == date(2026, 8, 6) assert w.deadline_for("Day+1") == date(2026, 8, 1) # Saturday worked = WD1 assert w.deadline_for("Day+2") == date(2026, 8, 3) assert w.deadline_for("Day+3") == date(2026, 8, 4) assert w.deadline_for("Day+4") == date(2026, 8, 5) assert w.deadline_for("Day+5") == date(2026, 8, 6) assert date(2026, 8, 1) in w.weekend_work_dates # ── Day-N is a plain calendar countdown from the Day-1 anchor ── def test_pre_close_anchors_are_calendar_days(): w = _window("2026-06") assert w.deadline_for("Day-1") == date(2026, 6, 30) assert w.deadline_for("Day-2") == date(2026, 6, 29) assert w.deadline_for("Day-3") == date(2026, 6, 28) # a Sunday — allowed assert w.deadline_for("Day-5") == date(2026, 6, 26) assert date(2026, 6, 28).weekday() == 6 # confirm it is Sunday # ── holiday + makeup interaction (Sep 2026 close lands in Oct golden week) ── def test_holiday_and_makeup_interaction_sep_2026(): """Closing Sep 2026: Oct 1–7 are holidays; Sat Oct 10 is a makeup workday.""" w = _window("2026-09") days = {wd.day: wd for wd in w.days} # National Day holiday week is off. assert days[date(2026, 10, 1)].kind == KIND_HOLIDAY # First business day after the holiday is WD1. assert w.deadline_for("Day+1") == date(2026, 10, 8) assert w.deadline_for("Day+2") == date(2026, 10, 9) # Sat Oct 10 is an official makeup workday → counts as WD3. assert days[date(2026, 10, 10)].kind == KIND_MAKEUP assert w.deadline_for("Day+3") == date(2026, 10, 10) assert w.deadline_for("Day+4") == date(2026, 10, 12) # Sun 11 off assert w.deadline_for("Day+5") == date(2026, 10, 13) # close assert w.close_date == date(2026, 10, 13) # ── config knobs ── def test_target_workday_is_configurable(): w3 = _window("2026-06", target_workday=3) assert w3.close_date == date(2026, 7, 3) w7 = _window("2026-06", target_workday=7) assert w7.close_date == date(2026, 7, 8) # WD6=Jul7, WD7=Jul8 def test_sunday_work_rule(): """If the team works Sundays instead, the Sunday in the window is promoted.""" w = _window("2026-06", work_weekend_day="sunday") kinds = {wd.day: wd.kind for wd in w.days} assert kinds[date(2026, 7, 5)] == KIND_WEEKEND_WORK # Sun worked assert kinds[date(2026, 7, 4)] == KIND_WEEKEND_OFF # Sat off def test_no_weekend_overlap_no_promotion(): """A window that ends before any weekend has no promoted days.""" # Closing 2026-04: May 1–5 are holidays (Labour Day). May 6 Wed WD1 … w = _window("2026-04", target_workday=3) # WD1=May6(Wed), WD2=May7(Thu), WD3=May8(Fri) — no weekend reached. assert w.close_date == date(2026, 5, 8) assert w.weekend_work_dates == [] # ── anchor resolution edge cases ── def test_ongoing_and_unparseable_anchor_have_no_deadline(): w = _window("2026-06") assert w.deadline_for(ONGOING) is None assert w.deadline_for("") is None assert w.deadline_for("whenever") is None assert w.deadline_for("Day+0") is None def test_day_plus_beyond_lookahead_returns_none(): w = _window("2026-06") assert w.deadline_for("Day+99") is None def test_holidays_are_excluded_from_workday_counting(): """Locks the documented rule: public holidays don't count as workdays. Closing 2026-03: Apr 4–6 are holidays, so WD5 is Apr 8 (not Apr 6 — a holiday). Closing 2026-04: May 1–5 holidays + Sat May 9 is a makeup workday (WD4), so WD5 = May 11. (The source tool's stored Apr-6 / May-5 for these months predate its final holiday config and land *on* holidays, which the rule forbids — Helix computes them correctly.) """ assert _window("2026-03").close_date == date(2026, 4, 8) w_apr = _window("2026-04") assert w_apr.close_date == date(2026, 5, 11) assert date(2026, 5, 9) in w_apr.weekend_work_dates # makeup Saturday = WD4 def test_close_can_land_on_a_worked_weekend_day(): """When the target workday lands on a worked weekend day, the close is that day — the documented rule says the worked Saturday counts toward WD1–WDn. Locked intentionally so a future 'fix' doesn't silently change it.""" w = _window("2026-08") # Sep 1 Tue … Sat Sep 5 worked = WD5 assert w.close_date == date(2026, 9, 5) assert date(2026, 9, 5) in w.weekend_work_dates def test_explicit_anchor_override(): """Caller-supplied anchors override the period defaults.""" w = _window( "2026-06", anchor_minus_1=date(2026, 6, 29), anchor_plus_1=date(2026, 7, 2), ) assert w.deadline_for("Day-1") == date(2026, 6, 29) assert w.deadline_for("Day+1") == date(2026, 7, 2)