Skip to content

Test Cases

Test Strategy

Test type Target Tool Current state
Unit tests (backend) Pure-function business logic Vitest Implemented (2 files)
Integration tests (backend) API and DB Vitest Not implemented (only the test:integration script exists)
Unit / E2E (frontend) Screens - Not set up (no test runner)

Coverage is currently limited

Automated tests cover only the backend's services/scoring.ts and services/candidate.ts (parseResponseFilter). Everything else relies on Swagger UI (/docs) and manual verification in the UI. No coverage target has been set.

Running Tests

cd heineken-interview-arrange-backend

bun run test                # vitest run (everything)
bun run test:unit           # __tests__/unit only
bun run test:integration    # __tests__/integration only (currently empty)
bun run test:watch          # Watch mode
bun run test:coverage       # Coverage (v8)

bunx vitest run __tests__/unit/services/scoring.test.ts   # A single file
bunx vitest run -t "should cap score at maxScore"         # A single test

The frontend has no test runner. Its quality gates are:

cd heineken-interview-arrange-frontend
bun run check-types
bun run lint

Test Configuration

vitest.config.ts:

Item Value
environment node
globals true (no import needed for describe / it / expect)
include __tests__/**/*.test.ts
testTimeout / hookTimeout 10000 ms
Coverage provider v8 (text / json / html reporters)

Automated Test Cases

Scoring (__tests__/unit/services/scoring.test.ts)

calculateScore:

ID Scenario Expected result Priority
SC-001 No rules Returns 0 High
SC-002 equals condition matches Adds the rule's score High
SC-003 not_equals condition Adds the score when it does not match High
SC-004 in condition Adds the score when the value is in the list High
SC-005 between condition within range Adds the score High
SC-006 between condition out of range Contributes 0 High
SC-007 contains condition (array value) Adds the score when the element is present Medium
SC-008 greater_than condition Adds the score above the threshold Medium
SC-009 less_than condition Adds the score below the threshold Medium
SC-010 Multiple rules Sums each rule's score High
SC-011 Total exceeds maxScore Capped at maxScore High

validateScoringRules:

ID Scenario Expected result Priority
SC-012 Well-formed rules Returns true High
SC-013 Malformed rules Returns false High
SC-014 Unknown condition Returns false High

Response Filter Parsing (__tests__/unit/services/candidate.test.ts)

parseResponseFilter (the responseFilter query on the candidate list):

ID Scenario Expected result Priority
CF-001 Empty string Returns undefined High
CF-002 undefined Returns undefined High
CF-003 String that is not valid JSON Throws BadRequestError High
CF-004 JSON that does not match the schema Throws BadRequestError High
CF-005 Array form with one condition Interpreted as AND High
CF-006 Array form with several conditions All combined with AND High
CF-007 Object form with logic: "and" Interpreted as AND High
CF-008 Object form with logic: "or" Interpreted as OR High
CF-009 logic omitted Defaults to AND High
CF-010 eq operator Accepted Medium
CF-011 neq operator Accepted Medium
CF-012 contains operator Accepted Medium
CF-013 in operator with an array value Accepted Medium
CF-014 Unknown operator Rejected High
CF-015 String value Accepted Medium
CF-016 Numeric value Accepted Medium
CF-017 Array of strings Accepted Medium
CF-018 Array of numbers Accepted Medium
CF-019 Mixed array of strings and numbers Accepted Low
CF-020 Empty conditions array Handled without error Medium
CF-021 Field name containing dots Treated as a nested path Medium
CF-022 Japanese field names Handled correctly High
CF-023 Special characters in values Handled correctly Medium

Areas Requiring Manual Verification

