fix(seed): emit the users array the backend seeder reads
The fixture generator was a version behind the seeder, and the gap was
the `users` key.
`internal/seeder` unmarshals `seed.json` into `{demoUser, users,
entities}` and writes every account in `users`, falling back to
`demoUser` alone when the key is absent. `fixtureFrom` never emitted it —
its comment still claimed the Go loader "ignores the rest", which stopped
being true when the seeder gained the field. So `npm run seed:check`
reported stale on every run, and that is the seventh suite failure, the
one that pre-dates the TypeScript migration.
What made this worth investigating rather than regenerating: the
committed fixture was NOT wrong. It carries two accounts — the demo
administrator and `employer@krow.app` — and running `npm run seed:fixture`
against the old generator would have written a fixture without them.
`krow-backend/docs/deploy-9d3192a.md` says so in as many words, and tells
anyone deploying not to run it.
The accounts are not deleted from a live database by that — the only
DELETE in the seeder is the shift-record prune, and `upsertUser`'s ON
CONFLICT deliberately leaves `password_hash` alone. The damage lands on
the next fresh environment, where `user_employer` would simply never be
created, leaving nobody to sign in as to reach the employer console —
which is the exact gap `seeder.go` records the account as having been
added to close.
The matching frontend half already existed, unmerged, on
`feat/krow-employee-pages` (7a4b61d). That branch is a 53-file console
restructure that renames the same files this migration renamed, so it is
not mergeable here. Only the three files that carry the contract are
taken: `EMPLOYER_USER`, `seedData.User` naming both accounts, and the
generator emitting `users`.
The proof is that nothing had to be written. With the generator corrected
its output is byte-identical to the fixture already committed in
krow-backend — 168,293 bytes, `cmp` clean — so `seed.json` was never
opened for writing, and its md5 and mtime are untouched.
typecheck 0 errors
seed:check "seed.json is in step with src/api/seed.js", exit 0
npm test 1685/1691, up from 1684 — the fixture check now reads
"24 applications, byte-identical". The remaining 6 are
exactly the documented dca1842 baseline failures.
lint exit 0, 0 errors, 289 warnings
build exit 0, bundle 74d17e2d… unchanged
The bundle being unchanged is itself a check: `demoUser.ts` IS in the
production bundle, and `EMPLOYER_USER` is a new export of it. Nothing in
the app imports it, so it is tree-shaken out and not one byte reaches the
shipped code.
No baseline artifact touched. krow-backend not modified.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HBG1wnuRfJKCstGB8Fekr8
This commit is contained in:
@@ -31,10 +31,31 @@ export const GENERATED_NOTE =
|
|||||||
|
|
||||||
export function fixtureFrom(seedModule) {
|
export function fixtureFrom(seedModule) {
|
||||||
const { ShiftRecord, ...entities } = seedModule.seedData;
|
const { ShiftRecord, ...entities } = seedModule.seedData;
|
||||||
/* First key, so it is the first thing anyone opening the file reads. The Go
|
/* `_generated` first, so it is the first thing anyone opening the file reads.
|
||||||
loader unmarshals into a struct of demoUser + entities and ignores the
|
|
||||||
rest, so this costs nothing on the reading side. */
|
`users` is the channel the seeder actually reads — `internal/seeder`
|
||||||
return { _generated: GENERATED_NOTE, demoUser: seedModule.DEMO_USER, entities };
|
unmarshals into `{demoUser, users, entities}` and writes every account in
|
||||||
|
`users`, falling back to `demoUser` alone when the key is absent. That
|
||||||
|
fallback is why `demoUser` stays beside it rather than being replaced: a
|
||||||
|
fixture written here still seeds correctly against a backend that predates
|
||||||
|
the list, it simply seeds one account instead of two.
|
||||||
|
|
||||||
|
Emitting only `demoUser` is what this generator used to do, and it was a
|
||||||
|
version behind: the fixture on disk carries the employer account, the
|
||||||
|
seeder reads it from `users`, and regenerating without this key would drop
|
||||||
|
`employer@krow.app` from every fresh seed — leaving nobody to sign in as
|
||||||
|
to reach the employer console, which is the exact gap the account was
|
||||||
|
added to close.
|
||||||
|
|
||||||
|
`entities.User` carries the same list. The seeder ignores it, and it is
|
||||||
|
emitted because `seedData` is copied wholesale; the two are the same array
|
||||||
|
rather than two places to keep in step. */
|
||||||
|
return {
|
||||||
|
_generated: GENERATED_NOTE,
|
||||||
|
demoUser: seedModule.DEMO_USER,
|
||||||
|
users: seedModule.seedData.User,
|
||||||
|
entities,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Serialised exactly as the committed file is: 2-space indent, no trailing newline. */
|
/** Serialised exactly as the committed file is: 2-space indent, no trailing newline. */
|
||||||
|
|||||||
@@ -39,3 +39,29 @@ export const DEMO_USER: User = {
|
|||||||
emailDigest: true,
|
emailDigest: true,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The employer account, and the reason there is a second one at all.
|
||||||
|
*
|
||||||
|
* `policy.go` has always had three roles — admin, employer, talent — and the
|
||||||
|
* fixture had one administrator, so two thirds of the authorization table was
|
||||||
|
* never exercised and there was nobody to sign in as to reach the employer
|
||||||
|
* console. This account is what makes that path reachable.
|
||||||
|
*
|
||||||
|
* Unlike `DEMO_USER` this is not a default shape for the first render: nothing
|
||||||
|
* in the running app reads it. It exists so that `seedData.User` names both
|
||||||
|
* accounts, which is what the backend seeder writes. See `seed.ts`.
|
||||||
|
*/
|
||||||
|
export const EMPLOYER_USER: User = {
|
||||||
|
id: 'user_employer',
|
||||||
|
full_name: 'Jordan Blake',
|
||||||
|
email: 'employer@krow.app',
|
||||||
|
role: 'employer',
|
||||||
|
account_type: 'employer',
|
||||||
|
created_date: '2026-06-01T09:00:00.000Z',
|
||||||
|
preferences: {
|
||||||
|
owliverDefault: true,
|
||||||
|
compactDensity: false,
|
||||||
|
emailDigest: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { SHIFT_RECORDS } from './attendanceSeed';
|
import { SHIFT_RECORDS } from './attendanceSeed';
|
||||||
import { DEMO_USER } from './demoUser';
|
import { DEMO_USER, EMPLOYER_USER } from './demoUser';
|
||||||
|
|
||||||
const iso = (date: string) => new Date(`${date}T09:00:00.000Z`).toISOString();
|
const iso = (date: string) => new Date(`${date}T09:00:00.000Z`).toISOString();
|
||||||
|
|
||||||
@@ -1990,8 +1990,12 @@ const USER_ACTIVITY = ACTIVITY_EVENTS.map(([event_type, user_email, user_name, a
|
|||||||
* candidate, interview and shift record below — into the production bundle.
|
* candidate, interview and shift record below — into the production bundle.
|
||||||
* The fixtures here are for the test scripts, and they still read
|
* The fixtures here are for the test scripts, and they still read
|
||||||
* `seedData.User` and `DEMO_USER` from this module, so both keep working.
|
* `seedData.User` and `DEMO_USER` from this module, so both keep working.
|
||||||
|
*
|
||||||
|
* `EMPLOYER_USER` travels the same way. It is seeded, never rendered: the
|
||||||
|
* backend writes both accounts so the employer console has somebody to sign
|
||||||
|
* in as, and nothing in the running app imports it.
|
||||||
*/
|
*/
|
||||||
export { DEMO_USER } from './demoUser';
|
export { DEMO_USER, EMPLOYER_USER } from './demoUser';
|
||||||
|
|
||||||
/* ── Assignments ───────────────────────────────────────────────────────────
|
/* ── Assignments ───────────────────────────────────────────────────────────
|
||||||
Who is on which position, and until when — the record that turns a hire into
|
Who is on which position, and until when — the record that turns a hire into
|
||||||
@@ -2051,5 +2055,5 @@ export const seedData = {
|
|||||||
Assignment: ASSIGNMENTS,
|
Assignment: ASSIGNMENTS,
|
||||||
ShiftRecord: SHIFT_RECORDS,
|
ShiftRecord: SHIFT_RECORDS,
|
||||||
Evidence: EVIDENCE,
|
Evidence: EVIDENCE,
|
||||||
User: [DEMO_USER],
|
User: [DEMO_USER, EMPLOYER_USER],
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user