CODEX // oaoisme wiki

§ unswayed-backend

Postman complete-flow collection

updated 2026-06-10

Postman complete-flow collection

What it is. A Postman collection (schema v2.1) that drives the entire implemented API surface as one continuous story against a live local server: health → master data → auth (register/login/OTP/reset) → applicant profile, education, filter prefs → the resume pipeline → employer profile & viewers → employer jobs CRUD + the network feed → discovery (job detail / save / saved-jobs merge / external / career-jobs / job alerts) → four job applications → the hiring pipeline (interview → accept → offer → accept → position_filled, plus the reject, interview-decline and counter-offer-decline branches) → Google Calendar's unconfigured contract → employer reviews → notifications → session lifecycle (refresh rotation, reuse detection, logout).

The numbers: 15 folders · 281 requests · 1008 assertions, all green (newman, ~16s end to end). Every request asserts the status code, the {status,message,data} envelope, the e2e-pinned exact messages, the right pagination shape for that endpoint (pagination vs meta vs deliberately none), and the frozen legacy quirks (capitalized Ethnicities, the job-store country:"", the GET-that-mutates notification read, …).

How to run it

npm run postman          # cold → verdict, one command

That executes postman/run.sh: brings up the dockerised Postgres, creates the dedicated ralli_postman database (dev/test DBs untouched), applies migrations, builds, seeds master data from the production CSVs, runs postman/seed.cjs, boots the API on :3010, and runs newman with postman/local.postman_environment.json. Extra args pass through to newman (npm run postman -- --folder "10 - Applications (§32/§34/§22)").

How it's structured (and why)

  • Source = postman/folders/NN-*.json — one Postman folder per domain, filename-ordered. postman/assemble.cjs merges them into postman/unswayed-backend.postman_collection.json. Edit a folder file and reassemble; never hand-edit the collection artifact.
  • Run order is the contract. Folders chain state through collection variables: folder 02 captures master-data ids → folder 03 logs in the seeded users and captures tokens → folder 08's job_id/job3_id feed folders 09–11 → folder 10's four application*_ids feed the pipeline → etc. The collection must run top-to-bottom (Collection Runner or newman), not as individual requests.
  • Seeded vs created. seed.cjs provides only what the API cannot create: two verified applicants (full profiles, fixed UCNs, one resume each with filteredResume set — the §32 apply snapshots it), two employers, the employer2→employer1 follow row (the §41 network feed shows only followed employers' jobs and no follow endpoint exists yet), and five disability rows (no disabilities CSV ships in seeders/). Everything else — jobs, applications, interviews, offers, reviews, notifications — is created by the requests themselves. The seed is idempotent by reset (truncates the user graph; master data survives), so every run starts identical.

The two gotchas worth remembering

  1. @prisma/client auto-loads the repo .env at import — regardless of NODE_ENV or cwd. A developer's real OPENAI/AFFINDA/CLOUDINARY/RAPIDAPI keys would silently un-stub the server (Affinda starts persisting resumes; external jobs serve real 200s instead of the 503 contract). run.sh therefore shelves .env while the server boots and restores it once up — the same trick the Jest e2e uses (test/env-shelf.ts). If you boot the server by hand for the Postman app, shelve .env yourself.
  2. data is a predeclared global in newman's sandbox (the legacy data-file variable). const data = pm.response.json().data works in the Postman app but throws Identifier 'data' has already been declared under newman. Name it body or d.

Capture strategies for non-obvious ids

  • Apply and employer-action endpoints return data:null — application ids are captured from GET my-applications (matched by job title); interview/offer ids from GET application-details/{id}histories[].history_data.interview_id/offer_id.
  • §11 reviews take the employers-table id, not the user id: the popular payload carries it as employer_id (its id field is the user id).
  • requisition_number never appears on the employer jobs wire — it's captured via GET job-detail/{id} as the owner.

Deliberately out of scope

OTP completion (delivered out-of-band, stored hashed) and the 30-day interview/offer expiry 403s (need a fake clock) — both fully covered by the Jest e2e suite; the collection tests their failure contracts. Real provider paths (JSearch/Affinda/OpenAI/Google/LinkedIn/Cloudinary/FCM) run unconfigured by design and are asserted at their documented 503/501/422/403 contracts.

Repo memory: docs/ai/memory/0015-postman-complete-flow-collection.md. Files: postman/ (README inside).