With no automated coverage, the following must be checked through Swagger UI (http://localhost:8080/docs) or the UI itself.

Authentication

ID Scenario Expected result Priority
TC-101 Log in with a Google account Lands on the top page with accessToken in localStorage High
TC-102 Log in with an unregistered account A users row is created with role = member High
TC-103 Call a protected API without a token 401 UNAUTHORIZED High
TC-104 Call an API with an expired token 401 (Token has expired) High
TC-105 Call a write API as viewer 403 FORBIDDEN High

Arrange Flow

ID Scenario Expected result Priority
TC-201 Calculate slots in STEP1 Slots come back from the interviewers' Google Calendars High
TC-202 Place tentative holds in STEP1 Events are created and stored in arrange_settings.step1.tentativeEventIds High
TC-203 Recreate tentative holds Existing holds are deleted first, then recreated Medium
TC-204 Define and save segments in STEP2 Persisted to arrange_settings.step2.segments High
TC-205 Mark primary / reserve in STEP2 project_candidates.selection_type is updated High
TC-206 Register additional questions in STEP3 Persisted to arrange_settings.step3.questions Medium
TC-207 Place holds with PROTOTYPE_MODE=true Nothing is written to the calendar High

Candidate Import

ID Scenario Expected result Priority
TC-301 Upload a UTF-16LE TSV Columns, sample values and a suggested mapping are returned High
TC-302 Upload a UTF-8 TSV 400 from a parse error Medium
TC-303 Execute an import with a mapping Returns 201 and progress becomes processing High
TC-304 Import data containing an existing external_user_id Counted as a duplicate, not registered twice High
TC-305 Run a survey import preview Returns the filtered count and samples High
TC-306 Run a survey import An import_logs row is created and counts are filled in on completion High

Status Management

ID Scenario Expected result Priority
TC-401 Change not_contacted โ†’ contacted Succeeds and appends one row to status_histories High
TC-402 Change away from completed 400 INVALID_STATUS_TRANSITION High
TC-403 Move bounced โ†’ contacted Succeeds Medium
TC-404 Move cancelled โ†’ not_contacted Succeeds Medium

Scheduling

ID Scenario Expected result Priority
TC-501 Open the public page with a valid token Shows the request details and the candidate's name High
TC-502 Open with an expired token Returns is_expired: true or an error High
TC-503 Submit one preferred slot Returns selectedCount: 1 and the status becomes scheduling High
TC-504 Submit 21 preferred slots 400 (validation error) Medium
TC-505 Submit preferred slots again Existing slots are fully replaced High
TC-506 Submit for a candidate already confirmed 409 SLOT_NOT_AVAILABLE High
TC-507 Confirm the schedule as an admin A reservations row is created and the tentative event becomes confirmed High
TC-508 Confirm an already-confirmed candidate again 409 SLOT_NOT_AVAILABLE High

Email

ID Scenario Expected result Priority
TC-601 Preview a template Returns the rendered result with placeholders substituted High
TC-602 Bulk send to several candidates Returns sent_count / failed_count and records email_logs High
TC-603 Send with an empty candidateIds 400 (validation error) Medium
TC-604 Send with PROTOTYPE_MODE=true No email is actually sent High
TC-605 Delete a template Past delivery logs remain Medium

Databricks Integration

ID Scenario Expected result Priority
TC-701 Run the metadata refresh with dryRun Fetches metadata for 611 surveys and writes nothing High
TC-702 Run the metadata refresh databricks_surveys is upserted and existing rows survive High
TC-703 List surveys with Databricks unconfigured 503 DATABRICKS_NOT_CONFIGURED Medium
TC-704 Fetch a survey absent from databricks_surveys 404, never 502 High
TC-705 Fetch a survey with a non-numeric surveyId 400 INVALID_SURVEY_ID; it never reaches a table name High
TC-706 Import a panel with several answers in one field Kept as a list rather than truncated to the first value High
TC-707 Filter responses on a question containing newlines 200, never 500 High
TC-708 Sign in from a disallowed email domain Redirected back to login as domain_not_allowed High

Adding a Test

  • Place it at __tests__/unit/services/<service-name>.test.ts
  • Prioritize pure functions with no external dependencies (DB, Google API, Databricks)
  • Structure describe in three levels โ€” service name โ†’ function name โ†’ aspect โ€” matching the existing tests
  • Running a single file with bunx vitest run <path> is the fastest loop