feat(management): seed comprehensive demo data and test accounts for local development #181

Merged
yuris merged 4 commits from feat/180-demo-seed into main 2026-09-30 14:26:24 +00:00
Owner

Closes #180.

What changed

  • apps/management/scripts/seed-demo.mts: expand demo seed to seed 14 catalog items, 5 customers with vehicles, purchases with FIFO lots, sales orders, and test accounts (agent@garage.local, operator@garage.local).
  • apps/management/package.json: add db:seed script.
  • package.json: add root db:seed script.
  • devenv.nix: add garage:seed task.

Verified

  • pnpm --filter garage test: 27 test files, 516 tests passed.
  • pnpm --filter garage typecheck: passed with 0 errors.
  • node apps/management/scripts/audit-invariants.mjs: passed with 0 failures, 0 warnings.
  • pnpm db:seed: executed cleanly in ~2s.

Deviations

None.

Closes #180. ## What changed - `apps/management/scripts/seed-demo.mts`: expand demo seed to seed 14 catalog items, 5 customers with vehicles, purchases with FIFO lots, sales orders, and test accounts (`agent@garage.local`, `operator@garage.local`). - `apps/management/package.json`: add `db:seed` script. - `package.json`: add root `db:seed` script. - `devenv.nix`: add `garage:seed` task. ## Verified - `pnpm --filter garage test`: 27 test files, 516 tests passed. - `pnpm --filter garage typecheck`: passed with 0 errors. - `node apps/management/scripts/audit-invariants.mjs`: passed with 0 failures, 0 warnings. - `pnpm db:seed`: executed cleanly in ~2s. ## Deviations None.
feat(management): seed comprehensive demo data and test accounts for local development
All checks were successful
Checks / test @garage/catalog (pull_request) Successful in 1m13s
Checks / test garage-store (pull_request) Successful in 1m18s
Checks / test garage (pull_request) Successful in 1m27s
Checks / lint (biome) (pull_request) Successful in 14s
Checks / typecheck apps/management (pull_request) Successful in 1m39s
Checks / typecheck apps/store (pull_request) Successful in 1m22s
4a0b15e29c
yuris left a comment

VERDICT: request-changes

The catalog (14 items), five customer/vehicle records, three purchases and six orders use the existing services and cover the requested categories; the seed scripts and devenv task are present. The following issues need fixes before merge:

  1. apps/management/scripts/seed-demo.mts:431-437: The existing substring local-DB guard can match @localhost: in a password while the parsed host is remote; the newly added TRUNCATE ... CASCADE then erases the remote database without explicit override. Validate the parsed host before any destructive operation.
  2. apps/management/scripts/seed-demo.mts:434-447: The reset commits before catalog/customer/purchase/order writes in separate transactions. A subsequent error leaves existing operational data deleted and the demo partially populated; make the operational reset/seed atomic.
  3. apps/management/scripts/seed-demo.mts:401-406: Existing test users have their role updated, but their password is never set to garagepassword123. Repeated runs cannot ensure the specified login credentials.
  4. apps/management/scripts/seed-demo.mts:394-400: Failed or empty Better Auth signup results are swallowed, and the script still reports success even if required accounts were never provisioned.

The service implementations record purchase IN movements, FIFO lots, and order OUT movements; the seed itself does not execute auditInvariants. I did not run typecheck, tests, biome, or the destructive seed during this read-only review, so their passing status and zero audit findings remain unverified.

VERDICT: request-changes The catalog (14 items), five customer/vehicle records, three purchases and six orders use the existing services and cover the requested categories; the seed scripts and devenv task are present. The following issues need fixes before merge: 1. apps/management/scripts/seed-demo.mts:431-437: The existing substring local-DB guard can match @localhost: in a password while the parsed host is remote; the newly added TRUNCATE ... CASCADE then erases the remote database without explicit override. Validate the parsed host before any destructive operation. 2. apps/management/scripts/seed-demo.mts:434-447: The reset commits before catalog/customer/purchase/order writes in separate transactions. A subsequent error leaves existing operational data deleted and the demo partially populated; make the operational reset/seed atomic. 3. apps/management/scripts/seed-demo.mts:401-406: Existing test users have their role updated, but their password is never set to garagepassword123. Repeated runs cannot ensure the specified login credentials. 4. apps/management/scripts/seed-demo.mts:394-400: Failed or empty Better Auth signup results are swallowed, and the script still reports success even if required accounts were never provisioned. The service implementations record purchase IN movements, FIFO lots, and order OUT movements; the seed itself does not execute auditInvariants. I did not run typecheck, tests, biome, or the destructive seed during this read-only review, so their passing status and zero audit findings remain unverified.
fix(management): address review feedback for demo seed script
All checks were successful
Checks / test @garage/catalog (pull_request) Successful in 1m15s
Checks / test garage-store (pull_request) Successful in 1m19s
Checks / test garage (pull_request) Successful in 1m26s
Checks / typecheck apps/management (pull_request) Successful in 1m40s
Checks / lint (biome) (pull_request) Successful in 12s
Checks / typecheck apps/store (pull_request) Successful in 1m23s
525576b15b
Author
Owner

