For Quality / QA
Business view
QA is the primary consumer of this feature. It exists to give you a realistic clinic to test against — and it's also a thing you need to test itself. This page covers both: how to use the seeded data, and where the edges are.
What the seed gives you to test with
A single run produces (at sandbox "small profile" scale):
- Patients across the billing matrix: cash, insured (various %), VAT-exempt,
and combinations — the descriptive names spell out which is which, so you can pick a test subject without opening the record.
- Invoices in every payment state: paid in full, partially paid, and unpaid,
paid by cash / card / wallet.
- Insurance receivables left intentionally open (so there's AR to test claims
against), across multiple insurers and policy classes.
- Appointments: months of completed history plus today's and the next two
weeks' bookings in open/confirmed/checked-in states.
- Inventory that has been purchased and consumed, so on-hand amounts and
values are non-trivial.
- Staff with commissions, salaries, and different permission groups.
High-value manual checks (using the data)
- Follow the money on one insured patient. Open their invoice: confirm
subtotal − discount + VAT = total, and that the insurance portion + patient portion reconcile. See The money model for the exact formula.
- Filter the patients list by insurance company / policy class / tags — the
seed populates all of these.
- Check the treasury balances are positive and plausible (capital was injected
before expenses/purchases, so nothing goes negative).
- Open the calendar on "today" — there should be a few appointments in mixed
states, and more spread across the next fortnight.
Testing the seeder itself — the edges
- Determinism. Same
SEED+ same params ⇒ identical clinic. Two runs into
two fresh local DBs should match record-for-record.
- Idempotency. Re-running with the same
OWNER_EMAILmust not create a
second company — it should refresh passwords and reprint the summary.
- Safety guard. Point
DATABASE_URLat a non-local host with no--confirm
→ it must refuse. With --confirm → it proceeds.
- Cleanup.
--cleanup <companyId>should remove the company and its children;
it must reject a non-UUID id.
- Country presets.
SA(15% VAT, SAR),AE(5%, AED),EG(14%, EGP) —
VAT and currency must follow the preset, never Faker.
- Parameter clamping. Silly inputs (e.g.
BRANCHES=0,PATIENTS=-5) are
floored to sane minimums rather than crashing.
- Generic-mode skips. On a base-schema DB (no accounting module), the HR (S-5b)
and coverage (S-6) steps must skip cleanly and reconciliation must report OK with nothing to reconcile — not error.
Known edges / things to watch
- Insurer AR is left open on purpose — unpaid insurance balances are expected,
not a bug. They exist so the claims flow (accounting branch) has receivables.
- Quotations can silently fail. Quotation and upcoming-appointment creation are
wrapped in .catch(() => null), so a schema mismatch there is swallowed. If quotations/appointments look sparse, that swallow is the first place to look.
allowNegativeinventory. Items are created allowing negative stock; if
consumption ever exceeds purchases the amount can go negative by design.
- Sandbox scale ≠ default scale. The PR text says "small profile" (1 branch,
2 doctors, 12 patients, 3 months); the code defaults are 2/4/80/12mo. The sandbox passes overrides. Test both scales.
Technical view
- Determinism: single Faker instance seeded once; every draw goes through
rng (rng.js:15-20, rng.js:48). No direct Math.random.
- Idempotency: owner-exists early return +
updateManypassword refresh
- Safety guard:
assertSafeTarget/isLocalHost(config.js:105-113),
called on both the seed and cleanup paths (index.js:58,64).
- Cleanup: UUID regex validation, FK triggers disabled within one
transaction, child-then-parent deletes, tables absent from the schema skipped (report.js:76-97).
- Country/VAT source of truth:
COUNTRIESpreset, never Faker
(countries.js:7); used for tax, currency, splits (generate.js:73).
- Parameter clamping:
Math.max(1, …)/Math.max(0, …)on counts
- Generic-mode skips: default hooks no-op;
runHr/runCoveragereturnnull
and the caller logs "skipped"; reconcile returns { ok: true } (hooks.js:140-150, index.js:117-129).
- Swallowed failures to watch:
makeQuotationandscheduleUpcoming
.catch(() => null) (generate.js:242, generate.js:348); per-procedure commission override .catch(() => null) (setupPeople.js:118).
- Financial correctness: rounding via
r2, insurance capped at remaining class
limit, VAT split patient/insurer (generate.js:80-97). Detailed in The money model.
- Reconciliation gate: on the accounting branch a failed reconcile sets a
non-zero exit code (index.js:159-162); on this branch it always passes.