"""The timer's state machine.""" from __future__ import annotations import pytest from app.models import PomodoroSession from app.services import pomodoro, settings_service def test_start_sets_duration_from_settings(session, settings): row = pomodoro.start(session, settings, kind="work") assert row.planned_seconds == settings.pomodoro.work_minutes * 60 assert row.status == "running" assert pomodoro.remaining_seconds(row) <= row.planned_seconds def test_minutes_override_wins(session, settings): row = pomodoro.start(session, settings, kind="work", minutes=3) assert row.planned_seconds == 180 def test_starting_again_abandons_the_previous_interval(session, settings): first = pomodoro.start(session, settings, kind="work") pomodoro.start(session, settings, kind="work") session.refresh(first) assert first.status == "abandoned" assert len(session.query(PomodoroSession).all()) == 2 def test_pause_counts_an_interruption_and_freezes_the_clock(session, settings): pomodoro.start(session, settings, kind="work") row = pomodoro.pause(session, settings) assert row.status == "paused" assert row.interruptions == 1 frozen = pomodoro.elapsed_seconds(row) assert pomodoro.elapsed_seconds(row) == frozen def test_pause_can_be_switched_off(session, settings): settings.pomodoro.allow_pause = False settings_service.save_settings(session, settings) pomodoro.start(session, settings, kind="work") with pytest.raises(pomodoro.TimerError): pomodoro.pause(session, settings) def test_long_break_arrives_on_the_configured_cycle(session, settings): settings.pomodoro.cycles_before_long_break = 2 settings.pomodoro.auto_start_breaks = False settings_service.save_settings(session, settings) pomodoro.start(session, settings, kind="work") pomodoro.complete(session, settings, auto_next=False) assert pomodoro.next_kind(session, settings) == "short_break" pomodoro.start(session, settings, kind="short_break") pomodoro.complete(session, settings, auto_next=False) pomodoro.start(session, settings, kind="work") pomodoro.complete(session, settings, auto_next=False) assert pomodoro.next_kind(session, settings) == "long_break" def test_auto_start_breaks_rolls_straight_on(session, settings): settings.pomodoro.auto_start_breaks = True settings_service.save_settings(session, settings) pomodoro.start(session, settings, kind="work") finished, follow_on = pomodoro.complete(session, settings) assert finished.status == "completed" assert follow_on is not None and follow_on.kind == "short_break" def test_strict_mode_demands_a_reason(session, settings): settings.pomodoro.strict_mode = True settings_service.save_settings(session, settings) pomodoro.start(session, settings, kind="work") with pytest.raises(pomodoro.TimerError): pomodoro.abandon(session, settings) row = pomodoro.abandon(session, settings, reason="fire alarm") assert row.status == "abandoned" and row.abandon_reason == "fire alarm" def test_reap_closes_intervals_left_running(session, settings): from datetime import timedelta from app.models import utcnow row = pomodoro.start(session, settings, kind="work") row.started_at = utcnow() - timedelta(hours=9) session.commit() assert pomodoro.reap_stale(session, settings) == 1 session.refresh(row) assert row.status == "abandoned" def test_timer_disabled_prevents_start(session, settings): settings.pomodoro.timer_enabled = False settings_service.save_settings(session, settings) with pytest.raises(pomodoro.TimerError, match="view-only"): pomodoro.start(session, settings, kind="work") st = pomodoro.state(session, settings) assert st["timer_enabled"] is False