124 lines
4.1 KiB
Python
124 lines
4.1 KiB
Python
from datetime import datetime
|
|
from zoneinfo import ZoneInfo
|
|
|
|
from beaver_agent.jobs.leisure import (
|
|
MINE_MIN,
|
|
QUIET_TICKS,
|
|
Plan,
|
|
brief,
|
|
day_of,
|
|
enabled,
|
|
plan_of,
|
|
quiet_hour,
|
|
tick_hours,
|
|
)
|
|
|
|
ZONE = ZoneInfo("Europe/Warsaw")
|
|
|
|
|
|
def at(text: str) -> datetime:
|
|
return datetime.fromisoformat(text).replace(tzinfo=ZONE)
|
|
|
|
|
|
def test_switch_defaults_to_on_and_only_explicit_off_stops_it():
|
|
assert enabled(None)
|
|
assert enabled("# досуг\n\nничего про выключатель")
|
|
assert enabled("досуг: вкл")
|
|
assert not enabled("# досуг\n\nдосуг: выкл\nсутки: 2026-09-08")
|
|
assert not enabled("досуг: off")
|
|
|
|
|
|
def test_day_starts_at_noon():
|
|
assert day_of(at("2026-09-08 12:00")) == "2026-09-08"
|
|
assert day_of(at("2026-09-08 23:30")) == "2026-09-08"
|
|
assert day_of(at("2026-09-09 03:00")) == "2026-09-08"
|
|
assert day_of(at("2026-09-09 11:59")) == "2026-09-08"
|
|
assert day_of(at("2026-09-09 12:00")) == "2026-09-09"
|
|
|
|
|
|
def test_active_window_ticks_hourly_and_quiet_window_only_by_plan():
|
|
hours = tick_hours(QUIET_TICKS)
|
|
assert hours[0] == 12
|
|
assert len(hours) == 20
|
|
assert all(h in hours for h in (12, 18, 23, 0, 3))
|
|
assert [h for h in hours if quiet_hour(h)] == [4, 6, 8, 10]
|
|
assert 5 not in hours and 11 not in hours
|
|
assert tick_hours((5, 7))[-2:] == (5, 7)
|
|
|
|
|
|
def test_plan_is_read_from_the_file_for_todays_day_only():
|
|
text = (
|
|
"# досуг\n\n"
|
|
"досуг: вкл\n"
|
|
"сутки: 2026-09-08\n"
|
|
"тихие часы: 05, 07, 09, 11\n"
|
|
"мои часы: 13, 16, 19, 22, 01, 05, 09\n"
|
|
)
|
|
plan = plan_of(text, "2026-09-08")
|
|
assert plan.written
|
|
assert plan.quiet == (5, 7, 9, 11)
|
|
assert plan.mine == (13, 16, 19, 22, 1, 5, 9)
|
|
|
|
stale = plan_of(text, "2026-09-09")
|
|
assert not stale.written
|
|
assert stale.quiet == QUIET_TICKS
|
|
|
|
|
|
def test_missing_plan_falls_back_to_defaults():
|
|
plan = plan_of(None, "2026-09-08")
|
|
assert not plan.written
|
|
assert plan.quiet == QUIET_TICKS
|
|
assert len(plan.mine) == MINE_MIN
|
|
assert set(plan.mine) <= set(tick_hours(plan.quiet))
|
|
|
|
|
|
def test_short_plan_is_topped_up_to_the_guaranteed_minimum():
|
|
text = "сутки: 2026-09-08\nмои часы: 14, 20\n"
|
|
plan = plan_of(text, "2026-09-08")
|
|
assert plan.written
|
|
assert len(plan.mine) == MINE_MIN
|
|
assert 14 in plan.mine and 20 in plan.mine
|
|
assert plan.mine == tuple(sorted(plan.mine, key=tick_hours(plan.quiet).index))
|
|
|
|
|
|
def test_unreachable_hours_are_dropped_from_the_plan():
|
|
text = "сутки: 2026-09-08\nтихие часы: 04, 06, 08, 10\nмои часы: 05, 11, 14\n"
|
|
plan = plan_of(text, "2026-09-08")
|
|
assert 5 not in plan.mine and 11 not in plan.mine
|
|
assert 14 in plan.mine and len(plan.mine) == MINE_MIN
|
|
|
|
|
|
def test_brief_names_the_type_and_counts_spent_slots():
|
|
plan = Plan(
|
|
day="2026-09-08",
|
|
quiet=QUIET_TICKS,
|
|
mine=(13, 15, 18, 21, 0, 2, 6),
|
|
written=True,
|
|
)
|
|
text = brief(at("2026-09-08 18:00"), plan)
|
|
assert "Тип: досуг" in text
|
|
assert "израсходовано 3 из 7" in text
|
|
assert "впереди 21, 00, 02, 06" in text
|
|
assert "Сутки заканчиваются через 18 ч" in text
|
|
assert "Задания нет" in text
|
|
assert "Разложи" not in text
|
|
|
|
mixed = brief(at("2026-09-08 19:00"), plan)
|
|
assert "Тип: смешанный" in mixed
|
|
assert "израсходовано 3 из 7" in mixed
|
|
|
|
|
|
def test_first_tick_of_the_day_is_the_planning_one():
|
|
text = brief(at("2026-09-08 12:00"), plan_of(None, "2026-09-08"))
|
|
assert "Первый тик суток" in text
|
|
assert "расписания на эти сутки нет" in text.lower()
|
|
assert str(MINE_MIN) in text
|
|
|
|
|
|
def test_last_night_tick_says_nothing_is_left_ahead():
|
|
plan = Plan(day="2026-09-08", quiet=QUIET_TICKS, mine=(13, 6), written=True)
|
|
text = brief(at("2026-09-09 06:00"), plan)
|
|
assert "впереди больше нет" in text
|
|
assert "израсходовано 2 из 2" in text
|
|
assert "Сутки заканчиваются через 6 ч" in text
|