Milestone Review - bounswe/bounswe2026group9 GitHub Wiki
Project Summary
Social Event Mapper reached its MVP milestone as a fully functional, three-platform application: a FastAPI backend deployed on EC2, a Next.js web frontend, and a Kotlin/Jetpack Compose Android app. All core event lifecycle and social features were implemented, tested on the deployed system, and presented in the demo.
Key decisions made during development:
- Adopted an integration-first testing strategy for the backend: all tests run against a real Supabase PostgreSQL database rather than mocks. This caught several constraint and cascade behaviors that would not surface with SQLite stubs.
- Chose Supabase as the hosted database rather than self-managing PostgreSQL to reduce infrastructure overhead and take advantage of built-in row-level security.
- Used Jetpack Compose for the Android UI throughout rather than the legacy XML view system, which required more initial setup but paid off in cleaner state management with ViewModels.
- Implemented JWT access + refresh token rotation on the backend so mobile clients can stay authenticated across sessions without re-entering credentials.
- Separated event status transitions (draft β published β updated / cancelled / ended) into an explicit state machine enforced at the service layer, preventing invalid transitions from reaching the database.
- Decided to skip automated frontend and mobile tests for the MVP milestone and rely on manual validation of the deployed app; automated test suites (Vitest + React Testing Library for web, JUnit + MockK + Compose Testing for Android) are planned for the Final Milestone.
Summary of customer feedback received:
The demo was presented to the Suzan ΓskΓΌdarlΔ± and all teaching assistants.
During the demo: The instructor responded positively to the scenario β a nature walk bird-watching event β and in particular smiled at the "elementary school friendship" detail woven into the narrative. During the mobile demo, she reminded one of the presenter to face the audience and speak louder.
Post-demo feedback from the instructor: She suggested expanding the review/rating system into a more detailed feedback mechanism. Specifically, she proposed that attendees could be asked to fill in structured reviews after an event, including scales for physical difficulty (e.g., how strenuous a walk was), an overall event rating, and free-text fields for hardships encountered or advice for the event organizer. This would make the platform more useful for recurring community events where past-attendee experience guides future participants.
TA feedback: One of the TAs suggested implementing a location search feature during event creation β allowing hosts to search for a venue by name rather than placing a pin manually, noting it would be a useful UX improvement. Another TA praised the equipment/materials feature, specifically noting that the "must bring" list (demonstrated via the "bring binoculars" warning in the bird-watching scenario) was well thought out and successfully implemented.
At the close of the session, one of the TA also requested that clicking on a user's avatar/icon anywhere in the app should navigate to that user's profile page β a navigation shortcut not yet implemented for all pages at MVP.
Which elicitation questions were most helpful:
Six question clusters produced the clearest signal for requirements:
-
Q 3.3.1β3.3.3 (visibility modes and private event access) β asking explicitly whether private events should be hidden from discovery entirely or shown with limited info, and whether access should use invite links, access codes, or request-to-join, forced a concrete answer that drove one of the most complex feature areas in the system.
-
Q 2.1.1β2.1.2 (mandatory fields for publishing, including cover image) β distinguishing between fields required to save a draft versus fields required to publish gave us the draft/publish separation that underlies the entire event creation flow.
-
Q 9.2.2β9.2.3 (cancelled event behavior: removed from discovery vs. still visible with label) β asking these as two separate questions rather than one revealed that both behaviors are required simultaneously, not as alternatives.
-
Q 2.4.1 + 2.4.5 (multi-location with one designated primary) β asking whether a single event can span multiple venues and whether one must be flagged as primary directly shaped the locations array data model and map pin display logic.
-
Q 6.3.1 (host profile and rating eligibility) β asking specifically who is eligible to rate a host (any user vs. only attendees of an ended event) produced the eligibility rule that became one of the more non-trivial enforcement points in the system.
-
Q 3.1.1β3.1.2 (roles and per-role permissions) β mapping out guest, registered user, and host roles and which actions each can perform gave us a clear permission matrix before any implementation started.
What responses transformed requirements:
-
Q 3.3.2 β private events visible in discovery with limited info β The answer that private events must still appear in map and list views (with a "Private" badge and no detail) rather than being hidden completely transformed the access control design from a simple show/hide into a three-tier visibility model: full detail (authorized), limited preview (unauthorized), hidden (cancelled/draft). This drove conditional rendering logic on both web and mobile for the Request Access / Pending / Denied / Full Detail access states.
-
Q 2.1.1 β cover image mandatory for publish β The answer that at least one cover image is required to publish (not just recommended) transformed event creation from a single-step form into a two-phase flow: create a draft without an image, upload images, then publish. The backend enforces this at the status transition level β draft β published is blocked without β₯1 image (FRS-2.1.1).
-
Q 9.2.2 + 9.2.3 combined β dual cancelled event behavior β The answers together β remove from discovery and keep the event page visible with a cancellation label β created a requirement more specific than either answer alone. This became FRS-2.3.3 and FRS-2.3.4, requiring the backend to exclude cancelled events from
GET /eventsresults while still returning them onGET /events/{id}with their cancelled status. -
Q 3.3.3 β both invite link and request-to-join required β The answer that private access must support both token-based invites (host generates a shareable link) and user-initiated access requests (host approves individually) transformed a single access mechanism into a dual system. Each path has independent state (invite: max_uses, expires_at; request: pending/approved/denied), and both grant equivalent access once resolved.
-
Q 6.3.1 β rating eligibility gated on attendance of an ended event β The answer that only users who attended an ended event hosted by that host may rate transformed the rating endpoint from a simple write into one with a prerequisite check (
has_attended_ended_event_by_host). This also added thecan_ratefield to the host profile response so clients can hide the rating UI proactively for ineligible users.
| # | Deliverable | Status | Links |
|---|---|---|---|
| D1 | Software Requirements Specification | Completed | Requirements Β· Elicitation Questions Β· Glossary |
| D2 | Software Design (UML Diagrams) | Completed | Class Diagram Β· Use Case (Final) Β· Sequence (Final) |
| D3 | Scenarios and Mockups | Completed | Scenario 1 Β· Scenario 2 Β· Scenario 3 |
| D4 | Project Plan, Communication Plan, RAM | Completed | Implementation Plan Β· Test Plan Β· Communication Plan Β· RAM Β· Team Policies |
| D5 | Pre-release software | Completed | Release 0.1.0-alpha (includes debug APK) |
| D5.1 | Backend (FastAPI) | Completed | Deployed to EC2 at https://thesocialeventmapper.social; auth, events, discovery, comments, bookmarks, attendance, invites, access requests, notifications, host profiles, ratings |
| D5.2 | Web Frontend (Next.js) | Completed | Deployed at https://thesocialeventmapper.social; all MVP screens implemented and manually validated |
| D5.3 | Android Mobile App | Completed | Debug APK built via GitHub Actions and attached to release; all MVP screens implemented and manually validated on emulator and device |
| D5.4 | CI / DevOps | Completed |
backend-ci.yml (lint + tests on every PR), android-build.yml (APK on push/release), deploy.yml (auto-deploy to EC2 on push to main) |
| D6 | Milestone Review | Completed | Milestone Review |
| D7 | Individual Contributions | Completed | Individual Contributions |
Feature delivery status:
| Feature Area | Web | Mobile | Backend |
|---|---|---|---|
| Auth & User Management (register, login, JWT, refresh, email verification, Google OAuth, account lockout) | β | β | β |
| Event Creation & Lifecycle (7-step form, draft/publish/update/cancel/end, venue metadata, equipment) | β | β | β |
| Image Upload & Management (upload, carousel, delete, 10-image limit, resize) | β | β | β |
| Event Discovery (list/map view, search, category/temporal filters, pagination) | β | β | β |
| Event Detail & Access Control (full detail, limited preview, private restricted, age gate) | β | β | β |
| Private Event Flows (invite generation, invite acceptance, access request, approve/reject) | β | β | β |
| Bookmarking | β | β | β |
| Attendance (going/interested, capacity enforcement) | β | β | β |
| Comments (create, list, delete, replies) | β | β | β |
| Host Profile & Ratings (eligibility: must have attended an ended event by the host) | β | β | β |
| Notifications (event update/cancellation triggers, list, mark-as-read, unread badge) | β | β | β |
| Category Management (predefined list, custom category creation) | β | β | β |
Reflection on deliverable status and impact on project plan:
The full MVP feature set was delivered on schedule. The most impactful planning decision was treating the backend as the single source of truth for all business logic (access control, lifecycle transitions, capacity enforcement) and building both the web and mobile clients against a single deployed API. This reduced duplication of logic and kept client code focused on presentation.
Two areas required more time than planned: the private event invite/access request flow (more edge cases than anticipated β host self-accept, max uses, already-granted idempotency) and the mobile age gate (required device-level DOB storage and conditional navigation). Both were resolved before the demo.
UX design with focus on domain-specific features:
The UI design centers on two domain-specific interaction patterns:
-
Event lifecycle as a first-class concept: Events surface their status (draft, published, updated, cancelled, ended) prominently in all views. The creation flow uses a 7-step wizard that mirrors how event organizers think about planning β basic info β date/time β location β accessibility/venue β categories β images β review. The host receives explicit confirmation at each step and cannot accidentally publish without at least one image.
-
Access control transparency: Private events appear in discovery with a clear "Private" badge and limited info rather than disappearing entirely. The web and mobile clients expose the correct action per user state β Request Access (never requested), Pending (awaiting decision), Access Denied, or Full Detail (approved/invited). Age-restricted events show a date-of-birth collection screen on first encounter rather than a plain error, reducing friction for eligible users.
API documentation with examples:
Full interactive API documentation is available at https://thesocialeventmapper.social/docs (Swagger UI, auto-generated from FastAPI annotations).
Selected endpoint examples:
POST /auth/register
{
"username": "mehmetkaya",
"email": "[email protected]",
"password": "securepassword",
"date_of_birth": "1995-06-15"
}
β 201: { "user": {...}, "access_token": "...", "token_type": "bearer" }
POST /events
Authorization: Bearer <token>
{
"title": "Bosphorus Bird Walk",
"description": "...",
"start_datetime": "2026-04-12T09:00:00Z",
"end_datetime": "2026-04-12T12:00:00Z",
"visibility": "public",
"is_age_restricted": false,
"category_ids": ["<uuid>"],
"locations": [{ "name": "Bebek Park", "latitude": 41.077, "longitude": 29.044, "is_primary": true, "order_index": 0 }]
}
β 201: { "id": "...", "status": "draft", ... }
GET /events?search=bird&temporal_filter=upcoming&page=1&page_size=20
β 200: { "items": [...], "total": 3, "page": 1, "page_size": 20, "total_pages": 1 }
The following requirements from the Software Requirements Specification were addressed in this milestone. Requirements are referenced by their SRS identifiers (FRU = Functional User Requirements, FRS = Functional System Requirements, NFR = Non-Functional Requirements).
| Requirement | Description | Status |
|---|---|---|
| FRU-1.1 | Registered users can create community events | β |
| FRU-1.1.1 | Events require title, description, category, start/end datetime | β |
| FRU-1.1.2 | Users can upload one or more images for an event | β |
| FRU-1.1.3 | Users can edit events they created | β |
| FRU-1.1.3.1 | Only the host can delete their event; deletion requires cancelling or ending first | β |
| FRU-1.1.3.2 | Host changes result in either event updated or event cancelled | β |
| FRU-1.1.4 | Events can be set public or private | β |
| FRU-1.1.4.1 | Private events appear in browse/search with limited info; full detail restricted to host | β |
| FRU-1.1.5 | Events classified by selecting from a predefined category catalog | β |
| FRU-1.1.5.1 | Users can create a new category entry if no suitable predefined option exists | β |
| FRU-1.1.6 | Users can specify event location | β |
| FRU-1.1.6.1 | Users can specify multiple ordered locations (route/path) | β |
| FRU-1.1.7 | Users can mark an event as 18+ (age-restricted) | β |
| FRU-1.1.8 | Users can set an optional attendee limit (capacity) | β |
| FRU-2.1 | Users can browse/discover events via map view and list view | β |
| FRU-2.1.1 | Users can filter events by category and date/time | β |
| FRU-2.1.2 | Users can search for events starting after a specified time | β |
| FRU-3.1 | Registered users can view event detail pages for public events | β |
| FRU-3.1.1 | Users can bookmark events to view later and receive notifications | β |
| FRU-3.1.2.1 | Users can comment under an event | β |
| FRU-3.1.3 | Users can rate the event host | β |
| FRU-4.1 | Unregistered users cannot view event detail pages | β |
| FRU-4.2 | Unregistered users can only browse events in map/list with limited info | β |
| FRU-4.3 | Private events visible in browse/search but full details restricted to host | β |
| FRU-5.1 | Users can view host profile pages | β |
| FRU-5.1.1 | Host profiles show contact info per privacy settings | β |
| FRU-5.1.2 | Host profiles show hosted events (past and upcoming) | β |
| FRU-5.1.3 | Host profiles show ratings | β |
| FRU-6.1 | Users receive notifications when bookmarked/followed events are updated or cancelled | β |
| FRU-6.1.1 | Notifications sent on every edit (updated) and cancellation | β |
| Requirement | Description | Status |
|---|---|---|
| FRS-1.1 | System supports guest access without registration | β |
| FRS-1.2 | System allows users to register, sign in, and sign out | β |
| FRS-1.2.1β1.2.6 | Auth required for: publish, edit/cancel, comment, bookmark, going; actions associated with user account | β |
| FRS-1.3 | Roles of guest, registered user, and admin enforced (guest + registered for MVP) | β |
| FRS-2.1 | System validates event data before publishing | β |
| FRS-2.1.1 | Required fields enforced before publish: title, description, category, times, location, β₯1 image | β |
| FRS-2.1.2 | End time must be later than start time | β |
| FRS-2.1.3 | Publishing blocked if mandatory fields missing or invalid | β |
| FRS-2.2 | System manages event lifecycle states | β |
| FRS-2.2.1 | States supported: draft, published, updated, cancelled, ended | β |
| FRS-2.2.2 | System automatically marks events as ended once end time has passed | β |
| FRS-2.3 | System regulates editing and cancellation behavior | β |
| FRS-2.3.1 | Hosts can edit event details before the event starts | β |
| FRS-2.3.2 | Modification of time/location blocked after event has started | β |
| FRS-2.3.3 | Cancelled events removed from discovery results | β |
| FRS-2.3.4 | Cancelled event page remains accessible with cancellation label | β |
| FRS-2.4 | Duplicate event creation by same host (same title/time/location) prevented | β |
| FRS-2.5 | Optional venue metadata supported | β |
| FRS-2.5.1 | Health requirements, age restriction, language fields available | β |
| FRS-2.5.2 | Accessibility features: wheelchair, restroom, elevator, seating, captions, quiet-friendly | β |
| FRS-2.6 | System manages event categories | β |
| FRS-2.6.1 | Predefined category catalog provided | β |
| FRS-2.6.2 | Hosts must assign at least one category when publishing | β |
| FRS-2.6.3 | Hosts can create custom categories (pending admin approval) | β |
| FRS-3.1 | Public and private visibility modes supported | β |
| FRS-3.1.1 | Private events appear in discovery with limited preview | β |
| FRS-3.1.2 | Full private event details restricted to host | β |
| FRS-3.1.3 | Private event access control enforced at the backend level | β |
| FRS-3.2 | 18+ events labelled; interaction restricted for underage users | β |
| FRS-4.1 | Both map and list views provided for discovery | β |
| FRS-4.1.1 | Users can switch between map and list views | β |
| FRS-4.2.1 | Events prioritised by proximity in time | β (sorted by start_datetime) |
| FRS-4.3.1 | Filtering by custom startβend time window supported | β |
| FRS-4.3.2 | Quick filters: upcoming, today, this_week | β |
| FRS-5.1 | Registered users can bookmark events | β |
| FRS-5.2 | Registered users can mark events as Going | β |
| FRS-5.2.1 | Hosts can define optional attendee capacity | β |
| FRS-5.2.2 | Going blocked once capacity limit reached | β |
| FRS-5.3 | Users can remove Bookmark or Going status at any time | β |
| FRS-6.1 | Registered users can comment on events | β |
| FRS-6.2 | Comments displayed publicly on event page | β |
| FRS-6.3 | Host profile pages provided | β |
| FRS-6.3.1β6.3.3 | Hosted events, ratings, and contact info (per privacy settings) displayed | β |
| FRS-7.1 | Users notified when bookmarked/going events are updated | β |
| FRS-7.2 | Users notified immediately when such events are cancelled | β |
| FRS-7.3 | Notifications delivered via in-app messaging | β |
| FRS-8.1 | Hosts can define multiple locations for an event; one designated primary | β |
| FRS-8.3 | Hosts can specify required and optional equipment/materials | β |
| FRS-10.1 | Past events retained and marked ended once end time passes | β |
| FRS-10.2 | Past events viewable on event detail and host profile pages | β |
| FRS-10.3 | Past events excluded from default discovery results | β |
Requirements deferred to Final Milestone: FRU-1.1.2.1 (online events), FRU-2.1.1.1 (GPS discovery), FRU-3.1.2.2 (P2P messaging), FRS-4.2.2 (distance sort), FRS-4.3.3 (accessibility filters), FRS-4.4 (GPS/default map area preferences), FRS-8.2 (itinerary segments), FRS-9 (reporting & abuse), NFR-01 (2s search), NFR-02 (60s update visibility), NFR-06 (10k events), NFR-07 (structured logging).
| Requirement | Description | Status |
|---|---|---|
| NFR-03 | HTTPS only for all client-server communication | β (production deployment) |
| NFR-04 | Backend does not return private event details to unauthorised users, even via direct API calls | β |
| NFR-08 | Core pages work on mobile screen widths without broken layout | β (manually verified) |
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.
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
The web frontend uses Vitest + React Testing Library for automated unit and component tests covering the auth and routing layer (16 tests, all passing). All other user-facing feature flows were validated through manual testing and visual inspection on the deployed application, including edge cases such as private event access, underage gate behavior, invite token acceptance, and error page rendering.
Additional automated coverage (event discovery, event detail, creation flow, notifications) is planned for the Final Milestone with a frontend-ci.yml GitHub Actions workflow.
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 | Vitest + 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 | Vitest with MSW (Mock Service Worker) |
| Snapshot tests | Catch unintended UI regressions across updates | Vitest snapshots |
Planned CI: A frontend-ci.yml workflow will be added β triggered on PRs touching frontend/**: checkout β Node.js setup β npm install β eslint β vitest --coverage.
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.
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: 16 automated + manual validation | Additional automation: Final Milestone
For the MVP, the auth and routing layer has 16 Vitest tests covering login, registration, protected routes, guest-only routes, and session management. All other feature flows (event discovery, detail, creation, notifications) were validated manually on the deployed application after each feature was merged. Additional automated test infrastructure (Vitest + React Testing Library + MSW + frontend-ci.yml) will be introduced in the Final Milestone.
Estimated additional automated test total for Final Milestone: ~42 tests (total ~58)
| 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 |
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 |
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) |
β |
Full test reports page: GitHub Wiki β Test Reports
Backend API (209 tests, 0 failures):
- XML Report: backend-junit.xml
- CI Run History: GitHub Actions β backend-ci.yml
Web App (16 automated tests, 0 failures):
- XML Report (Vitest): frontend-junit.xml
- Test files:
guest-only-route.test.tsx(2),login-page.test.tsx(4),protected-route.test.tsx(6),register-page.test.tsx(2),session.test.ts(2)
Mobile App (manual validation for MVP; automated reports: Final Milestone):
- Manual validation details: GitHub Wiki β Test Reports
- Automated test suite (JUnit + MockK + Compose Testing): planned for Final Milestone
Coverage:
The 209 backend tests cover every MVP functional requirement end-to-end β from HTTP request through business logic to the real PostgreSQL database. Every endpoint in the API has at least one positive path and one negative/error path test. Multi-step flow tests (e.g., register β login β create event β publish β cancel β delete) exercise integration between modules that unit tests cannot catch. The integration-first approach means that passing tests on CI give high confidence that the deployed API behaves correctly.
Frontend and mobile coverage for the MVP was achieved through manual validation on the live deployment. While this is less reproducible than automated tests, it exercised the full UI layer including rendering, navigation, and API contract correctness from the client side.
Bug Detection:
During the testing cycle, the following bugs were found and fixed as a direct result of test failures or manual validation:
-
Comment replies not displayed on mobile β The Gson deserializer silently dropped
parent_idandrepliesfields fromCommentDtobecause they were not declared in the data class. Adding the fields toCommentDtofixed the display of nested replies. Caught during manual mobile testing against the live API. -
Host rating eligibility not enforced β Initial implementation allowed any authenticated user to rate any host. Tests revealed no attendance check was in place. Fixed by adding
has_attended_ended_event_by_host()check in the rating service; thecan_ratefield was added to the host profile response so clients can hide the rating UI proactively. -
Event publish redirect to edit page β After publishing an event via the creation flow, the web app redirected to
/events/{id}/editinstead of/event/{id}. Caught during manual web testing and fixed by correcting the router.replace call. -
Past date not rejected on event creation form β The create event form allowed selecting past dates via the datetime-local input. Fixed by adding
minattribute to the input and adding explicit validation invalidateStep. -
Map view exposed age-restricted/private event actions β The event popup on the map view showed Going and Request Access buttons without applying the age restriction or private event access checks that the full detail page enforces. Fixed by removing those action buttons from the map popup entirely.
Readiness:
The backend is production-ready from a testing standpoint: 209 tests pass on every CI run against the real database, covering all MVP scenarios. The web and mobile frontends are functionally complete and manually validated. The main gap is the absence of automated frontend and mobile tests, which are scoped for the Final Milestone. The existing CI pipeline (lint + backend tests on every PR, APK build and deploy on merge) provides a solid foundation to add frontend and mobile automation.
How the team organized and managed the project:
The team used GitHub Projects (kanban board) to track tasks, with issues created for each feature and bug. Pull requests were required for all changes to main β direct commits to main were blocked by branch protection rules. All PRs required at least one review before merge. Commit messages followed the Conventional Commits format (feat:, fix:, docs:, chore:, refactor:).
Work was divided by component ownership while maintaining cross-component awareness:
-
Backend: centralized, with all team members contributing features to the FastAPI application organized by feature module (
app/routers/,app/services/,app/repositories/) - Frontend (web): Next.js App Router with per-feature component directories
-
Mobile (Android): Jetpack Compose with per-screen ViewModels in
ui/directories
The team communicated primarily via a group chat and weekly sync meetings. Issues were filed on GitHub for tracking; feature branches were named feat/<short-description> and fix/<description>.
Evaluation of tools and processes:
- GitHub Projects worked well for visualizing work in progress but was less useful for estimating capacity. Transitioning to story-point estimates before the Final Milestone would improve sprint planning.
- Conventional Commits improved the readability of the git log and made it straightforward to produce changelogs and PR descriptions.
- Integration-first backend testing (against a real Supabase DB) added test setup complexity but paid for itself by catching constraint violations and cascade behaviors that a mock DB would not have surfaced.
- Manual frontend/mobile validation was sufficient for MVP but will not scale to the Final Milestone feature set. Automated test suites are a priority for the next sprint.
- GitHub Actions CI gave the team immediate feedback on regressions β the backend CI workflow blocked two PRs that introduced test failures before they reached main.
Links to project plan and process artifacts:
- GitHub Projects board: https://github.com/orgs/bounswe/projects/146
- Backend CI workflow: .github/workflows/backend-ci.yml
- Android build workflow: .github/workflows/android-build.yml
- Deploy workflow: .github/workflows/deploy.yml