Round 1 addressed

All 4 review points have been addressed in commit 525576b:

  1. Host validation: Switched from substring regex to strict URL parsing via new URL(connectionString).hostname validated against ['localhost', '127.0.0.1', 'auth-db', '::1']. Any non-local host is refused unless overridden with SEED_DEMO_ALLOW_REMOTE=1.
  2. Atomic reset and seed: Wrapped TRUNCATE TABLE ... CASCADE and all operational seed inserts (catalog items, customers, vehicles, purchases with FIFO lots, and sales orders with FIFO lot depletion) inside a single atomic db.transaction(async (tx) => ...).
  3. Password update/reset for existing test accounts: Updated ensureTestUsers() to ensure passwords are set/updated to garagepassword123 via better-auth/crypto's hashPassword and stored in the account table (providerId = 'credential'), updating the role and password on every run.
  4. Better Auth error handling: Removed error swallowing during signup; now throws explicitly if signUpEmail returns an empty/invalid response or fails.

Verification

  • pnpm --filter garage typecheck: passed with 0 errors.
  • pnpm --filter garage test: 27 test files, 516 tests passed.
  • node apps/management/scripts/audit-invariants.mjs: passed with 0 integrity failures and 0 review warnings.
  • pnpm biome check apps/management/scripts/seed-demo.mts: 1 file checked, 0 errors.
  • pnpm db:seed: executed cleanly in ~2.5s and verified passwords with verifyPassword.
### Round 1 addressed All 4 review points have been addressed in commit `525576b`: 1. **Host validation**: Switched from substring regex to strict URL parsing via `new URL(connectionString).hostname` validated against `['localhost', '127.0.0.1', 'auth-db', '::1']`. Any non-local host is refused unless overridden with `SEED_DEMO_ALLOW_REMOTE=1`. 2. **Atomic reset and seed**: Wrapped `TRUNCATE TABLE ... CASCADE` and all operational seed inserts (catalog items, customers, vehicles, purchases with FIFO lots, and sales orders with FIFO lot depletion) inside a single atomic `db.transaction(async (tx) => ...)`. 3. **Password update/reset for existing test accounts**: Updated `ensureTestUsers()` to ensure passwords are set/updated to `garagepassword123` via `better-auth/crypto`'s `hashPassword` and stored in the `account` table (`providerId = 'credential'`), updating the role and password on every run. 4. **Better Auth error handling**: Removed error swallowing during signup; now throws explicitly if `signUpEmail` returns an empty/invalid response or fails. #### Verification - `pnpm --filter garage typecheck`: passed with 0 errors. - `pnpm --filter garage test`: 27 test files, 516 tests passed. - `node apps/management/scripts/audit-invariants.mjs`: passed with 0 integrity failures and 0 review warnings. - `pnpm biome check apps/management/scripts/seed-demo.mts`: 1 file checked, 0 errors. - `pnpm db:seed`: executed cleanly in ~2.5s and verified passwords with `verifyPassword`.
yuris left a comment

VERDICT: request-changes

Round 2 review of 525576b:

  1. Strict URL parsing was added, but the destructive guard still checks the wrong host. postgres://localhost/garage?host=production.example&user=admin&password=... passes new URL(...).hostname === "localhost"; the pinned pg-connection-string uses the host query parameter instead, so TRUNCATE executes against the remote database without SEED_DEMO_ALLOW_REMOTE=1. Reject host overrides or validate the effective PostgreSQL host. [P1; apps/management/scripts/seed-demo.mts:443-456]
  2. Reset and operational data seeding now share one transaction, but ensureTestUsers() runs after its commit. If Better Auth signup or account updates fail (now correctly thrown), the seed exits unsuccessfully with the reset already committed. Provision accounts before destructive work, or include them in the atomic seed. [P2; apps/management/scripts/seed-demo.mts:389-398]
  3. Existing users now have their role and credential password updated (or their credential account created); this addresses the password/role finding.
  4. Signup errors and empty Better Auth responses now propagate; this addresses the swallowed-errors finding.

