97 lines
3.1 KiB
Python
97 lines
3.1 KiB
Python
from datetime import date, datetime
|
|
|
|
import pytest
|
|
|
|
from random_times import MIN_GAP_MIN, compute_fire_times
|
|
|
|
# Window 09:00-21:00 = minutes 540..1260 -> 720 min wide.
|
|
WINDOW = "09:00-21:00"
|
|
WINDOW_START = datetime(2026, 3, 21, 9, 0)
|
|
WINDOW_END = datetime(2026, 3, 21, 21, 0)
|
|
|
|
|
|
def cfg(**overrides) -> dict:
|
|
base = {"times_per_day": 5, "window": WINDOW}
|
|
base.update(overrides)
|
|
return base
|
|
|
|
|
|
def test_deterministic():
|
|
day = date(2026, 3, 21)
|
|
assert compute_fire_times(day, "drink water", cfg()) == compute_fire_times(day, "drink water", cfg())
|
|
|
|
|
|
def test_differs_per_text():
|
|
day = date(2026, 3, 21)
|
|
assert compute_fire_times(day, "drink water", cfg()) != compute_fire_times(day, "stretch", cfg())
|
|
|
|
|
|
@pytest.mark.parametrize("count", [1, 2, 5, 10])
|
|
def test_count_matches_times_per_day(count):
|
|
times = compute_fire_times(date(2026, 3, 21), "x", cfg(times_per_day=count))
|
|
assert len(times) == count
|
|
|
|
|
|
def test_min_gap_respected():
|
|
times = compute_fire_times(date(2026, 3, 21), "x", cfg(times_per_day=8))
|
|
for earlier, later in zip(times, times[1:]):
|
|
assert (later - earlier).total_seconds() >= MIN_GAP_MIN * 60
|
|
|
|
|
|
def test_within_window():
|
|
for fire in compute_fire_times(date(2026, 3, 21), "x", cfg()):
|
|
assert WINDOW_START <= fire <= WINDOW_END
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"day,expected",
|
|
[
|
|
(date(2026, 3, 23), True), # Monday
|
|
(date(2026, 3, 27), True), # Friday
|
|
(date(2026, 3, 28), False), # Saturday
|
|
(date(2026, 3, 29), False), # Sunday
|
|
],
|
|
)
|
|
def test_days_weekday_filter(day, expected):
|
|
times = compute_fire_times(day, "x", cfg(days="1-5"))
|
|
assert bool(times) == expected
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"spec,day,expected",
|
|
[
|
|
("*", date(2026, 3, 28), True), # Saturday allowed by wildcard
|
|
("0", date(2026, 3, 29), True), # Sunday as 0
|
|
("7", date(2026, 3, 29), True), # Sunday as 7
|
|
("1,3,5", date(2026, 3, 25), True), # Wednesday
|
|
("1,3,5", date(2026, 3, 24), False), # Tuesday
|
|
],
|
|
)
|
|
def test_days_parser(spec, day, expected):
|
|
times = compute_fire_times(day, "x", cfg(days=spec))
|
|
assert bool(times) == expected
|
|
|
|
|
|
def test_from_until_filter():
|
|
bounded = cfg(**{"from": "2026-06-01", "until": "2026-06-30"})
|
|
assert compute_fire_times(date(2026, 5, 31), "x", bounded) == []
|
|
assert compute_fire_times(date(2026, 7, 1), "x", bounded) == []
|
|
assert len(compute_fire_times(date(2026, 6, 15), "x", bounded)) == 5
|
|
|
|
|
|
def test_infeasible_count_raises():
|
|
# 50 times * 15-min gap = 735 min required > 720 min window.
|
|
with pytest.raises(ValueError):
|
|
compute_fire_times(date(2026, 3, 21), "x", cfg(times_per_day=50))
|
|
|
|
|
|
def test_bad_window_raises():
|
|
with pytest.raises(ValueError):
|
|
compute_fire_times(date(2026, 3, 21), "x", cfg(window="21:00-09:00"))
|
|
|
|
|
|
def test_config_validated_before_date_filter():
|
|
# Out-of-range date still surfaces a structural error rather than returning [].
|
|
with pytest.raises(ValueError):
|
|
compute_fire_times(date(2000, 1, 1), "x", cfg(times_per_day=50, until="1999-01-01"))
|