# TesterStay · Junior QA interview

**Duration: 60 minutes.** Test a small Prague accommodation booking product on the web, through its API in Postman, and on iOS. All people and reservations are fictional. Some behavior may differ from the requirements below. Report what you can prove; you do not need to find every defect or fix code.

## Your tools and workspace · 0–5 minutes

- Web app: https://tester.dotaeva.cz
- API base URL: `https://tester.dotaeva.cz/api`
- Open **Start new session** in the web app. Click the session ID in the top bar and copy the session token.
- Import the supplied Postman collection and environment. Select the **TesterStay** environment, then paste your token into `token`. The `baseUrl` variable is `https://tester.dotaeva.cz` (without `/api`).
- On iOS, paste the same token and tap **Connect session**. The interviewer provides an already-built app on an iPhone or simulator. Building the app is not part of your interview.
- If you create your session in Postman instead, run **Create separate session**, then use its returned token on the web and iOS. Creating another session gives you a different dataset.
- Start with **Health**, **Session**, **Rooms**, and **List bookings**. Save your evidence before resetting anything. A session lasts seven days after its last database modification.

Browser Developer Tools, Postman, notes, and the built-in **Database explorer** are available. You may consult documentation and ask clarifying questions. Use your session only; load testing and security testing are outside this exercise.

## Product requirements

1. The same token refers to the same reservations in every client. Refresh after changes in another client.
2. There are three individually bookable rooms: Courtyard Studio (`r1`, €120/night, capacity 2), Riverside Loft (`r2`, €180/night, capacity 4), and Garden Suite (`r3`, €240/night, capacity 6).
3. A booking requires an existing room, a guest name of 2–80 characters after trimming, and a whole-number guest count from **1 to the room's capacity**. Guest names are trimmed at both ends before counting.
4. Check-in and check-out are calendar dates in **YYYY-MM-DD** format. Dates have no time or timezone. Check-out is exclusive, strictly later than check-in, and the stay is at most 30 nights. For repeatability, use **October 2026**, even if today's date is later. There is no past-date restriction in this exercise.
5. The total is **nightly rate × nights**. There are no taxes, discounts, or fees. A two-night Courtyard Studio stay costs €240. Estimates and saved totals must agree.
6. Each room is a single unit: confirmed bookings for that room must not overlap. Back-to-back stays are allowed. Cancelled bookings do not occupy a room.
7. A new booking has status `confirmed`. Cancellation persists, returns `cancelled`, and is safe to repeat. The reservation remains visible. Every client must display its actual status.
8. The All, Confirmed, and Cancelled filters must show the matching reservations. Search matches guest names or booking IDs without case sensitivity.
9. Booking JSON must contain string `checkIn` and `checkOut` values in YYYY-MM-DD, an integer `guests`, an integer `total` in whole EUR, and status `confirmed` or `cancelled`.
10. The database should represent calendar dates consistently as ISO date text. `import_log` preserves the original values received from the legacy system. The database interface is read-only.

## Task 1 · Web booking flow · 5–17 minutes

Explore the room rates and sample reservations. Create a Courtyard Studio reservation for **25–27 October 2026**, for two guests, under your chosen fictional name. Check the estimate before saving and the saved reservation after saving. Compare your booking with its API response in Postman.

Record a short test checklist with at least one passing observation and any discrepancy. If you have time, try the status filters. Keep the booking ID for later comparisons.

## Task 2 · API investigation in Postman · 17–32 minutes

Use the supplied requests as a starting point. You must make and save your own test requests:

- Retrieve the booking you created on the web using its ID.
- Create a valid API booking in `r2` for **25–27 October 2026**, with two guests. Confirm it appears on the web after refresh.
- Investigate guest-count boundaries: `0`, `1`, room capacity, above capacity, and a string such as `"2"`. You may use the quote endpoint to avoid creating many bookings; verify any suspicious accepted input with the create-booking endpoint too.
- Add at least two Postman assertions: one for an expected HTTP status, and one for a response value or type. A failing assertion can be valuable evidence.
- Cancel a booking and GET it again to check persistence.