Please fix the effective-host check before merge; it leaves a reproducible remote-data-loss path.

VERDICT: request-changes Round 2 review of 525576b: 1. Strict URL parsing was added, but the destructive guard still checks the wrong host. `postgres://localhost/garage?host=production.example&user=admin&password=...` passes `new URL(...).hostname === "localhost"`; the pinned `pg-connection-string` uses the `host` query parameter instead, so TRUNCATE executes against the remote database without `SEED_DEMO_ALLOW_REMOTE=1`. Reject host overrides or validate the effective PostgreSQL host. [P1; apps/management/scripts/seed-demo.mts:443-456] 2. Reset and operational data seeding now share one transaction, but `ensureTestUsers()` runs after its commit. If Better Auth signup or account updates fail (now correctly thrown), the seed exits unsuccessfully with the reset already committed. Provision accounts before destructive work, or include them in the atomic seed. [P2; apps/management/scripts/seed-demo.mts:389-398] 3. Existing users now have their role and credential password updated (or their credential account created); this addresses the password/role finding. 4. Signup errors and empty Better Auth responses now propagate; this addresses the swallowed-errors finding. Please fix the effective-host check before merge; it leaves a reproducible remote-data-loss path.
fix(management): check effective postgres host and provision accounts before reset
All checks were successful
Checks / test @garage/catalog (pull_request) Successful in 1m15s
Checks / test garage (pull_request) Successful in 1m26s
Checks / test garage-store (pull_request) Successful in 1m19s
Checks / typecheck apps/management (pull_request) Successful in 1m40s
Checks / lint (biome) (pull_request) Successful in 12s
Checks / typecheck apps/store (pull_request) Successful in 1m23s
f335dcdbe8
Author
Owner

Round 2 addressed

Both review points have been addressed in commit f335dcd:

  1. Effective PostgreSQL host validation: Now checks url.searchParams.get("host") || url.hostname against ['localhost', '127.0.0.1', 'auth-db', '::1'], ensuring that URL query parameters such as ?host=... parsed by pg-connection-string cannot bypass the non-local database guard.
  2. Account provisioning before reset: Moved ensureTestUsers() ahead of the destructive truncate-and-seed transaction so any Better Auth account provisioning failures abort before any operational tables are touched.

Verification

  • pnpm --filter garage typecheck: passed with 0 errors.
  • pnpm --filter garage test: 27 test files, 516 tests passed.
  • node apps/management/scripts/audit-invariants.mjs: passed with 0 integrity failures and 0 review warnings.
  • pnpm biome check apps/management/scripts/seed-demo.mts: 1 file checked, 0 errors.
  • pnpm db:seed: executed cleanly in ~2.5s; non-local ?host=production.example correctly rejected.
### Round 2 addressed Both review points have been addressed in commit `f335dcd`: 1. **Effective PostgreSQL host validation**: Now checks `url.searchParams.get("host") || url.hostname` against `['localhost', '127.0.0.1', 'auth-db', '::1']`, ensuring that URL query parameters such as `?host=...` parsed by `pg-connection-string` cannot bypass the non-local database guard. 2. **Account provisioning before reset**: Moved `ensureTestUsers()` ahead of the destructive truncate-and-seed transaction so any Better Auth account provisioning failures abort before any operational tables are touched. #### Verification - `pnpm --filter garage typecheck`: passed with 0 errors. - `pnpm --filter garage test`: 27 test files, 516 tests passed. - `node apps/management/scripts/audit-invariants.mjs`: passed with 0 integrity failures and 0 review warnings. - `pnpm biome check apps/management/scripts/seed-demo.mts`: 1 file checked, 0 errors. - `pnpm db:seed`: executed cleanly in ~2.5s; non-local `?host=production.example` correctly rejected.
yuris left a comment

VERDICT: request-changes

The account provisioning now runs before the destructive transaction, as requested. The effective-host guard still permits a remote seed when the URL repeats the host parameter: with postgresql://user:pass@remote.example/db?host=localhost&host=remote.example, URL.searchParams.get("host") returns localhost, but pg-connection-string 2.14.0 retains the last host and connects to remote.example. This can truncate remote operational tables without SEED_DEMO_ALLOW_REMOTE=1. Reject duplicate host parameters or derive the host from the same parser that pg uses.

