| from __future__ import annotations |
|
|
| import asyncio |
|
|
| from backend.services import task_service |
| from backend.services import reminder_dispatcher, reminder_service |
|
|
|
|
| def test_health_endpoint(client): |
| response = client.get("/health") |
|
|
| assert response.status_code == 200 |
| body = response.json() |
| assert body["status"] == "ok" |
| assert "app" in body |
|
|
|
|
| def test_session_lifecycle(client): |
| created = client.post("/api/history/sessions") |
| assert created.status_code == 200 |
| session = created.json() |
|
|
| listed = client.get("/api/history/sessions") |
| assert listed.status_code == 200 |
| assert len(listed.json()) == 1 |
|
|
| fetched = client.get(f"/api/history/sessions/{session['id']}") |
| assert fetched.status_code == 200 |
| assert fetched.json()["id"] == session["id"] |
|
|
| deleted = client.delete(f"/api/history/sessions/{session['id']}") |
| assert deleted.status_code == 200 |
| assert deleted.json()["deleted"] is True |
|
|
| missing = client.get(f"/api/history/sessions/{session['id']}") |
| assert missing.status_code == 404 |
|
|
|
|
| def test_task_routes(client): |
| created = client.post("/api/tasks", json={"text": "Ship release notes"}) |
| assert created.status_code == 200 |
| task_id = created.json()["id"] |
|
|
| listed = client.get("/api/tasks") |
| assert listed.status_code == 200 |
| assert listed.json()[0]["text"] == "Ship release notes" |
|
|
| completed = client.patch(f"/api/tasks/{task_id}/complete") |
| assert completed.status_code == 200 |
| assert completed.json()["completed"] is True |
|
|
| deleted = client.delete(f"/api/tasks/{task_id}") |
| assert deleted.status_code == 200 |
| assert deleted.json()["id"] == task_id |
|
|
|
|
| def test_reminder_routes_and_validation(client): |
| invalid = client.post("/api/reminders", json={"title": "Review sprint board", "date": "2026-06-01"}) |
| assert invalid.status_code == 422 |
|
|
| created = client.post( |
| "/api/reminders", |
| json={"title": "Review sprint board", "note": "Bring blockers", "date": "2020-01-01", "time": "10:00"}, |
| ) |
| assert created.status_code == 200 |
| reminder_id = created.json()["id"] |
| assert created.json()["title"] == "Review sprint board" |
| assert created.json()["status"] == "overdue" |
|
|
| listed = client.get("/api/reminders") |
| assert listed.status_code == 200 |
| assert len(listed.json()) == 1 |
| assert listed.json()[0]["note"] == "Bring blockers" |
|
|
| due = client.get("/api/reminders/due") |
| assert due.status_code == 200 |
| assert due.json()[0]["id"] == reminder_id |
|
|
| updated = client.put( |
| f"/api/reminders/{reminder_id}", |
| json={"title": "Review sprint board updated", "note": "Updated note", "date": "2030-01-01", "time": "11:30"}, |
| ) |
| assert updated.status_code == 200 |
| assert updated.json()["title"] == "Review sprint board updated" |
| assert updated.json()["status"] == "scheduled" |
|
|
| completed = client.patch(f"/api/reminders/{reminder_id}/complete") |
| assert completed.status_code == 200 |
| assert completed.json()["completed"] is True |
| assert completed.json()["status"] == "done" |
|
|
| postponed = client.patch(f"/api/reminders/{reminder_id}/postpone") |
| assert postponed.status_code == 200 |
| assert postponed.json()["completed"] is False |
|
|
| deleted = client.delete(f"/api/reminders/{reminder_id}") |
| assert deleted.status_code == 200 |
|
|
|
|
| def test_note_routes(client): |
| created = client.post("/api/notes", json={"text": "Remember the retro action items", "source": "manual"}) |
| assert created.status_code == 200 |
| note_id = created.json()["id"] |
|
|
| listed = client.get("/api/notes") |
| assert listed.status_code == 200 |
| assert listed.json()[0]["id"] == note_id |
|
|
| deleted = client.delete(f"/api/notes/{note_id}") |
| assert deleted.status_code == 200 |
|
|
|
|
| def test_chat_commands_history_search_and_save(client): |
| created = client.post("/api/chat", json={"message": "add task finish QA audit"}) |
| assert created.status_code == 200 |
| payload = created.json() |
| session_id = payload["session_id"] |
| assert "Task added" in payload["reply"] |
|
|
| follow_up = client.post("/api/chat", json={"session_id": session_id, "message": "show my tasks"}) |
| assert follow_up.status_code == 200 |
| assert "Your tasks" in follow_up.json()["reply"] |
|
|
| session_detail = client.get(f"/api/history/sessions/{session_id}") |
| assert session_detail.status_code == 200 |
| messages = session_detail.json()["messages"] |
| assistant_message = next(message for message in messages if message["role"] == "assistant") |
|
|
| saved = client.post(f"/api/history/sessions/{session_id}/messages/{assistant_message['id']}/save") |
| assert saved.status_code == 200 |
|
|
| saved_messages = client.get("/api/history/saved") |
| assert saved_messages.status_code == 200 |
| assert len(saved_messages.json()) == 1 |
|
|
| results = client.get("/api/history/search", params={"query": "finish QA audit"}) |
| assert results.status_code == 200 |
| assert len(results.json()) >= 1 |
|
|
|
|
| def test_summary_counts(client): |
| client.post("/api/tasks", json={"text": "Prepare 1:1 summary"}) |
| client.post("/api/reminders", json={"title": "Prepare 1:1 summary", "date": "2020-01-01", "time": "10:00"}) |
| client.post("/api/notes", json={"text": "Growth areas for next quarter", "source": "manual"}) |
|
|
| response = client.get("/api/history/summary") |
| assert response.status_code == 200 |
| summary = response.json() |
|
|
| assert summary["pending_tasks"] == 1 |
| assert summary["active_reminders"] == 1 |
| assert summary["notes"] == 1 |
| assert summary["due_reminders"] == 1 |
|
|
|
|
| def test_storage_recovers_from_invalid_json(client): |
| task_service.STORE.path.write_text("{invalid json", encoding="utf-8") |
|
|
| response = client.get("/api/tasks") |
| assert response.status_code == 200 |
| assert response.json() == [] |
|
|
|
|
| def test_due_reminders_send_to_telegram_once(monkeypatch): |
| sent_messages = [] |
|
|
| async def fake_send(reminder): |
| sent_messages.append(reminder["title"]) |
| return True |
|
|
| monkeypatch.setattr(reminder_dispatcher, "send_reminder_notification", fake_send) |
| monkeypatch.setattr(reminder_dispatcher, "telegram_notifications_enabled", lambda: True) |
|
|
| created = reminder_service.add_reminder( |
| "Send standup update", |
| reminder_service.datetime.now() - reminder_service.timedelta(minutes=1), |
| note="Mention blockers", |
| ) |
|
|
| sent_count = asyncio.run(reminder_dispatcher.process_due_reminders()) |
| assert sent_count == 1 |
| assert sent_messages == ["Send standup update"] |
|
|
| updated = reminder_service.get_reminder(created["id"], include_deleted=True) |
| assert updated is not None |
| assert updated["notified_at"] is not None |
|
|
| sent_count_again = asyncio.run(reminder_dispatcher.process_due_reminders()) |
| assert sent_count_again == 0 |
| assert sent_messages == ["Send standup update"] |
|
|