@cosmicdrift/kumiko-testing 0.314.0 → 0.316.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -18,7 +18,8 @@ await tenant.api.writeOk("my-feature:write:thing:create", { title: "x" });
18
18
  `bunfig.integration.toml` and `bunfig.real.toml` (plus `bunfig.dom.toml`). `--dom` needs your own
19
19
  `./test-setup/dom.preload.ts`.
20
20
  - `kumiko-testing integration [--parallel N] [--timings <file>]`: runs `*.integration.test.ts`
21
- with the 15s budget. No `--no-isolate`; `--parallel` only when you ask for it.
21
+ with the 15s budget. `--parallel` only when you ask for it, and then with `--no-isolate`:
22
+ bun 1.4.0's implicit `--isolate` leaks native memory per file until CI workers are OOM-killed.
22
23
 
23
24
  ## E2E (`./e2e`, `./e2e/seed-route`)
24
25
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-testing",
3
- "version": "0.314.0",
3
+ "version": "0.316.0",
4
4
  "description": "Test template for Kumiko apps: per-flow seedTenant, app test stack, canonical test preloads, bunfig generator and integration runner.",
5
5
  "license": "BUSL-1.1",
6
6
  "author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
@@ -63,9 +63,9 @@
63
63
  "kumiko-testing": "./bin/kumiko-testing.ts"
64
64
  },
65
65
  "dependencies": {
66
- "@cosmicdrift/kumiko-bundled-features": "0.314.0",
67
- "@cosmicdrift/kumiko-dev-server": "0.314.0",
68
- "@cosmicdrift/kumiko-framework": "0.314.0",
66
+ "@cosmicdrift/kumiko-bundled-features": "0.316.0",
67
+ "@cosmicdrift/kumiko-dev-server": "0.316.0",
68
+ "@cosmicdrift/kumiko-framework": "0.316.0",
69
69
  "zod": "^4.4.3"
70
70
  },
71
71
  "peerDependencies": {
package/src/changes.json CHANGED
@@ -1,4 +1,22 @@
1
1
  [
2
+ {
3
+ "version": "0.316.0",
4
+ "type": "fix",
5
+ "title": "Parallel integration runs no longer OOM-kill their workers",
6
+ "detail": "bun 1.4.0's --parallel implies --isolate, which leaks native memory per\ntest file while the JS heap stays flat: about 4 MB per file for a bare\nsetupTestStack, about 40 MB with bundled-features. In publicstatus\n(--parallel 4) each worker grew from ~290 to ~1000 MiB and the run died\nwith exit 137 on the 3 GiB CI runner. With --no-isolate the same run\npeaks at 380 MiB and finishes in 28.9 s instead of 49.9 s, with no new\nfailures. Without --parallel bun never isolated files, so the tests\nalready isolate through data (seedTenant per flow, queue prefix per\nstack), not through processes."
7
+ },
8
+ {
9
+ "version": "0.316.0",
10
+ "type": "fix",
11
+ "title": "A dedicated screenshots Playwright config without SCREENSHOT_DIR now fails with a clear message",
12
+ "detail": "A dedicated screenshots config (testDir: \"./e2e/screenshots\", used by\nphronexsis and publicstatus) had its entire testDir excluded by\nscreenshotSpecsIgnore()'s \"**/screenshots/**\" pattern whenever\nSCREENSHOT_DIR was unset, so Playwright reported the generic \"No tests\nfound\" with no mention of the missing env var — even though\nrequireScreenshotDir() already existed with a clear message, just never\ncalled on this path. defineAppE2eConfig now calls it up front for any\ntestDir ending in \"screenshots\" outside a screenshot run."
13
+ },
14
+ {
15
+ "version": "0.316.0",
16
+ "type": "improvement",
17
+ "title": "tenant.addUser accepts an optional displayName/email identity",
18
+ "detail": "seedTenant()'s admin already accepted `admin: { displayName, email }`, but\n`tenant.addUser(roles)` (the seed-user route and the in-process seedTenant\nalike) had no such parameter and always fell back to `Seed <hash>` /\n`user-<uuid>@example.test`, which then leaked into screenshots and docs\n(solon, money-horse). `addUser(roles, { displayName, email })` now threads\nan optional identity through to the created user row, the same\n`SeedAdminIdentity` shape the admin already uses. Also: the seedTenant\nfixture logging the shared Playwright `context` in as the tenant admin was\npreviously documented only in a code comment in auth-kit.ts — it is now on\nthe `SeedTenantFixture` type's own JSDoc."
19
+ },
2
20
  {
3
21
  "version": "0.314.0",
4
22
  "type": "improvement",
@@ -20,7 +20,7 @@ import {
20
20
  SEED_TOKEN_ENV,
21
21
  STYLESHEET_WATCH_ENV,
22
22
  } from "./constants";
23
- import { isScreenshotRun, screenshotSpecsIgnore } from "./screenshot-dir";
23
+ import { isScreenshotRun, requireScreenshotDir, screenshotSpecsIgnore } from "./screenshot-dir";
24
24
  import { E2E_TIMEOUT_MS } from "./timeouts";
25
25
 
26
26
  // Apps pick Meilisearch over their in-memory search adapter just because
@@ -146,6 +146,13 @@ export function defineAppE2eConfig(input: AppE2eConfigInput): PlaywrightTestConf
146
146
  } = input;
147
147
  assertNoReservedEnv(env);
148
148
  for (const project of projects) assertTemplateOwnedKeysUnset(project);
149
+ // A dedicated screenshots config (testDir: "./e2e/screenshots") has its
150
+ // entire testDir excluded by screenshotSpecsIgnore() until SCREENSHOT_DIR
151
+ // is set, which Playwright reports as a generic "No tests found" with no
152
+ // mention of the missing env var.
153
+ if (!isScreenshotRun() && /(?:^|\/)screenshots\/?$/.test(testDir)) {
154
+ requireScreenshotDir();
155
+ }
149
156
  const baseURL = `http://localhost:${port}`;
150
157
  const realRun = isRealProviderRun();
151
158
 
@@ -60,6 +60,9 @@ export function createSeedUserRequestSchema(extraRoles: readonly string[] = [])
60
60
  )
61
61
  .min(1)
62
62
  .max(seedable.length),
63
+ displayName: z.string().min(1).max(MAX_DISPLAY_NAME_LENGTH).optional(),
64
+ // May contain the literal "{tenantId}" placeholder, same as seedAdminIdentitySchema.
65
+ email: z.string().min(1).max(MAX_EMAIL_LENGTH).optional(),
63
66
  });
