Test Plan and Coverage - bounswe/bounswe2026group9 GitHub Wiki
Test Plan & Coverage
Testing Strategy
Our project has three main components: a Python/FastAPI backend, a Next.js (TypeScript) web frontend, and a Kotlin Android mobile app. Each has its own testing strategy tailored to its role.
Backend Testing Strategy
Our backend testing strategy is integration-first: all tests run against a real Supabase (PostgreSQL) database — no mocks for data access, no SQLite stubs. This ensures tests validate actual behavior including constraints, cascades, and query semantics. We mock only external side-effects (email sending, cloud storage uploads, Google OAuth callbacks) to keep tests deterministic and fast.
Test types used:
| Type | Purpose | Example |
|---|---|---|
| Unit tests | Isolated logic (hashing, JWT encode/decode, datetime validation) | TestPassword, TestJWT in test_auth_unit.py; TestValidateEventDatetime in test_event_unit.py; TestGetAffectedUserIds, TestEmitEventNotification in test_notification_emitter_unit.py |
| API integration tests | Full HTTP request → router → service → repository → DB round-trip | All endpoint tests (test_events.py, test_discovery.py, test_categories.py, test_images.py, test_comments.py, test_invites.py, test_notifications.py, test_attendances.py, test_bookmarks.py, test_users.py) |
| Multi-step flow tests | End-to-end user journeys spanning multiple endpoints | TestFullAuthFlow, TestRefreshTokenRotation, TestEmailVerificationFlow in test_auth_integration.py; test_accept_invite_grants_event_access, test_approve_grants_event_access in test_invites.py |
Tools & infrastructure:
- pytest as test runner (configured in
pyproject.toml) - FastAPI TestClient (backed by httpx) for HTTP-level testing
- ruff for static analysis / linting (runs before tests in CI)
- GitHub Actions CI (
backend-ci.yml) — triggered on every PR touchingbackend/**: checkout → Python 3.12 → install deps →ruff check .→pytest tests/ -v - Autouse fixtures for cleanup:
cleanup_test_usersdeletes all rows matching test prefixes (testuser_,eventtest_,imgtest_,cattest_,disctest_,attndtest_,bmtest_,cmttest_,invtest_,notiftest_,proftest_) after each test — no stale data between runs - Mocking:
unittest.mock.patchfor email sending, Supabase storage upload/delete, and Google OAuth token exchange
Frontend Testing Strategy (Next.js — MVP: Manual; Automated: Final Milestone)
For the MVP milestone, the web frontend was validated through manual testing and visual inspection after each feature implementation. Every user-facing flow was exercised on the deployed application, including edge cases such as private event access, underage gate behavior, invite token acceptance, and error page rendering. No automated test framework was introduced for the MVP.
Automated tests (Jest + React Testing Library + MSW + frontend-ci.yml GitHub Actions workflow) are planned for the Final Milestone.
MVP validation approach:
| Area | How validated for MVP |
|---|---|
| Auth screens | Manually tested registration, login, logout, duplicate email error, Google OAuth flow on live deployment |
| Event discovery | Manually verified map/list toggle, search, category and temporal filters, pagination, guest vs. authenticated card differences |
| Event detail | Manually tested full detail view, limited preview for guests, private event restricted view, age gate (underage block + DOB prompt), image carousel, comments, bookmark/going buttons |
| Event creation | Manually stepped through all 7 form steps, draft save, image upload, publish flow, pre-publish validation |
| Private event flows | Manually tested Request Access button, pending state persistence, host invite generation and copy, access request approve/reject |
| Profile & ratings | Manually tested host profile page, hosted events list, rating submission |
| Notifications | Manually verified in-app notification list, unread badge, mark-as-read, event update/cancellation triggers |
| Error pages | Manually navigated to non-existent URLs (404), private events (access denied), and 18+ events as underage user |
Planned automated test coverage for Final Milestone:
| Type | Purpose | Tool |
|---|---|---|
| Component tests | Individual UI components render with correct props and conditional logic | Jest + React Testing Library |
| User interaction tests | Simulate user actions (click, type, submit) and verify resulting UI changes | React Testing Library (fireEvent / userEvent) |
| Navigation tests | Screen transitions work correctly (e.g., login → discovery, event card → detail) | Next.js routing test utilities |
| API integration tests | API calls are made with correct parameters, responses are handled, errors are displayed | Jest with MSW (Mock Service Worker) |
| Snapshot tests | Catch unintended UI regressions across updates | Jest snapshots |
Planned CI: A frontend-ci.yml workflow will be added — triggered on PRs touching frontend/**: checkout → Node.js setup → npm install → eslint → jest --coverage.
Mobile Testing Strategy (Kotlin Android — MVP: Manual; Automated: Final Milestone)
For the MVP milestone, the Android app was validated through manual testing on emulator and physical device after each feature implementation. All screens and flows were exercised by hand, including edge cases such as the 18+ block, private event access request, invite deep link acceptance, and error page routing.
Automated tests (JUnit + MockK + Jetpack Compose Testing + android-ci.yml GitHub Actions workflow) are planned for the Final Milestone.
MVP validation approach:
| Area | How validated for MVP |
|---|---|
| Auth screens | Manually tested registration, login, logout, token persistence (session survives app restart) |
| Event discovery | Manually verified map/list view, search, category and temporal filters, FAB (create event, auth-only) |
| Event detail | Manually tested full detail, limited preview for guests, private event restricted view, age gate (underage + DOB required screens), image carousel, bookmark/going buttons, comments |
| Event creation | Manually stepped through all 7 form steps, date/time picker, image upload, draft save, publish, edit mode pre-fill, cancel and delete confirmation dialogs |
| Private event flows | Manually tested Request Access button with pending/rejected/approved state, host invite management panel, access request approve/reject |
| Notifications | Manually verified notification list, unread badge count in top bar, mark-as-read |
| Host profile | Manually tested host profile page, hosted events list, rating submission |
| Navigation | Manually tested all routes including deep link (sem://invite/{eventId}/{token}), back stack, auth guard |
Planned automated test coverage for Final Milestone:
| Type | Purpose | Tool |
|---|---|---|
| Unit tests | ViewModel logic, repository data mapping, input validation | JUnit 5 + MockK |
| UI tests | Screen rendering, user interactions, navigation | Jetpack Compose Testing + Espresso |
| API integration tests | Retrofit/OkHttp calls constructed correctly, responses parsed, errors handled | JUnit 5 + MockWebServer |
| Navigation tests | Screen transitions, back stack behavior, deep linking | Compose Navigation Testing |
Planned CI: An android-ci.yml workflow will be added — triggered on PRs touching mobile/**: checkout → JDK setup → Gradle build → detekt → ./gradlew test → ./gradlew connectedAndroidTest.
Test Plan Coverage
Backend — 209 tests across 17 test files
All tests currently implemented and passing. Organized by feature area:
1. Authentication & Authorization (36 tests — test_auth.py, test_auth_integration.py, test_auth_unit.py)
| Area | Tests | What is validated |
|---|---|---|
| Password hashing | 3 | bcrypt hash/verify, wrong password rejection, salt randomness |
| JWT tokens | 3 | Create/decode, invalid token rejection, expiry enforcement |
| Registration | 6 | Success (201), duplicate email/username (409), validation (short password, invalid email, short username — 422) |
| Login | 5 | Success (200), wrong password (401), non-existent email (401), account lockout after 5 failures (429), deactivated account (403) |
| Token flow | 7 | /me with valid/invalid/missing token, refresh via cookie, token rotation (old token revoked), logout, refresh-after-logout fails |
| Email verification | 4 | Verify valid token (200), invalid token (400), resend (200), resend-when-already-verified (400) |
| Integration flows | 5 | Full register→login→me→refresh→logout, refresh rotation, register→verify→confirm, resend→verify, expired token |
| Google OAuth | 3 | New user created, existing user linked, deactivated user rejected |
2. Event CRUD & Lifecycle (49 tests — test_events.py, test_event_unit.py)
| Area | Tests | What is validated |
|---|---|---|
| Create event | 12 | Draft creation (201), direct-publish blocked (400), venue metadata, equipment, multiple locations, invalid datetime/category (400), duplicates (409), auth required (403), missing fields (422) |
| Get event | 6 | Full detail for auth user, limited preview for guest, private event visibility (host vs. others), 404, auto-end on read |
| Update event | 6 | Title update (200), published→updated transition, non-host forbidden (403), cancelled event blocked (400), time change after start blocked (400), auth required |
| Status transitions | 7 | draft→published without image rejected (400), draft→published with image (200), published→cancelled/ended, invalid transitions (cancelled→published, draft→cancelled — 400), non-host forbidden |
| Delete event | 5 | Delete cancelled/ended (200), delete published blocked (400), non-host forbidden (403), auth required |
| Age restriction | 3 | Underage user blocked (403), missing DOB blocked, host exempt |
| Edge cases | 5 | Updated→cancelled/ended, invalid UUID (422), past start date rejected (400), clear attendee limit |
| Unit — datetime validation | 3 | Rejects past start by default, rejects end before start, allows past start when explicitly enabled |
| Unit — auto-end logic | 2 | Marks published past event as ended, leaves future event unchanged |
3. Event Discovery & Search (34 tests — test_discovery.py)
| Area | Tests | What is validated |
|---|---|---|
| Basic listing | 9 | 200 without auth, response shape (items/total/page/page_size/total_pages), published appears, draft hidden, private appears with limited info, past-end-datetime excluded, cancelled excluded, auth user gets full fields, guest vs. auth field differentiation |
| Sort order | 1 | Results sorted by start_datetime ascending |
| Text search | 5 | Search by title, by description, no results, case-insensitive, excludes non-matching |
| Category filter | 3 | Matching events returned, no-match returns empty, invalid UUID rejected (422) |
| Temporal filter | 5 | upcoming, today, this_week, far-future exclusion, invalid value rejected (422) |
| Pagination | 8 | Default (page=1, size=20), custom size, max 100, min 1, second page, total_pages calculation, empty beyond-total, page=0 rejected |
| Combined filters | 3 | search + category, search + temporal, all filters together |
4. Categories (9 tests — test_categories.py)
| Area | Tests | What is validated |
|---|---|---|
| List | 4 | Returns predefined (≥15), sorted alphabetically, search by name, search no results |
| Create | 5 | Custom category (201), duplicate fails (409), no auth (403), empty name (422), custom hidden until approved |
5. Image Management (10 tests — test_images.py)
| Area | Tests | What is validated |
|---|---|---|
| Upload | 7 | JPEG (201), PNG (201), invalid format rejected (400), non-host forbidden (403), max 10 limit (400), large image resized (≤2048×2048), auth required |
| Delete | 3 | Success (200), non-host forbidden (403), nonexistent (404) |
6. Comments (20 tests — test_comments.py)
| Area | Tests | What is validated |
|---|---|---|
| Create comment | 7 | Success (201), no auth (401), empty text (422), comment on draft event blocked, comment on cancelled event blocked, nonexistent event (404), host can comment own event |
| List comments | 7 | Empty list, list with data, newest-first ordering, pagination, no auth required to read, nonexistent event (404), includes user info (username) |
| Delete comment | 6 | Owner can delete (200), host can delete others' comments, other user cannot delete (403), nonexistent comment (404), no auth (401), wrong event context |
7. Invites & Access Requests (26 tests — test_invites.py)
| Area | Tests | What is validated |
|---|---|---|
| Create invite | 5 | Success (201), invite with options (max_uses/expires_at), non-host forbidden (403), public event rejected (400), no auth (401) |
| Accept invite | 6 | Success (200), invite grants event access (full detail now visible), invalid token (400), already-granted idempotent (200), host self-accept rejected (400), max uses reached (400) |
| List invites | 2 | Host can list (200), non-host forbidden (403) |
| Create access request | 5 | Success (201), request notifies host, duplicate blocked (409), public event rejected (400), host self-request rejected (400) |
| List access requests | 2 | Host sees pending requests (200), non-host forbidden (403) |
| Access request decision | 6 | Approve grants event access, approve confirms full detail visible, deny (200), deny keeps limited access, non-host forbidden (403), already-resolved blocked (400) |
8. Attendance & Capacity (1 test — test_attendances.py)
| Area | Tests | What is validated |
|---|---|---|
| Attendance flow | 1 | Full flow: mark going, capacity enforcement (second user blocked when limit=1), remove attendance, re-mark going after capacity freed |
9. Bookmarks (1 test — test_bookmarks.py)
| Area | Tests | What is validated |
|---|---|---|
| Bookmark flow | 1 | Create bookmark (201), list bookmarks, duplicate blocked (409), delete bookmark (200), list empty after deletion |
10. User Profiles & Ratings (1 test — test_users.py)
| Area | Tests | What is validated |
|---|---|---|
| Profile & rating flow | 1 | Get host profile, rate host (201), rating reflected in profile response, self-rating blocked (400) |
11. Notifications (20 tests — test_notifications.py, test_notification_emitter.py, test_notification_emitter_unit.py)
| Area | Tests | What is validated |
|---|---|---|
| List notifications | 6 | Empty list, returns items, pagination, ordered newest-first, unauthenticated blocked (401), only own notifications returned |
| Mark as read | 4 | Mark single (200), idempotent (already read → 200), not found (404), cannot mark other user's notification |
| Mark all as read | 3 | Marks all unread (200), no unread → no-op, only affects own notifications |
| Emitter integration | 4 | Event update notifies going user, empty update does not notify, cancel notifies going user, end does not notify |
| Emitter unit | 3 | Deduplicates attendees + bookmarkers, excludes host from recipients, returns zero when no non-host recipients |
12. Health Check (1 test — test_health.py)
| Area | Tests | What is validated |
|---|---|---|
| System | 1 | GET /health returns 200 with database connectivity confirmed |
Frontend (Next.js) — MVP: Manual Validation ✅ | Automated Tests: Final Milestone
For the MVP, all frontend functionality was validated manually on the deployed application after each feature was merged. Testers exercised every user flow end-to-end including edge cases (private events, underage gate, invite token, error pages). Automated test infrastructure (Jest + React Testing Library + frontend-ci.yml) will be introduced in the Final Milestone.
Estimated automated test total for Final Milestone: ~58 tests
| Area | Planned Tests | What will be validated |
|---|---|---|
| Auth screens | ~10 | Login/registration form validation, error messages (409, 401), token storage, redirect after login, Google OAuth |
| Event discovery | ~12 | List renders from API, search filters results, category/temporal chips, pagination, empty state, guest vs. auth card differences |
| Event detail | ~10 | Full detail for auth user, limited preview for guest, private restricted view, image carousel, Going/Bookmark buttons, comments, age gate |
| Event creation | ~8 | Multi-step form navigation, required field validation, image upload preview, draft save vs. publish flow |
| Profile | ~5 | Profile info renders, hosted/past event tabs, rating display, edit form |
| Notifications | ~4 | Notification list, unread badge, mark-as-read, empty state |
| Navigation & routing | ~5 | Protected routes redirect to login, back navigation, deep links, logout clears session |
| Error handling | ~4 | 404 page, access denied page, network error display, loading spinners |
Mobile (Kotlin Android) — MVP: Manual Validation ✅ | Automated Tests: Final Milestone
For the MVP, all Android app functionality was validated manually on an emulator and physical device after each feature was merged to main. All screens, flows, and edge cases were exercised by hand including deep links, the 18+ gate, private event access requests, and host action dialogs. Automated test infrastructure (JUnit + MockK + Jetpack Compose Testing + android-ci.yml) will be introduced in the Final Milestone.
Estimated automated test total for Final Milestone: ~58 tests
| Area | Planned Tests | What will be validated |
|---|---|---|
| Auth screens | ~10 | Login/registration validation, API error display, token persistence in DataStore, session restore on relaunch |
| Event discovery | ~12 | LazyColumn renders from ViewModel state, search triggers API call, filter chips update query, pagination, empty state, guest vs. auth card differences |
| Event detail | ~10 | Full detail composable, limited preview for guest, private restricted view, HorizontalPager image carousel, Going/Bookmark button calls, comment submission, age gate dialog |
| Event creation | ~8 | 7-step form navigation, required field validation, date/time pickers, image picker, venue metadata, draft save vs. publish |
| Profile | ~5 | Profile screen renders, tab switching, rating bar, edit profile |
| Notifications | ~4 | Notification list, unread badge, mark-as-read, empty state |
| Navigation | ~5 | NavHost routes, auth guard, deep link (sem://invite/{eventId}/{token}), back stack |
| Offline / error | ~4 | No-network snackbar, retry, 404/403 error screens, loading shimmer |
User Acceptance Criteria Mapping
Each MVP acceptance criterion is mapped to the specific tests that validate it:
| MVP Acceptance Criterion | Covered By | Pass? |
|---|---|---|
| Users can register and log in | TestRegister (6), TestLogin (5), TestFullAuthFlow (1) |
✅ |
| JWT access + refresh token flow works | TestTokenFlow (7), TestRefreshTokenRotation (1) |
✅ |
| Email verification works | TestEmailVerification (4), TestEmailVerificationFlow (3) |
✅ |
| Google OAuth sign-in works | TestGoogleOAuth (3) |
✅ |
| Users can create events with all required fields | TestCreateEvent (12) |
✅ |
| Event images can be uploaded and deleted | TestUploadImage (7), TestDeleteImage (3) |
✅ |
| Events require at least one image to publish | test_draft_to_published_no_image_rejected, test_draft_to_published_with_image |
✅ |
| Event lifecycle (draft → published → updated → cancelled/ended) | TestChangeEventStatus (7), TestUpdatedStatusTransitions (2) |
✅ |
| Only host can edit/delete/change status | test_update_not_host_forbidden, test_delete_not_host, test_status_change_not_host |
✅ |
| Guest users see limited event info | test_get_event_guest_limited, test_guest_sees_limited_info_auth_sees_full |
✅ |
| Private events visible but restricted | test_get_private_event_other_user_limited, test_private_event_appears_with_limited_info |
✅ |
| Age-restricted events enforced | TestAgeRestriction (3) |
✅ |
| GET /events returns paginated published events | TestListEventsBasic (9), TestListEventsPagination (8) |
✅ |
| Text search across title and description | TestListEventsSearch (5) |
✅ |
| Category filter works | TestListEventsCategoryFilter (3) |
✅ |
| Temporal filter works (upcoming/today/this_week) | TestListEventsTemporalFilter (5) |
✅ |
| Results sorted by start_datetime | test_sorted_by_start_datetime |
✅ |
| Past-end events excluded from discovery | test_past_end_datetime_not_in_listing |
✅ |
| Category management (predefined + custom) | TestListCategories (4), TestCreateCategory (5) |
✅ |
| Auto-end events when time passes | TestAutoEndEventIfPast (2) |
✅ |
| Comments — create, list, delete | TestCreateComment (7), TestListComments (7), TestDeleteComment (6) |
✅ |
| Invite generation and acceptance | TestCreateInvite (5), TestAcceptInvite (6), TestListInvites (2) |
✅ |
| Access request flow (request → approve/reject) | TestAccessRequests (5), TestAccessDecision (6) |
✅ |
| Attendance and capacity enforcement | test_attendance_and_capacity (1) |
✅ |
| Bookmark create and delete | test_create_bookmark (1) |
✅ |
| Host profile and ratings | test_user_profiles_and_ratings (1) |
✅ |
| Notifications sent on update/cancellation | TestUpdateNotification (2), TestCancelNotification (2) |
✅ |
| Notification list, mark-as-read | TestListNotifications (6), TestMarkNotificationAsRead (4), TestMarkAllAsRead (3) |
✅ |
| CI pipeline runs lint + tests on every PR | backend-ci.yml (ruff → pytest) |
✅ |
MVP Acceptability Criteria
The MVP is acceptable from a user perspective when these scenarios work end-to-end across both backend and frontend:
Backend (API-level validation — currently passing, 209 tests)
-
New user onboarding: A user registers, receives a verification email, verifies their account, and logs in — receiving a JWT that authenticates all subsequent actions.
-
Event creation flow: An authenticated user creates a draft event with title, description, category, location, date/time, and optional venue metadata. They upload at least one image, then publish. The event appears in discovery results.
-
Event discovery: Any user (guest or registered) can browse
GET /events, search by text, filter by category or time window, and paginate through results. Guests see limited fields; registered users see full details. Private events appear with restricted info. Past events are excluded. -
Event lifecycle management: A host can edit their published event (status becomes "updated"), cancel it (removed from discovery), or end it. Non-hosts are blocked from all management actions.
-
Access control enforcement: Guests cannot create/edit/delete events. Private event details are hidden from non-hosts at the API level. Age-restricted events block underage users. Account lockout activates after repeated failed logins.
-
Private event invite & access flow: A host can generate invite links for their private event. Non-host users can request access. The host can approve or reject requests; approval grants full event detail access. Invite token acceptance also grants access.
-
Social features: Authenticated users can comment on events, bookmark them, mark themselves as going (subject to capacity), and rate the event host. All actions are auth-protected and host-restricted where applicable.
-
Notifications: Users receive in-app notifications when events they bookmarked or are going to are updated or cancelled. The notification list supports pagination, mark-as-read, and mark-all-as-read.
Frontend (UI-level validation — manually verified for MVP)
-
Visual completeness: All screens are implemented and match the design system. Manually verified on live deployment after each feature merge.
-
Auth UI flow: Registration, login, logout, Google OAuth, and validation errors all work as expected in the browser.
-
Discovery UI flow: Map/list toggle, search, filters, pagination, and guest vs. authenticated view differences all verified manually.
-
Event detail UI flow: Full detail, limited preview, private restricted view, age gate, image carousel, comments, and attendance buttons all verified manually.
-
Event creation UI flow: All 7 form steps, draft save, image upload, and publish flow verified manually.
-
Private event & invite flows: Request Access button, pending state, host invite panel, access request management, and invite acceptance page all verified manually.
-
Responsive layout: Core pages verified on mobile screen widths (NFR-08).
Mobile (UI-level validation — manually verified for MVP)
-
Android auth flow: Registration, login, session persistence, and token restore on app relaunch verified on emulator.
-
Android discovery flow: Map/list view, search, filters, FAB (auth-only), and pagination verified manually.
-
Android event detail flow: Full detail, limited preview, private restricted view, 18+ gate (underage screen + DOB required screen), image carousel, Going/Bookmark, comments all verified manually.
-
Android event creation flow: All 7 steps including date/time pickers, image upload, venue metadata, draft save, publish, edit mode (pre-fill), cancel and delete dialogs — all verified manually.
-
Android private event flows: Request Access with pending/rejected/approved states, host invite management panel, access request approve/reject — all verified manually.
-
Android deep linking:
sem://invite/{eventId}/{token}deep link opens correct invite acceptance screen — verified manually.