from datetime import date, datetime from app.auth.services import create_user from app.tasks.services import ( calculate_forgiving_streak, complete_task, create_task, ) def login(client, email: str, password: str = "secure password") -> None: client.post( "/auth/login", data={"email": email, "password": password}, ) def make_task(user_id: int, title: str, *, priority: str = "normal"): return create_task( user_id, title=title, description=None, status="not_started", priority=priority, due_date=None, ) def test_focus_mode_surfaces_only_highest_ranked_today_task(client, db): user = create_user("focus_user", "focus@example.com", "secure password") make_task(user.id, "Lower priority choice") primary = make_task(user.id, "Primary urgent choice", priority="urgent") login(client, user.email) response = client.get("/tasks/focus") assert response.status_code == 200 assert b"Primary urgent choice" in response.data assert b"Lower priority choice" not in response.data assert f'data-task-id="{primary.id}"'.encode() not in response.data def test_focus_settings_store_timer_context_and_chunks(client, db): user = create_user("setup_user", "setup@example.com", "secure password") task = make_task(user.id, "Configured focus task") login(client, user.email) response = client.post( f"/tasks/{task.id}/focus-settings", data={ "timer_minutes": "25", "context": "At my desk with headphones", "chunk_plan": "5, 5, 10", }, follow_redirects=True, ) db.session.refresh(task) assert response.status_code == 200 assert b"Focus setup saved" in response.data assert b"Visual countdown" in response.data assert b"At my desk with headphones" in response.data assert task.timer_seconds == 1500 assert task.context == "At my desk with headphones" assert task.chunk_sessions == [ {"minutes": 5, "completed": False}, {"minutes": 5, "completed": False}, {"minutes": 10, "completed": False}, ] def test_invalid_chunk_plan_is_rejected_without_changes(client, db): user = create_user("invalid_user", "invalid@example.com", "secure password") task = make_task(user.id, "Unchanged focus task") login(client, user.email) response = client.post( f"/tasks/{task.id}/focus-settings", data={"timer_minutes": "10", "chunk_plan": "5, later, 10"}, follow_redirects=True, ) db.session.refresh(task) assert b"Use comma-separated whole minutes" in response.data assert task.timer_seconds == 0 assert task.chunk_sessions == [] def test_chunk_toggle_updates_only_requested_block(client, db): user = create_user("chunk_user", "chunk@example.com", "secure password") task = make_task(user.id, "Chunked task") task.chunk_sessions = [ {"minutes": 5, "completed": False}, {"minutes": 10, "completed": False}, ] db.session.commit() login(client, user.email) response = client.post( f"/tasks/{task.id}/chunks/1/toggle", follow_redirects=True, ) db.session.refresh(task) assert response.status_code == 200 assert task.chunk_sessions[0]["completed"] is False assert task.chunk_sessions[1]["completed"] is True assert client.post(f"/tasks/{task.id}/chunks/4/toggle").status_code == 404 def test_completion_celebrates_and_advances_to_next_task(client, db): user = create_user("complete_user", "complete@example.com", "secure password") primary = make_task(user.id, "Finish this first", priority="urgent") make_task(user.id, "Then show this task") login(client, user.email) response = client.post( f"/tasks/{primary.id}/complete", follow_redirects=True, ) db.session.refresh(primary) assert response.status_code == 200 assert b"That step is complete" in response.data assert b'id="focus-task-title">Then show this task' in response.data assert b'id="focus-task-title">Finish this first' not in response.data assert primary.status == "done" assert primary.completed_at is not None def test_focus_mutations_do_not_expose_foreign_tasks(client, db): owner = create_user("focus_owner", "focus-owner@example.com", "secure password") visitor = create_user( "focus_visitor", "focus-visitor@example.com", "secure password", ) task = make_task(owner.id, "Private focus task") login(client, visitor.email) assert client.get(f"/tasks/{task.id}/focus").status_code == 404 assert client.post(f"/tasks/{task.id}/complete").status_code == 404 assert ( client.post( f"/tasks/{task.id}/focus-settings", data={"timer_minutes": "15"}, ).status_code == 404 ) assert client.post(f"/tasks/{task.id}/chunks/0/toggle").status_code == 404 def test_forgiving_streak_allows_one_internal_rest_day(db): user = create_user("streak_user", "streak@example.com", "secure password") for day in (8, 7, 5): task = make_task(user.id, f"Completed on day {day}") complete_task(task, completed_at=datetime(2026, 8, day, 12, 0)) streak = calculate_forgiving_streak(user.id, today=date(2026, 8, 8)) assert streak.days == 3 assert streak.freeze_used is True def test_current_incomplete_day_does_not_break_streak(db): user = create_user("rest_user", "rest@example.com", "secure password") yesterday = make_task(user.id, "Yesterday counted") complete_task(yesterday, completed_at=datetime(2026, 8, 7, 12, 0)) streak = calculate_forgiving_streak(user.id, today=date(2026, 8, 8)) assert streak.days == 1 assert streak.freeze_used is False