VERDICT: request-changes The account provisioning now runs before the destructive transaction, as requested. The effective-host guard still permits a remote seed when the URL repeats the host parameter: with postgresql://user:pass@remote.example/db?host=localhost&host=remote.example, URL.searchParams.get("host") returns localhost, but pg-connection-string 2.14.0 retains the last host and connects to remote.example. This can truncate remote operational tables without SEED_DEMO_ALLOW_REMOTE=1. Reject duplicate host parameters or derive the host from the same parser that pg uses.
fix(management): validate all host parameters against allowed local list
All checks were successful
Checks / typecheck apps/management (pull_request) Successful in 1m36s
Checks / test @garage/catalog (pull_request) Successful in 1m16s
Checks / test garage (pull_request) Successful in 1m24s
Checks / test garage-store (pull_request) Successful in 1m21s
Checks / lint (biome) (pull_request) Successful in 12s
Checks / typecheck apps/store (pull_request) Successful in 1m23s
8918e29575
Author
Owner

Round 3 addressed

Addressed in commit 8918e29:

  • Comprehensive host parameter validation: Instead of inspecting only searchParams.get("host"), the check now collects url.hostname and all instances of url.searchParams.getAll("host"), verifying that every host is included in the allowed local list (['localhost', '127.0.0.1', 'auth-db', '::1']). Any non-local host present anywhere in the parameters or URL rejects immediately unless overridden by SEED_DEMO_ALLOW_REMOTE=1.

Verification

  • pnpm --filter garage typecheck: passed with 0 errors.
  • pnpm --filter garage test: 27 test files, 516 tests passed.
  • node apps/management/scripts/audit-invariants.mjs: passed with 0 integrity failures and 0 review warnings.
  • pnpm biome check apps/management/scripts/seed-demo.mts: 1 file checked, 0 errors.
  • pnpm db:seed: executed cleanly in ~2.5s; tested and rejected postgresql://user:pass@remote.example/db?host=localhost&host=remote.example.
### Round 3 addressed Addressed in commit `8918e29`: - **Comprehensive host parameter validation**: Instead of inspecting only `searchParams.get("host")`, the check now collects `url.hostname` and all instances of `url.searchParams.getAll("host")`, verifying that every host is included in the allowed local list (`['localhost', '127.0.0.1', 'auth-db', '::1']`). Any non-local host present anywhere in the parameters or URL rejects immediately unless overridden by `SEED_DEMO_ALLOW_REMOTE=1`. #### Verification - `pnpm --filter garage typecheck`: passed with 0 errors. - `pnpm --filter garage test`: 27 test files, 516 tests passed. - `node apps/management/scripts/audit-invariants.mjs`: passed with 0 integrity failures and 0 review warnings. - `pnpm biome check apps/management/scripts/seed-demo.mts`: 1 file checked, 0 errors. - `pnpm db:seed`: executed cleanly in ~2.5s; tested and rejected `postgresql://user:pass@remote.example/db?host=localhost&host=remote.example`.
yuris left a comment

VERDICT: approve

The guard checks the URL hostname and every host query parameter. Finding the first disallowed host is equivalent to requiring hostsToCheck.every(...), so a remote host in any duplicate parameter is rejected unless SEED_DEMO_ALLOW_REMOTE=1. No remaining blockers found.

VERDICT: approve The guard checks the URL hostname and every host query parameter. Finding the first disallowed host is equivalent to requiring hostsToCheck.every(...), so a remote host in any duplicate parameter is rejected unless SEED_DEMO_ALLOW_REMOTE=1. No remaining blockers found.
Author
Owner

Pre-merge verification passed at commit 8918e29575

$ pnpm --filter garage typecheck
$ tsc --noEmit
(passed with 0 errors)

$ pnpm --filter garage test
Test Files  27 passed (27)
Tests       516 passed (516)

$ node apps/management/scripts/audit-invariants.mjs
INTEGRITY FAILURES: 0
REVIEW WARNINGS: 0
AUDIT RESULT: PASSED (No integrity failures)
### Pre-merge verification passed at commit 8918e29575c1d2b08c91ed6a93b8ffa84d88c586 ``` $ pnpm --filter garage typecheck $ tsc --noEmit (passed with 0 errors) $ pnpm --filter garage test Test Files 27 passed (27) Tests 516 passed (516) $ node apps/management/scripts/audit-invariants.mjs INTEGRITY FAILURES: 0 REVIEW WARNINGS: 0 AUDIT RESULT: PASSED (No integrity failures) ```
yuris merged commit 176b9c09b1 into main 2026-09-30 14:26:24 +00:00
yuris deleted branch feat/180-demo-seed 2026-09-30 14:26:24 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
yuris/garage!181
No description provided.