fix: dispatch shape before window checks (unimplemented shapes always raise); interval-7 weekday-alignment and leap-day interval-2 tests

This commit is contained in:
Joshua King
2026-09-14 10:59:31 -04:00
parent 87251eeec5
commit b7911f66f6
2 changed files with 86 additions and 9 deletions
+15 -8
View File
@@ -425,16 +425,18 @@ def occurs_on(rule: ScheduleRule, target: datetime.date) -> bool:
are plain calendar dates (Feature 04 guardrail); the caller decides
the timezone that produced them.
"""
# Dispatch FIRST: an unimplemented shape must raise loudly even for
# dates outside the window, never silently evaluate to False.
shape = rule.rule_type
if shape is not RuleType.DAILY:
raise NotImplementedError(
f"occurs_on for {shape} lands with its own engine task "
"(Features 04 task sequence)"
)
if not _within_window(rule, target):
return False
shape = rule.rule_type
if shape is RuleType.DAILY:
offset = (target - _parse_date(rule.start_date)).days
return offset % rule.interval == 0
raise NotImplementedError(
f"occurs_on for {shape} lands with its own engine task "
"(Features 04 task sequence)"
)
offset = (target - _parse_date(rule.start_date)).days
return offset % rule.interval == 0
def occurrences_between(
@@ -446,6 +448,11 @@ def occurrences_between(
inverted bounds; a window entirely outside [rule.start_date,
rule.end_date] yields an empty list.
"""
if rule.rule_type is not RuleType.DAILY:
raise NotImplementedError(
f"occurrences_between for {rule.rule_type} lands with its own "
"engine task (Features 04 task sequence)"
)
_validate_date(start, "start")
_validate_date(end, "end")
start_date = _parse_date(start)
+71 -1
View File
@@ -220,4 +220,74 @@ def test_occurs_on_rejects_unimplemented_shapes_loudly() -> None:
rule_type=RuleType.WEEKLY, weekday_set={0}, start_date="2026-09-01"
)
with pytest.raises(NotImplementedError):
occurs_on(rule, _d("2026-09-07"))
occurs_on(rule, _d("2026-09-07"))
def test_interval_7_preserves_weekday_alignment_across_months() -> None:
"""Weekly-by-interval semantics: every firing lands on the SAME
weekday as start_date, no matter how many month edges intervene."""
rule = ScheduleRule(
rule_type=RuleType.DAILY, interval=7, start_date="2026-09-01"
)
# 2026-09-01 is a Tuesday; every firing must be a Tuesday too.
assert _d("2026-09-01").weekday() == 1
for day in ("2026-09-08", "2026-09-29", "2026-10-06", "2026-11-03"):
assert occurs_on(rule, _d(day)) is True, day
assert _d(day).weekday() == 1, f"{day} must be a Tuesday"
# The day after a firing is never a firing.
assert occurs_on(rule, _d("2026-10-07")) is False
def test_interval_2_across_leap_day() -> None:
"""Interval > 1 arithmetic across a leap day: the extra day shifts
subsequent offsets by one, and the mod arithmetic reflects it."""
rule = ScheduleRule(
rule_type=RuleType.DAILY, interval=2, start_date="2028-02-27"
)
assert occurs_on(rule, _d("2028-02-27")) is True # 0
assert occurs_on(rule, _d("2028-02-28")) is False # 1
assert occurs_on(rule, _d("2028-02-29")) is True # 2 (leap day)
assert occurs_on(rule, _d("2028-03-01")) is False # 3
assert occurs_on(rule, _d("2028-03-02")) is True # 4
def test_occurrences_between_leap_day_interval_2() -> None:
rule = ScheduleRule(
rule_type=RuleType.DAILY, interval=2, start_date="2028-02-27"
)
result = occurrences_between(rule, "2028-02-27", "2028-03-03")
assert result == ["2028-02-27", "2028-02-29", "2028-03-02"]
def test_occurrences_between_year_boundary_interval_2() -> None:
rule = ScheduleRule(
rule_type=RuleType.DAILY, interval=2, start_date="2026-12-30"
)
result = occurrences_between(rule, "2026-12-30", "2027-01-04")
# Offsets 0, 2, 4, 6 = Dec 30, Jan 1, Jan 3 ... wait: offsets are
# Dec30=0, Dec31=1, Jan1=2, Jan2=3, Jan3=4, Jan4=5. Even offsets:
assert result == ["2026-12-30", "2027-01-01", "2027-01-03"]
def test_occurs_on_unimplemented_shape_raises_even_outside_window() -> None:
"""An unimplemented shape must raise whether the date is inside or
outside the window — silent False would hide a missing engine."""
rule = ScheduleRule(
rule_type=RuleType.WEEKLY, weekday_set={0},
start_date="2026-09-01", end_date="2026-09-10",
)
# Before start, inside window, after end: all must raise.
for day in ("2026-08-31", "2026-09-07", "2026-09-20"):
with pytest.raises(NotImplementedError):
occurs_on(rule, _d(day))
def test_occurrences_between_unimplemented_shape_raises_even_disjoint(
tmp_path,
) -> None:
rule = ScheduleRule(
rule_type=RuleType.WEEKLY, weekday_set={0}, start_date="2026-09-01"
)
# A range entirely before the rule's start still raises: the shape
# is unimplemented, and silence would hide it.
with pytest.raises(NotImplementedError):
occurrences_between(rule, "2026-08-01", "2026-08-10")