Add core API endpoints with rolling window logic and integration tests #6
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Lineage
plan-mcd-tracker→ Phase 5: Core API Endpoints + Integration TestsRepo
forgejo_admin/mcd-tracker-apiUser Story
log-code: As a user, I want to log a coupon code when I get a McDonald's receiptslots-remaining: As a user, I want to see how many codes I have left at each locationreopen-countdown: As a user, I want to know when my next slot reopensredeem: As a user, I want to mark a code as redeemed when I use ithistory: As a user, I want to browse my history of codes and redemptionsadmin-stats: As an admin, I want to see aggregate usage stats across all usersArchitecture
arch-dataflow-mcd-tracker— implements all 4 runtime flows: log code (Flow 1), check availability (Flow 2), redeem (Flow 3), slot limit rejection (Flow 4)arch-domain-mcd-tracker#rolling-window-logic— the SQL queries from the diagram become SQLAlchemy queriesContext
Phases 1-4 complete. FastAPI app is live with SQLAlchemy models (Location, CouponUsage), Alembic migrations, Keycloak JWT auth (get_current_user, require_role). This phase adds all the business logic routes.
The rolling window is the core business rule: 5 codes per location per user per rolling 30-day window. Each code logged starts an independent 30-day timer (expires_at = used_at + 30 days). When expires_at passes, that slot reopens.
File Targets
Files to create:
src/mcd_tracker_api/routes/locations.py— Location CRUD routessrc/mcd_tracker_api/routes/codes.py— CouponUsage routes (log code, redeem)src/mcd_tracker_api/routes/dashboard.py— Dashboard + admin statssrc/mcd_tracker_api/schemas.py— Pydantic request/response schemas (LocationCreate, LocationResponse, CodeCreate, CodeResponse, SlotStatus, DashboardResponse, AdminStats)tests/test_locations.py— location CRUD teststests/test_codes.py— code logging, redeem, slot limit teststests/test_dashboard.py— dashboard + admin stats testsFiles to modify:
src/mcd_tracker_api/main.py— register new routerstests/conftest.py— add auth override fixture (mock get_current_user for tests)Files NOT to touch:
src/mcd_tracker_api/auth.py— auth module is completesrc/mcd_tracker_api/models.py— models are completesrc/mcd_tracker_api/database.py— database layer is completeAcceptance Criteria
POST /locationscreates a location for the authenticated userGET /locationsreturns only the authenticated user's locationsPOST /locations/{id}/codeslogs a code withexpires_at = used_at + 30 daysPOST /locations/{id}/codesreturns 409 when 5 active codes exist at that locationnext_reopendate (MIN(expires_at) of active codes)GET /locations/{id}/codesreturns codes for a location (auth user only)PATCH /codes/{id}/redeemsets redeemed=True and redeemed_at=NOW()GET /locations/{id}/slotsreturns{slots_remaining: N, next_reopen: date|null}GET /dashboardreturns all user's locations with slot status in one responseGET /admin/statsreturns aggregate stats (total users, total codes, popular locations) — admin onlyGET /admin/statsreturns 403 for non-admin usersTest Expectations
test_locations.py— CRUD: create, list, list-empty, create-for-different-user-invisibletest_codes.py— log code, log 5 codes, log 6th → 409, redeem, redeem-already-redeemed → 400, expired-code-frees-slottest_dashboard.py— dashboard with mixed locations, admin stats, admin-only guardpytest tests/ -vConstraints
/healthzrequireget_current_user. Admin stats requiresrequire_role("admin")expires_at > func.now()in SQLAlchemy queries, not Python datetime comparisonChecklist
Closes #5Related
project-mcd-tracker— project pagephase-mcd-tracker-5-core-api— phase notearch-dataflow-mcd-tracker— runtime flow diagramsarch-domain-mcd-tracker— entity model + rolling window SQLplan-mcd-tracker— parent plan