# Plan 5 — Observability & Test Coverage **Items covered:** #10 (no logging), #12 (missing test coverage) --- ## Context You are working on a FastAPI backend for a baby monitor / sleep tracker app. The codebase lives under `app/`. This is the final plan. Plans 1–4 must already be complete — this plan assumes auth returns 401, malformed parameters return 422, and `updated_at` is updated on PATCH/PUT. Do the logging setup first (it is quick), then work through the missing tests methodically. Add tests one group at a time and run `pytest` after each group. --- ## Tasks ### 1. Add structured logging (`app/main.py` and other files) #### Step 1 — Read `app/config.py` and `app/main.py` first Confirm that `LOG_LEVEL` already exists in `Settings` (it does — it is currently defined but never consumed). Note its default value. #### Step 2 — Configure logging in `main.py` At the top of `app/main.py`, before the app is created, add: ```python import logging logging.basicConfig( level=settings.LOG_LEVEL, format="%(asctime)s %(levelname)s %(name)s %(message)s", ) logger = logging.getLogger(__name__) ``` #### Step 3 — Add request logging middleware Add a simple middleware to log every request and response status: ```python import time from fastapi import Request @app.middleware("http") async def log_requests(request: Request, call_next): start = time.time() response = await call_next(request) duration_ms = (time.time() - start) * 1000 logger.info( "%s %s → %s (%.1fms)", request.method, request.url.path, response.status_code, duration_ms, ) return response ``` #### Step 4 — Add business event logging In the relevant router files, add a `logger = logging.getLogger(__name__)` at the module level and log the key business events: - Session started: `logger.info("Session started id=%s", session.id)` - Session ended: `logger.info("Session ended id=%s duration_min=%.1f", session.id, ...)` - Session deleted: `logger.info("Session deleted id=%s", session_id)` Keep log messages concise. Do not log sensitive user data. #### Step 5 — Verify Run the tests: ```bash pytest ``` Logging must not break any existing tests. --- ### 2. Add missing test coverage (`tests/`) Read the existing test files before adding anything so you understand the test client setup, fixtures, and conventions already in use. Add all new tests to appropriate existing files (or a new file if clearly warranted — e.g. `tests/test_stats.py` if one does not exist). Work through each group below. After adding each group, run `pytest` and fix any failures before continuing. --- #### Group A — Stats endpoints: empty date ranges For each stats endpoint (`/api/stats/daily-summary`, `/api/stats/timetable`, `/api/stats/wake-sleep-times`), add a test that: - Calls the endpoint with a valid date range that has no sessions - Asserts a 200 response - Asserts the response body represents an empty result (no days, empty list, etc.) --- #### Group B — Stats endpoints: session spanning midnight Add a test that: - Creates a session with `start_time` on day N at 23:00 and `end_time` on day N+1 at 07:00 - Calls `/api/stats/timetable` for a range covering both days - Asserts that the session appears in both days' slot data --- #### Group C — Stats endpoints: session partially overlapping the query range Add a test that: - Creates a session that starts before the query range and ends inside it - Calls each stats endpoint with the range - Asserts the session is counted/included correctly (it should be, since it overlaps the range) --- #### Group D — Stats endpoints: multiple sessions on the same day Add a test that: - Creates two or more sessions on the same day - Calls `/api/stats/daily-summary` for that day - Asserts that the totals correctly aggregate across both sessions --- #### Group E — Invalid query parameters return 422 These tests depend on Plan 2 (silent suppression replaced with 422). Add a parametrised test (or individual tests) for: - `/api/stats/daily-summary` with `day_start=not-a-time` → expect 422 - `/api/stats/timetable` with `day_start=not-a-time` → expect 422 - Any other endpoint that previously silently suppressed a `ValueError` --- #### Group F — Bounded `limit` parameter This test depends on Plan 4 (Query bound added). Add tests for: - `GET /api/sessions?limit=0` → expect 422 - `GET /api/sessions?limit=1000001` → expect 422 - `GET /api/sessions?limit=1` → expect 200 - `GET /api/sessions?limit=1000` → expect 200 --- #### Group G — `updated_at` regression This test depends on Plan 1 (updated_at bug fixed). Add a test that: - Creates a session - Records the `updated_at` value - Sends a PUT/PATCH request to update it - Asserts that `updated_at` in the response is strictly greater than the original value --- ## Final verification Run the complete test suite one last time: ```bash pytest --tb=short ``` All tests must pass. Review the output for any warnings. This completes all 15 improvements from the backend improvement report.