HRIS-01 · hris-journeyThree-role approval journey
Employee submits, manager approves, HR checks the balance and audit trail.
Expected: Pending → Approved; one reserved amount becomes used once. Each role sees only allowed data.
HRIS-02 · hris-rejectRejection requires context
Reject with a blank or short reason, then a valid explanation.
Expected: Invalid reasons do not mutate state. Valid rejection releases days and is visible to the employee.
HRIS-03 · hris-permissionsAPI role enforcement
As employee POST decide/adjust-balance, including a forged role field.
Expected: 403; roles come from the authenticated server session, never submitted fields.
HRIS-04 · hris-permissionsManager team isolation
Maya attempts to approve Nina's request or reads Operations data.
Expected: No cross-team data is returned; the attempted decision returns 404.
HRIS-05 · hris-permissionsEmployee ownership
Alex attempts to cancel Sam's request or forge employeeId on submission.
Expected: Cancellation is denied; a submission always belongs to the signed-in employee.
HRIS-06 · hris-balanceBalance reservation
Submit more days than available, counting Pending requests too.
Expected: 409; no request or audit row and no negative balance.
HRIS-07 · hris-balanceCancellation restores days
Cancel Pending and Approved future requests, then repeat.
Expected: Days are released once. Repeating the same request ID is idempotent; a new invalid transition gets 409.
HRIS-08 · hris-journeyOverlap across leave types
Submit Sick leave overlapping Pending Annual leave.
Expected: Overlap is rejected across types and pending/approved states.
HRIS-09 · hris-journeyWorking-day calculation
Submit Friday through Monday, then weekend-only dates.
Expected: Friday–Monday counts two days; weekend-only is rejected.
HRIS-10 · hris-journeyHalf-day boundary
Submit one weekday as Half day, then a multi-day Half day.
Expected: Single weekday reserves 0.5; multi-day half leave is rejected.
HRIS-11 · hris-journeyInvalid dates
Submit reversed, past, impossible, over-30-day and over-one-year dates.
Expected: Validation errors with no mutation, even when HTML validation is bypassed.
HRIS-12 · hris-conflictConcurrent decisions
Send approve and reject for version 1 concurrently.
Expected: One succeeds, one gets 409; exactly one decision audit entry.
HRIS-13 · hris-conflictTerminal status transitions
Try approving Rejected, Cancelled or already Approved requests.
Expected: 409; terminal requests cannot return to Pending or change decision.
HRIS-14 · hris-balanceHR adjustment guard
Reduce allowance below pending plus approved, then set a valid amount with a reason.
Expected: Invalid adjustment is rejected; valid change is versioned and audited.
HRIS-15 · hris-balanceStale HR balance edit
Submit two adjustments with the same employee version.
Expected: Second gets 409; the first adjustment is preserved.
HRIS-16 · hris-filtersFilter and pagination interaction
Filter while on page 2, clear and sort, use Back and reload.
Expected: Page resets on filter changes; count, rows and URL stay consistent.
HRIS-17 · hris-retryAmbiguous submission retry
Retry an unchanged submission after its response is interrupted.
Expected: Same record ID; one reservation and one audit entry.
HRIS-18 · hris-expiryExpired authorization
Submit after session expiry, then sign in again.
Expected: 401; no request is created and no balance is consumed.
HRIS-19 · hris-journeyRole switch and logout
Sign out as employee, sign in as manager then HR; replay old session tokens.
Expected: Session tokens rotate and old tokens cannot access private data.
HRIS-20 · hris-journeyAudit integrity
Inspect actor, target, old/new decision and timestamp after submit, decide, cancel and balance change.
Expected: Each successful operation records one immutable audit event; rejected operations record none.
AUTH-01 · loginEmail normalization
Sign in with [email protected]; repeat with surrounding spaces through the API.
Expected: The same account is used, with no duplicate user.
AUTH-02 · loginWrong password vs unknown user
Try a wrong password, then an unknown email.
Expected: Both return the same generic error and no account data.
AUTH-03 · loginDuplicate registration
Register the sample email again using different capitalization.
Expected: 409 conflict; existing account and password are unchanged.
AUTH-04 · loginPassword confirmation
Register with mismatched passwords; correct the confirmation.
Expected: Mismatch blocks submission; correction allows creation.
AUTH-05 · loginPassword bounds
Submit passwords of 7, 8, 128 and 129 characters via the API.
Expected: Only 8 through 128 characters are accepted; whitespace in passwords is significant.
AUTH-06 · login-lockoutPersistent lockout
Trigger lockout, reload and try the correct password.
Expected: Reload does not reset the 15-minute rate limit.
AUTH-07 · guest-mergeGuest favorites and cart merge
Sign in twice, with a logout between attempts.
Expected: Guest cart merges once and saved IDs remain unique.
AUTH-08 · session-expiredExpired session write
Save an edited profile after expiry.
Expected: 401; stored profile is unchanged.
AUTH-09 · loginLogout across tabs
Open Account in two tabs, sign out in one, focus the other.
Expected: The other tab refreshes its session and hides private profile data.
AUTH-10 · loginSession rotation
Inspect HttpOnly cookies before and after sign-in and sign-out; replay the old token via API.
Expected: Tokens rotate; the old token cannot read the account.
AUTH-11 · loginAccount isolation
Use two browser contexts and try to cancel the other user's order ID.
Expected: 404 and no change to another user's order.
FILTER-01 · filtersFilter intersection
Combine a team, job type, remote flag and search text.
Expected: Results match all active constraints, not just the last one.
FILTER-02 · filtersZero results and recovery
Search an impossible term, then clear all filters.
Expected: Zero state is explicit; all ten jobs return.
FILTER-03 · filtersBack and refresh
Filter, open a job, use Back, then reload.
Expected: Filter values and result count agree with the URL.
FILTER-04 · unicodeCase and whitespace
Search CAFÉ and then padded café; search 東京.
Expected: Case and outer whitespace do not change matching; Unicode works.
FILTER-05 · filtersLiteral special characters
Search for .* [ ] % and an apostrophe.
Expected: Input is literal text, never a regex or query expression.
FILTER-06 · filtersInvalid URL filters
Load a list with an unknown enum, invalid boolean and nonnumeric guest count.
Expected: Invalid values fall back to valid defaults; controls and results agree.
FILTER-07 · filtersSort after filtering
At Haven, choose Ubud and sort price low then high.
Expected: Only Ubud stays remain and prices are correctly ordered.
FILTER-08 · filtersSaved jobs plus filters
Save two jobs; combine filters on Saved jobs; remove one.
Expected: Only saved matching jobs appear; count updates after removal.
FILTER-09 · filtersDietary intersection
At Table & Thyme combine Bowls, vegan and text search.
Expected: Only matching vegan bowls remain; clear resets the checkbox too.
FORM-01 · empty-workspaceWhitespace-only input
Create a task or ticket using only spaces in required text.
Expected: Server validation rejects it even if HTML validation is bypassed.
FORM-02 · unicodeLong values
Try a 120-character task title and then 121 through the API.
Expected: 120 is accepted; 121 is rejected without partial mutation.
FORM-03 · unicodeMarkup remains text
Save literal <img src=x onerror=alert(1)> in a task description.
Expected: The exact text is displayed; no image or script executes.
FORM-04 · filtersUnsafe portfolio URL
Apply to a job using javascript: or data: as the portfolio URL.
Expected: The server accepts only http: or https: URLs.
FORM-05 · retry-saveValues survive failed save
Enter multiple fields, trigger 503, then inspect and retry.
Expected: Entered values remain; error clears on success.
FORM-06 · empty-workspaceEnter-key submit
Complete a form using Tab and submit with Enter.
Expected: Exactly one record is created with an accessible result.
CART-01 · last-itemStock boundary
Request quantity zero, negative, fractional, string, 1 and above stock.
Expected: Only valid integer quantities within stock are accepted.
CART-02 · last-itemAggregate stock across colors
Add the same product in different colors beyond combined availability.
Expected: Stock is enforced across variants; failed additions change nothing.
CART-03 · coupon-passCoupon normalization and repeat
Apply padded lowercase save10 twice.
Expected: 10% applies once and the total remains 90000.
CART-04 · coupon-passInvalid coupon after valid
Apply SAVE10, then an invalid code.
Expected: Error appears; the accepted coupon and total remain consistent.
CART-05 · last-itemEmpty bag coupon
Remove the final line, then apply a coupon via API.
Expected: Coupon is cleared and the empty-bag request is rejected.
CART-06 · checkout-lostPickup vs delivery validation
Choose pickup with no address; switch to express delivery.
Expected: Pickup needs no address; delivery requires address and five-digit postal code.
CART-07 · checkout-lostServer-owned prices
Submit a fake total or unit price with checkout.
Expected: The server calculates catalog prices and shipping itself.
CART-08 · checkout-lostDuplicate checkout
Send the same checkout request ID twice, including concurrently.
Expected: One order and one stock deduction; retries return the same order.
CART-09 · last-itemRepeated cancellation
Cancel an order twice.
Expected: Stock is restored only once.
BOOK-01 · booking-conflictOverlapping dates
Try partial overlap, identical dates and containing dates.
Expected: Every overlap with a confirmed booking is rejected.
BOOK-02 · booking-conflictAdjacent dates
Start a new stay exactly on an existing checkout date.
Expected: Adjacent stays are accepted.
BOOK-03 · booking-conflictInvalid date ranges
Try same-day, reversed, impossible date, 31 nights and over one year ahead.
Expected: Invalid ranges are rejected without a booking.
BOOK-04 · booking-conflictGuest capacity
Try zero guests and one more than room capacity.
Expected: The server rejects both requests.
BOOK-05 · last-seatStale availability race
Reserve the final ticket from two tabs concurrently.
Expected: One succeeds, one fails; confirmed total never exceeds capacity.
BOOK-06 · last-seatCancel and reserve again
Cancel a confirmed reservation and repeat the cancellation.
Expected: Capacity is released once and can be reserved again.
STATE-01 · stale-editLost update prevention
Save two different edits based on the same task version.
Expected: First succeeds; second receives 409 and does not overwrite it.
STATE-02 · empty-workspaceRefresh after create
Create a task, reload its details and the board.
Expected: The record is saved in PostgreSQL and counts agree.
STATE-03 · empty-workspaceDeleted record deep link
Delete a task, then revisit its old details URL.
Expected: A useful not-found state replaces the edit form.
STATE-04 · full-workspaceRecord count boundary
Attempt task 201; delete one and retry.
Expected: The limit is enforced and recovering space works.
STATE-05 · filtersDuplicate active application
Apply to the same job twice, then withdraw and apply again.
Expected: Active duplicate is rejected; reapplication after withdrawal works.
STATE-06 · empty-workspaceResolved ticket reply
Create and resolve a ticket, attempt reply via API, reopen and reply.
Expected: Closed reply is rejected; reopened reply persists.
STATE-07 · empty-workspaceCancellation confirmation
Dismiss a delete/cancel confirmation, then accept it.
Expected: Dismissal changes nothing; acceptance performs the change once.
NET-01 · slow-saveRapid duplicate clicks
Double-click a slow submit.
Expected: One mutation, visible busy state, then enabled controls.
NET-02 · retry-save503 before commit
Submit then retry unchanged.
Expected: First request changes nothing; retry creates one record.
NET-03 · lost-response503 after commit
Submit then retry unchanged.
Expected: Same request ID recovers the already-created record.
NET-04 · rate-limit429 retry
Inspect Retry-After and retry after one second.
Expected: A useful error appears; a later attempt succeeds.
NET-05 · load-errorInitial load recovery
Trigger failed bootstrap, then use Retry loading.
Expected: No infinite loading screen; seeded data appears on retry.
API-01 · empty-workspaceMalformed request
POST malformed JSON, an array, unknown action and missing fields.
Expected: 400 responses with safe messages; no partial records.
API-02 · empty-workspacePayload limit
POST more than 16 KiB to each business API and the lab API.
Expected: 413, with no state changes.
API-03 · empty-workspaceCross-origin mutation
POST with an unrelated Origin or Sec-Fetch-Site: cross-site.
Expected: 403 before creating or mutating any state.
API-04 · lost-responseReused request ID with new payload
Replay a successful request ID with changed task data.
Expected: 409; the first record is unchanged.
API-05 · empty-workspaceWorkspace isolation
Read and edit record IDs from another browser context.
Expected: Other workspace data is absent and mutations return 404.
UX-01 · filtersKeyboard-only filters
Navigate controls with Tab and choose values with the keyboard.
Expected: Focus is visible and every filter is operable and labeled.
UX-02 · unicodeMobile long content
Use a 375px viewport with long and Unicode task content.
Expected: No horizontal document overflow or clipped controls.
UX-03 · retry-saveAccessible error and recovery
Trigger a server error, inspect alert semantics, retry.
Expected: The error is announced and removed when the operation succeeds.
CONTROL-01 · coupon-failKnown bad assertion
Assert exact total 90000 after SAVE10 in the broken scenario.
Expected: A functioning test runner reports failure with actual 100000.
CONTROL-02 · coupon-passKnown good assertion
Run the same assertion in the fixed scenario.
Expected: The same test passes with actual 90000.
LAB-01 · loginFresh-run repeatability
Start a preset, mutate it, then start the preset again.
Expected: A new isolated run has its original seed data.
LAB-02 · loginNormal data preservation
Create a normal record, run a preset, then leave the lab.
Expected: The normal browser's data and cookies are restored unchanged.