5.0 KiB
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:
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:
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:
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_timeon day N at 23:00 andend_timeon day N+1 at 07:00 - Calls
/api/stats/timetablefor 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-summaryfor 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-summarywithday_start=not-a-time→ expect 422/api/stats/timetablewithday_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 422GET /api/sessions?limit=1000001→ expect 422GET /api/sessions?limit=1→ expect 200GET /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_atvalue - Sends a PUT/PATCH request to update it
- Asserts that
updated_atin the response is strictly greater than the original value
Final verification
Run the complete test suite one last time:
pytest --tb=short
All tests must pass. Review the output for any warnings. This completes all 15 improvements from the backend improvement report.