64
67
  }
65
68
 
@@ -206,7 +206,12 @@ export function createE2eSeedRoutes(
206
206
  const write: SeedWriter = async (handlerQn, payload, tenantId) =>
207
207
  unwrapSavedRow(handlerQn, await deps.dispatchSystemWrite({ handlerQn, payload, tenantId }));
208
208
  try {
209
- return c.json(await persistUserRows(write, verified.tenantId, verified.roles));
209
+ return c.json(
210
+ await persistUserRows(write, verified.tenantId, verified.roles, {
211
+ displayName: verified.displayName,
212
+ email: verified.email,
213
+ }),
214
+ );
210
215
  } catch (error) {
211
216
  return c.json({ error: error instanceof Error ? error.message : "seed failed" }, 500);
212
217
  }
@@ -36,6 +36,11 @@ export type E2eSeededTenant = SeededTenant & {
36
36
  readonly seed: (seeder: string, body?: unknown) => Promise<unknown>;
37
37
  };
38
38
 
39
+ /**
40
+ * Seeds a tenant over HTTP and logs the shared `context` in as its admin, so
41
+ * a `page` opened from that context is already authenticated — call
42
+ * `loginAs` to switch it to another seeded user instead.
43
+ */
39
44
  export type SeedTenantFixture = (opts?: E2eSeedTenantOptions) => Promise<E2eSeededTenant>;
40
45
 
41
46
  type E2eFixtures = {
@@ -125,12 +130,17 @@ export async function provideSeedTenant(
125
130
  name: seeded.name,
126
131
  admin,
127
132
  members: seeded.members.map((member) => toUser(member, [ROLES.Member])),
128
- addUser: async (roles = [ROLES.Member]) =>
133
+ addUser: async (roles = [ROLES.Member], identity = {}) =>
129
134
  toUser(
130
135
  await postSeedRoute(
131
136
  request,
132
137
  SEED_ROUTES.seedUser,
133
- { tenantId, roles: [...roles] } satisfies SeedUserRequest,
138
+ {
139
+ tenantId,
140
+ roles: [...roles],
141
+ displayName: identity.displayName,
142
+ email: identity.email,
143
+ } satisfies SeedUserRequest,
134
144
  seedUserResponseSchema,
135
145
  ),
136
146
  roles,
@@ -29,7 +29,10 @@ export function buildIntegrationTestArgs(opts: IntegrationRunOptions): string[]
29
29
  "test",
30
30
  `--config=${BUNFIG_FILES.integration}`,
31
31
  `--timeout=${TEST_TIMEOUT_MS.integration}`,
32
- ...(opts.parallel !== undefined ? [`--parallel=${opts.parallel}`] : []),
32
+ // bun 1.4.0's --parallel implies --isolate, which leaks native memory per test file
33
+ // (~40-60 MB with bundled-features) until the workers blow the CI runner's limit.
34
+ // Tests isolate through data (seedTenant per flow, queue prefix per stack), not processes.
35
+ ...(opts.parallel !== undefined ? [`--parallel=${opts.parallel}`, "--no-isolate"] : []),
33
36
  ...(opts.timings !== undefined ? [`--timings=${opts.timings}`] : []),
34
37
  ...(opts.updateTimings === true ? ["--update-timings"] : []),
35
38
  ...opts.files,
@@ -184,9 +184,13 @@ export async function seedTenant(
184
184
  name: rows.name,
185
185
  admin,
186
186
  members: rows.members.map((member) => withSession(member, id, [ROLES.Member])),
187
- addUser: async (roles = [ROLES.Member]) =>
187
+ addUser: async (roles = [ROLES.Member], identity = {}) =>
188
188
  withSession(
189
- persist ? await persistUserRows(write, id, roles) : lightCredentials(),
189
+ persist
190
+ ? await persistUserRows(write, id, roles, identity)
191
+ : lightCredentials(
192
+ identity.email !== undefined ? substituteTenantId(identity.email, id) : undefined,
193
+ ),
190
194
  id,
191
195
  roles,
192
196
  ),
package/src/seed-types.ts CHANGED
@@ -45,7 +45,10 @@ export type SeededTenant = {
45
45
  readonly name: string;
46
46
  readonly admin: SeededUser;
47
47
  readonly members: readonly SeededUser[];
48
- readonly addUser: (roles?: readonly string[]) => Promise<SeededUser>;
48
+ readonly addUser: (
49
+ roles?: readonly string[],
50
+ identity?: SeedAdminIdentity,
51
+ ) => Promise<SeededUser>;
49
52
  readonly api: BoundApi;
50
53
  readonly apiAs: (user: SeededUser) => BoundApi;
51
54
  };