Expected responses: valid quote/read/cancellation **200**; valid creation **201**; invalid fields **422**; booking overlap **409**; missing/unknown token **401**; unknown booking **404**. Errors contain `error.message` and `error.requestId`.

Optional if time remains: check what happens when you request dates that overlap the seeded `B1001` booking for `r1` on **15–17 October 2026**.

## Task 3 · Follow the date through the database · 32–42 minutes

A support report says: **“Taylor Reed's imported reservation has an unexpected check-in date.”** Its ID is `B1003`.

1. Inspect the reservation on the web and GET `/api/bookings/B1003` in Postman. Record the exact value and JSON type of each date field.
2. Open **Database explorer** and inspect `bookings` and `import_log`. Use a query to select the relevant row and the runtime types of its date values.
3. Compare the stored value, the original import value, and the API contract. Explain the mismatch, its likely origin, and which team should investigate first.
4. Suggest a correction and a regression test. Do not modify the database.

SQLite helpers are allowed: `SELECT`, `WHERE`, `typeof(column)`, and `date(...)`. The schema panel shows declared column types; runtime storage types can be inspected separately. If SQL is unfamiliar, ask the interviewer for the starter query; explaining the result matters more than memorizing syntax.

## Task 4 · Cross-check iOS · 42–50 minutes

Connect iOS to the same session. Review `B1002` (Jamie Novak) on iOS, the web, and in Postman. Compare the status. Then cancel another confirmed booking in iOS and refresh the web/API to check the outcome. Capture a screenshot and the corresponding raw API response for any discrepancy.

Explain whether the evidence points to the iOS client, backend, or database. State any uncertainty. A visible symptom alone does not establish the root cause.

## Deliverables and debrief · 50–60 minutes

Provide:

- Your brief test checklist, including passing checks.
- Around **3–4 clear bug reports**, or fewer if you need more time to substantiate them.
- Your exported Postman collection with your requests and assertions. Remove the session token before sharing exports beyond the interviewer.
- Your database query, results, and date-type explanation.
- A two-minute recommendation: which issue should be fixed first, why, and what you would test next.

Use the supplied bug report template. Include expected/actual behavior, reproduction steps, evidence, severity, and likely **Web FE / iOS FE / BE / DB-import** ownership with reasoning. Quality of investigation matters more than the number of findings.

## Request reference

All endpoints except health and session creation require `Authorization: Bearer <token>`. POST requests use `Content-Type: application/json`.

| Method | Path after `/api` | Purpose |
| --- | --- | --- |
| GET | `/health` | Service status; no token |
| POST | `/sessions` | Create a separate seeded session; body `{}`; no token |
| GET | `/session` | Verify your token |
| GET | `/rooms` | Room rates and capacities |
| POST | `/quotes` | Validate stay details and calculate price |
| GET | `/bookings` | List reservations; optional `?status=confirmed` or `cancelled` |
| POST | `/bookings` | Create a reservation |
| GET | `/bookings/{id}` | Read one reservation |
| POST | `/bookings/{id}/cancel` | Cancel; body `{}` |
| GET | `/database/schema` | Read table/column metadata |
| POST | `/database/query` | Execute read-only SQL with `{"sql":"SELECT * FROM bookings;"}` |
| POST | `/session/reset` | Reset only your session with `{"confirm":"RESET"}` |

Example create-booking body:

```json
{
  "roomId": "r2",
  "guestName": "Sam Parker",
  "checkIn": "2026-10-25",
  "checkOut": "2026-10-27",
  "guests": 2,
  "source": "postman"
}
```

Quote requests use `roomId`, `checkIn`, `checkOut`, and `guests`. The OpenAPI file describes the expected behavior; it is not a guarantee that the implementation satisfies it.
