Files
kadence/backend/tests/test_reports.py
T
2026-10-18 18:02:05 +03:00

100 lines
4.0 KiB
Python

"""Metrics, achievements and the rendered report."""
from __future__ import annotations
from datetime import timedelta
from app.models import utcnow
from app.services import pomodoro, report, settings_service, stats, tasks_service
from app.timeutil import local_date
def _run_pomodoros(session, settings, count: int, *, minutes: int = 25) -> None:
"""Record `count` finished work intervals, backdated so they do not overlap."""
for index in range(count):
row = pomodoro.start(session, settings, kind="work", minutes=minutes)
start = utcnow() - timedelta(minutes=(count - index) * (minutes + 5))
row.started_at = start
row.ended_at = start + timedelta(minutes=minutes)
row.actual_seconds = minutes * 60
row.status = "completed"
session.commit()
def test_day_metrics_counts_what_happened(session, settings):
_run_pomodoros(session, settings, 3)
metrics = stats.day_metrics(session, settings, local_date(settings.timezone))
assert metrics["pomodoros"] == 3
assert metrics["focus_minutes"] == 75
assert metrics["goal"] == settings.pomodoro.daily_goal_pomodoros
assert metrics["first_start"] and metrics["last_end"]
def test_longest_stretch_joins_adjacent_pomodoros(session, settings):
_run_pomodoros(session, settings, 3, minutes=25)
metrics = stats.day_metrics(session, settings, local_date(settings.timezone))
# 25-minute intervals five minutes apart are one unbroken stretch.
assert metrics["longest_stretch_minutes"] >= 75
def test_streak_survives_an_empty_today(session, settings):
today = local_date(settings.timezone)
_run_pomodoros(session, settings, 1)
# Move the work to yesterday and leave today empty.
from app.models import PomodoroSession
for row in session.query(PomodoroSession).all():
row.local_date = today - timedelta(days=1)
session.commit()
streak = stats.streak(session, settings, until=today)
assert streak["current"] == 1
def test_report_payload_has_the_pieces_the_ui_draws(session, settings):
settings.pomodoro.daily_goal_pomodoros = 2
settings_service.save_settings(session, settings)
_run_pomodoros(session, settings, 2)
task = tasks_service.create_local_task(session, title="Ship the thing")
tasks_service.complete_task(session, settings, task.id)
payload = report.build_daily(session, settings, local_date(settings.timezone))
assert payload["metrics"]["pomodoros"] == 2
assert payload["metrics"]["tasks_completed_count"] == 1
assert any(item["code"] == "goal_met" for item in payload["achievements"])
assert payload["fun_facts"]
assert 0 <= payload["score"] <= 100
def test_rendered_report_escapes_borrowed_text(session, settings):
task = tasks_service.create_local_task(session, title="Fix <b>&</b> ship")
tasks_service.complete_task(session, settings, task.id)
payload = report.build_daily(session, settings, local_date(settings.timezone))
text = report.render_daily_telegram(payload, settings)
assert "Fix &lt;b&gt;&amp;&lt;/b&gt; ship" in text
def test_russian_report_renders(session, settings):
settings.notifications.report.language = "ru"
settings.notifications.report.tone = "drill_sergeant"
settings_service.save_settings(session, settings)
_run_pomodoros(session, settings, 1)
payload = report.build_daily(session, settings, local_date(settings.timezone))
text = report.render_daily_telegram(payload, settings)
assert "Отчёт за день" in text
def test_weekly_review_covers_seven_days(session, settings):
_run_pomodoros(session, settings, 2)
payload = report.build_weekly(session, settings, local_date(settings.timezone))
assert len(payload["days"]) == 7
assert payload["pomodoros"] == 2
assert "Weekly review" in report.render_weekly_telegram(payload, settings)
def test_progress_bar_is_proportional():
assert report.progress_bar(0, 8) == "░" * 10
assert report.progress_bar(8, 8) == "█" * 10
assert report.progress_bar(4, 8).count("█") == 5