Files
kadence/backend/tests/test_pomodoro.py
T
timka123andClaude Opus 5 ec7eedbc66 style: say which lint rules this repo keeps, then keep them
`[tool.ruff]` set a line length and a target version and nothing else, so which
rules ran depended on the installed ruff's defaults — fine while nobody ran it,
not fine for a CI job that can turn a push red because a tool changed its mind.

The set is named explicitly (E, F, I, UP, SIM, B, RUF) with three ignores that
are each a real decision rather than a shrug: B008, because FastAPI's whole
dependency idiom is a call in a default argument; E501, because wrapping prose
is a formatter's business; and RUF001, because the reports and the bot speak
Russian and "ambiguous character" is the wrong complaint about a string the
user is meant to read.

The 73 findings that remained are fixed — mostly `datetime.timezone.utc` →
`datetime.UTC`, `typing.Callable` → `collections.abc.Callable`, import order,
and unused `noqa` markers — plus seven by hand: `contextlib.suppress` for two
try/except/pass pairs, unpacking instead of list concatenation, one negated
return.

`ruff format` is deliberately *not* adopted: it would reformat 51 files, and
some of the layout here is hand-set (the comment tables, the DNS wire-format
structs) in ways a formatter flattens.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-22 17:28:57 +03:00

102 lines
3.7 KiB
Python

"""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