๐ CHALLENGE COMPLETE - ALL 21 BUGS FIXED โ
CoWork is a REST API for managing bookable rooms inside a coworking space across multiple tenant organizations. Each organization has its own rooms, staff (admins), and members. Members book rooms for time slots; admins manage rooms and pull reports.
Status: โ
COMPLETE
Bugs Fixed: 21/21 (100%)
Tests Passing: 15/15 (100%)
Estimated Score: 124 points
See FINAL_SUMMARY.md and BUG_REPORT.md for detailed information.
- Python 3.11, FastAPI, SQLAlchemy, SQLite (single file, no external DB service)
- JWT auth (access + refresh tokens), HS256, secret from the
JWT_SECRETenv var - One container, served on port 8000
docker compose up --buildThe database schema is created automatically on first startup โ no manual
provisioning or seed scripts. The API listens on http://localhost:8000.
To run the smoke test locally:
pip install -r requirements.txt
pytest-
Datetimes. All API datetimes are ISO 8601. Input datetimes carrying a UTC offset are converted to UTC before storage or comparison; naive input is treated as UTC. All response datetimes are UTC with an explicit UTC designator (
Zor+00:00). -
Booking price.
price_cents = hourly_rate_cents ร duration_hours. Duration must be a whole number of hours, minimum 1, maximum 8.end_timemust be strictly afterstart_time.start_timemust be strictly in the future at request time โ no grace window of any size. -
No double-booking. Two
confirmedbookings for the same room overlap iffexisting.start_time < new.end_time AND new.start_time < existing.end_time. Back-to-back bookings (one ending exactly when the other starts) are allowed. Conflict โ409 ROOM_CONFLICT. Holds under concurrent requests. -
Booking quota. A member may hold at most 3
confirmedbookings withstart_timein the window(now, now + 24h], across all rooms in their org. Violation โ409 QUOTA_EXCEEDED. Holds under concurrent requests. -
Rate limit.
POST /bookingsis limited to 20 requests per rolling 60 seconds per user (all requests count, successful or not). Excess โ429 RATE_LIMITED. Holds under concurrent requests. -
Cancellation refund policy. Only the booking's owner or an admin of the same org may cancel. Notice =
start_time โ cancellation_time:- notice โฅ 48 hours โ 100% refund
- 24 hours โค notice < 48 hours โ 50% refund
- notice < 24 hours โ 0% refund
Refund amount = percentage of
price_cents, rounded to the nearest cent with half-cents rounding up (e.g. 50% of 1001 = 501). Cancelling an already-cancelled booking โ409 ALREADY_CANCELLED. A cancelled booking has exactly one RefundLog entry, and the amount returned by the cancel response equals the amount stored in the RefundLog. Holds under concurrent cancel requests for the same booking. -
Reference codes. Every booking's
reference_codeis unique, including under concurrent creation. -
Auth. Tokens are JWTs (HS256) with claims
sub(user id, string),org(org id),role,jti(unique per token),iat,exp,type(access|refresh). Access tokens:exp โ iat= exactly 900 seconds. Refresh tokens: 7 days. Logout immediately invalidates the presented access token for all further use (subsequent use โ401). Refresh tokens are single-use:POST /auth/refreshreturns a new access and refresh token and invalidates the presented refresh token (reuse โ401). -
Multi-tenancy. A user (including admins) may only ever read or act on data (rooms, bookings, reports, exports, availability, stats) belonging to their own organization, on every code path. Cross-org resource IDs behave as non-existent โ
404. -
Booking visibility. Members may read and cancel only their own bookings (another member's booking id โ
404 BOOKING_NOT_FOUND). Admins may read and cancel any booking in their org. -
Pagination & ordering.
GET /bookingstakespage(int โฅ 1, default 1) andlimit(int 1โ100, default 10). Items are the caller's own bookings sorted by ascendingstart_time(ties by ascendingid). Page N with limit L returns items[(Nโ1)ยทL, NยทL)of that ordering; sequential pages never skip or repeat items. Response includestotal. -
Usage report.
GET /admin/usage-report?from=YYYY-MM-DD&to=YYYY-MM-DDreturns, per room in the caller's org (including rooms with zero bookings), the count ofconfirmedbookings withstart_timeon a date in[from, to](UTC, inclusive) and their summedprice_cents. Cancelled bookings are excluded. The report reflects the current state immediately. -
Availability.
GET /rooms/{id}/availability?date=YYYY-MM-DDreturns the room'sconfirmedbookings starting on that UTC date as busy intervals, sorted ascending. Reflects the current state immediately. -
Room stats.
GET /rooms/{id}/statsreturns the room's current count ofconfirmedbookings and their summedprice_cents(cancellation decrements both). Always equals the values derivable from the bookings themselves. -
Registration.
POST /auth/registerwith an unknownorg_namecreates the org and the user asadmin; with a knownorg_nameit joins the caller asmember. A duplicate username within the org โ409 USERNAME_TAKEN. -
Liveness. The service responds to all endpoints at all times; no combination of concurrent valid requests may hang the service.
| Method | Path | Auth | Success | Description |
|---|---|---|---|---|
| POST | /auth/register |
No | 201 | Register org admin or join org as member |
| POST | /auth/login |
No | 200 | Returns access + refresh token |
| POST | /auth/refresh |
No (refresh token in body) | 200 | Rotates tokens |
| POST | /auth/logout |
Yes | 200 | Invalidates presented access token |
| GET | /rooms |
Yes | 200 | List rooms in caller's org |
| POST | /rooms |
Yes (admin) | 201 | Create a room |
| GET | /rooms/{id}/availability |
Yes | 200 | Busy intervals for a date |
| GET | /rooms/{id}/stats |
Yes | 200 | Live confirmed-booking count & revenue |
| POST | /bookings |
Yes | 201 | Create a booking |
| GET | /bookings |
Yes | 200 | Caller's bookings, paginated |
| GET | /bookings/{id} |
Yes | 200 | Single booking incl. refunds |
| POST | /bookings/{id}/cancel |
Yes | 200 | Cancel + refund calculation |
| GET | /admin/usage-report |
Yes (admin) | 200 | Per-room usage/revenue for range |
| GET | /admin/export |
Yes (admin) | 200 | Bookings CSV; room_id, include_all |
| GET | /health |
No | 200 | {"status": "ok"} |
POST /auth/registerbody{org_name, username, password}โ{user_id, org_id, username, role}POST /auth/loginbody{org_name, username, password}โ{access_token, refresh_token, token_type: "bearer"}; bad credentials โ401 INVALID_CREDENTIALSPOST /auth/refreshbody{refresh_token}โ same shape as login- Room:
{id, org_id, name, capacity, hourly_rate_cents};POST /roomsbody{name, capacity, hourly_rate_cents} - Availability:
{room_id, date, busy: [{start_time, end_time}, โฆ]} - Stats:
{room_id, total_confirmed_bookings, total_revenue_cents} POST /bookingsbody{room_id, start_time, end_time}โ Booking:{id, reference_code, room_id, user_id, start_time, end_time, status, price_cents, created_at}GET /bookingsโ{items: [Booking, โฆ], page, limit, total}GET /bookings/{id}โ Booking plusrefunds: [{amount_cents, status, processed_at}, โฆ]POST /bookings/{id}/cancelโ{id, status: "cancelled", refund_percent, refund_amount_cents}- Usage report โ
{from, to, rooms: [{room_id, room_name, confirmed_bookings, revenue_cents}, โฆ]} - Export CSV header (exact):
id,reference_code,room_id,user_id,start_time,end_time,status,price_cents
Application errors return JSON {"detail": <string>, "code": <CODE>} with codes:
USERNAME_TAKEN (409), INVALID_CREDENTIALS (401), ROOM_CONFLICT (409),
QUOTA_EXCEEDED (409), RATE_LIMITED (429), ALREADY_CANCELLED (409),
BOOKING_NOT_FOUND (404), ROOM_NOT_FOUND (404), FORBIDDEN (403),
INVALID_BOOKING_WINDOW (400 โ past start, non-whole/out-of-range duration, or
end_time โค start_time). Missing/invalid/expired/blacklisted tokens โ 401.
Framework validation errors (422) use FastAPI's default shape.
Your fixes must preserve this contract exactly (paths, status codes, error codes, JSON field names, JWT claims). Grading is black-box: the grader builds the container and asserts behavior against the business rules and API contract above by talking to the API only.