@cosmicdrift/kumiko-testing 0.319.0 → 0.320.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 +8 -6
- package/package.json +8 -4
- package/src/bunfig.ts +5 -1
- package/src/changes.json +12 -0
- package/src/e2e/constants.ts +0 -12
- package/src/e2e/define-app-e2e-config.ts +1 -1
- package/src/e2e/index.ts +5 -2
- package/src/e2e/screenshots.ts +2 -1
- package/src/preload/env.ts +1 -0
- package/src/preload/real.ts +1 -0
- package/src/preload/schema-env-defaults-values.ts +6 -0
- package/src/preload/schema-env-defaults.ts +18 -0
- package/src/preload/service-env-defaults-values.ts +0 -1
- package/src/scaffold.ts +1 -1
- package/src/seed-tenant.ts +8 -0
package/README.md
CHANGED
|
@@ -11,12 +11,14 @@ const persisted = await seedTenant(stack, { persist: true }); // real rows via t
|
|
|
11
11
|
await tenant.api.writeOk("my-feature:write:thing:create", { title: "x" });
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
- `preload/{temporal,ci-log,scrub-env,env,real,dom}`: bunfig `preload` entries.
|
|
15
|
-
`PROVIDER_ENV_KEYS` (unit default); `env`
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
`
|
|
14
|
+
- `preload/{temporal,ci-log,scrub-env,schema-env-defaults,env,real,dom}`: bunfig `preload` entries.
|
|
15
|
+
`scrub-env` removes `PROVIDER_ENV_KEYS` (unit default); `schema-env-defaults` fills `JWT_SECRET`
|
|
16
|
+
and the `KUMIKO_SECRETS_MASTER_KEY_*` pair that the framework's own env schema requires (unit,
|
|
17
|
+
DOM, integration, real — never a service endpoint); `env` adds `schema-env-defaults` plus the
|
|
18
|
+
localhost service defaults (integration); `real` refuses to run without
|
|
19
|
+
`KUMIKO_REAL_PROVIDERS=1` and in CI, and keeps the keys; `dom` registers happy-dom
|
|
20
|
+
(window/document/HTMLElement) while preserving Bun's native fetch/Request/Response, and cleans up
|
|
21
|
+
(`@testing-library/react` unmount, Radix DOM/style leaks, `window.location`) after every test.
|
|
20
22
|
- `kumiko-testing bunfig [--dom] [--coverage]`: writes `bunfig.toml`,
|
|
21
23
|
`bunfig.integration.toml` and `bunfig.real.toml` (plus `bunfig.dom.toml`). `--dom` adds
|
|
22
24
|
`preload/dom` (happy-dom + testing-library/react cleanup); `@happy-dom/global-registrator` and
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-testing",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.320.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>",
|
|
@@ -42,6 +42,10 @@
|
|
|
42
42
|
"types": "./src/preload/scrub-env.ts",
|
|
43
43
|
"default": "./src/preload/scrub-env.ts"
|
|
44
44
|
},
|
|
45
|
+
"./preload/schema-env-defaults": {
|
|
46
|
+
"types": "./src/preload/schema-env-defaults.ts",
|
|
47
|
+
"default": "./src/preload/schema-env-defaults.ts"
|
|
48
|
+
},
|
|
45
49
|
"./preload/env": {
|
|
46
50
|
"types": "./src/preload/env.ts",
|
|
47
51
|
"default": "./src/preload/env.ts"
|
|
@@ -71,9 +75,9 @@
|
|
|
71
75
|
"kumiko-testing": "./bin/kumiko-testing.ts"
|
|
72
76
|
},
|
|
73
77
|
"dependencies": {
|
|
74
|
-
"@cosmicdrift/kumiko-bundled-features": "0.
|
|
75
|
-
"@cosmicdrift/kumiko-dev-server": "0.
|
|
76
|
-
"@cosmicdrift/kumiko-framework": "0.
|
|
78
|
+
"@cosmicdrift/kumiko-bundled-features": "0.320.0",
|
|
79
|
+
"@cosmicdrift/kumiko-dev-server": "0.320.0",
|
|
80
|
+
"@cosmicdrift/kumiko-framework": "0.320.0",
|
|
77
81
|
"zod": "^4.4.3"
|
|
78
82
|
},
|
|
79
83
|
"peerDependencies": {
|
package/src/bunfig.ts
CHANGED
|
@@ -28,7 +28,11 @@ const PRELOAD_PREFIX = "@cosmicdrift/kumiko-testing/preload";
|
|
|
28
28
|
const DOM_PRELOAD = `${PRELOAD_PREFIX}/dom`;
|
|
29
29
|
|
|
30
30
|
const PRELOADS = {
|
|
31
|
-
unit: [
|
|
31
|
+
unit: [
|
|
32
|
+
`${PRELOAD_PREFIX}/temporal`,
|
|
33
|
+
`${PRELOAD_PREFIX}/scrub-env`,
|
|
34
|
+
`${PRELOAD_PREFIX}/schema-env-defaults`,
|
|
35
|
+
],
|
|
32
36
|
integration: [`${PRELOAD_PREFIX}/temporal`, `${PRELOAD_PREFIX}/env`],
|
|
33
37
|
real: [`${PRELOAD_PREFIX}/temporal`, `${PRELOAD_PREFIX}/real`],
|
|
34
38
|
} as const satisfies Record<BunfigVariant, readonly string[]>;
|
package/src/changes.json
CHANGED
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.320.0",
|
|
4
|
+
"type": "improvement",
|
|
5
|
+
"title": "requireRealProviders from kumiko-testing/e2e, SSE events for seedTenant tenants",
|
|
6
|
+
"detail": "`@cosmicdrift/kumiko-testing/e2e` re-exports `requireRealProviders`, `isRealProviderRun` and `REAL_PROVIDERS_ENV` from the framework's `testing/real-providers` subpath instead of keeping its own copy. Playwright specs and configs, which load under Node, no longer have to avoid the framework's `./testing` barrel.\n\n`seedTenant(stack, …)` subscribes `stack.events.sse` to the seeded tenant's SSE channel. Before, `setupTestStack` only listened on test tenant 1, so writes in a seeded tenant never showed up on `events.sse` and tests had to call `stack.sseBroker.addClient(tenantChannel(id), …)` themselves; that manual call can go. Events from `persist: true` seeding (tenant, user, membership) now also land on `events.sse` after the next drain."
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"version": "0.320.0",
|
|
10
|
+
"type": "improvement",
|
|
11
|
+
"title": "Schema-required test env keys (JWT_SECRET, secrets master key) now default in every bunfig variant",
|
|
12
|
+
"detail": "Apps with the secrets, field-encryption or auth-mfa features previously had\nto hand-roll a `test-setup/env.preload.ts` to satisfy the env schema's\n`JWT_SECRET` and `KUMIKO_SECRETS_MASTER_KEY_*` in unit and DOM tests, since\nonly `SERVICE_ENV_DEFAULTS` (integration/real only) carried a `JWT_SECRET`\ndefault and no map carried the master key. The new `SCHEMA_ENV_DEFAULTS`\nmap lives next to `SERVICE_ENV_DEFAULTS` and is preloaded in all four bunfig\nvariants (unit, DOM, integration, real) via a new `preload/schema-env-defaults`\nmodule, filled with `??=` so an app's own value is kept; only a\n`JWT_SECRET` shorter than 32 characters is replaced.\n`SERVICE_ENV_DEFAULTS` no longer duplicates `JWT_SECRET` — it has one home\nnow. Unit and DOM still carry no service-endpoint defaults."
|
|
13
|
+
},
|
|
2
14
|
{
|
|
3
15
|
"version": "0.318.0",
|
|
4
16
|
"type": "improvement",
|
package/src/e2e/constants.ts
CHANGED
|
@@ -15,18 +15,6 @@ export const SEED_TOKEN_ENV = "KUMIKO_TEST_SEED_TOKEN";
|
|
|
15
15
|
export const SEED_TOKEN_HEADER = "x-kumiko-test-seed";
|
|
16
16
|
export const E2E_WORKERS_ENV = "KUMIKO_E2E_WORKERS";
|
|
17
17
|
|
|
18
|
-
// Same literal as the framework's requireRealProviders()/isRealProviderRun(),
|
|
19
|
-
// duplicated because the Playwright config runs under Node and importing the
|
|
20
|
-
// framework's "./testing" barrel there would pull in its full Bun-toolchain
|
|
21
|
-
// dependency graph for a single string constant.
|
|
22
|
-
export const REAL_PROVIDERS_ENV = "KUMIKO_REAL_PROVIDERS";
|
|
23
|
-
|
|
24
|
-
export function isRealProviderRun(
|
|
25
|
-
env: Readonly<Record<string, string | undefined>> = process.env,
|
|
26
|
-
): boolean {
|
|
27
|
-
return env[REAL_PROVIDERS_ENV] === "1";
|
|
28
|
-
}
|
|
29
|
-
|
|
30
18
|
// Same literal as kumiko-dev-server's createKumikoServer (STYLESHEET_WATCH_ENV),
|
|
31
19
|
// duplicated because the Playwright config runs under Node and can't import
|
|
32
20
|
// that Bun-toolchain-adjacent module.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { availableParallelism } from "node:os";
|
|
3
|
+
import { isRealProviderRun } from "@cosmicdrift/kumiko-framework/testing/real-providers";
|
|
3
4
|
import {
|
|
4
5
|
defineConfig,
|
|
5
6
|
devices,
|
|
@@ -12,7 +13,6 @@ import { SERVICE_ENV_DEFAULTS } from "../preload/service-env-defaults-values";
|
|
|
12
13
|
import {
|
|
13
14
|
DESKTOP_VIEWPORT,
|
|
14
15
|
E2E_WORKERS_ENV,
|
|
15
|
-
isRealProviderRun,
|
|
16
16
|
PLAYWRIGHT_DEMO_ENV,
|
|
17
17
|
PROD_BUNDLES_ENV,
|
|
18
18
|
SCREENSHOT_DEVICE_SCALE_FACTOR,
|
package/src/e2e/index.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
export {
|
|
2
|
+
isRealProviderRun,
|
|
3
|
+
REAL_PROVIDERS_ENV,
|
|
4
|
+
requireRealProviders,
|
|
5
|
+
} from "@cosmicdrift/kumiko-framework/testing/real-providers";
|
|
1
6
|
export { expect } from "@playwright/test";
|
|
2
7
|
export {
|
|
3
8
|
apiCommand,
|
|
@@ -16,10 +21,8 @@ export {
|
|
|
16
21
|
CSRF_COOKIE_NAME,
|
|
17
22
|
CSRF_HEADER_NAME,
|
|
18
23
|
E2E_WORKERS_ENV,
|
|
19
|
-
isRealProviderRun,
|
|
20
24
|
KUMIKO_SECRETS_MASTER_KEY_V1,
|
|
21
25
|
PLAYWRIGHT_DEMO_ENV,
|
|
22
|
-
REAL_PROVIDERS_ENV,
|
|
23
26
|
SEED_ENABLE_ENV,
|
|
24
27
|
SEED_TOKEN_ENV,
|
|
25
28
|
} from "./constants";
|
package/src/e2e/screenshots.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { mkdirSync, statSync } from "node:fs";
|
|
3
3
|
import { dirname } from "node:path";
|
|
4
|
+
import { isRealProviderRun } from "@cosmicdrift/kumiko-framework/testing/real-providers";
|
|
4
5
|
import { expect, type Page, type Request } from "@playwright/test";
|
|
5
|
-
import { DESKTOP_VIEWPORT
|
|
6
|
+
import { DESKTOP_VIEWPORT } from "./constants";
|
|
6
7
|
import { pinEnglishLocale } from "./pin-english-locale";
|
|
7
8
|
import { requireScreenshotDir, SCREENSHOT_DIR_ENV } from "./screenshot-dir";
|
|
8
9
|
import { type SeedTenantFixture, test } from "./seeded-tenant-fixture";
|
package/src/preload/env.ts
CHANGED
package/src/preload/real.ts
CHANGED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// KUMIKO_SECRETS_MASTER_KEY_V1 must base64-decode to exactly 32 bytes (AES-256 KEK).
|
|
2
|
+
export const SCHEMA_ENV_DEFAULTS = {
|
|
3
|
+
JWT_SECRET: "test-jwt-secret-at-least-32-characters-long",
|
|
4
|
+
KUMIKO_SECRETS_MASTER_KEY_V1: "a3VtaWtvLXNjaGVtYS1lbnYtZGVmYXVsdC0zMmJ5dGU=",
|
|
5
|
+
KUMIKO_SECRETS_MASTER_KEY_CURRENT_VERSION: "1",
|
|
6
|
+
} as const satisfies Record<string, string>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { SCHEMA_ENV_DEFAULTS } from "./schema-env-defaults-values";
|
|
2
|
+
|
|
3
|
+
// Mirrors authEmailPasswordEnvSchema's JWT_SECRET.min(32) / jwt.ts's HS256
|
|
4
|
+
// floor: a too-short value from a stray .env is replaced, not kept, same as
|
|
5
|
+
// the app-local preloads this centralizes did before it.
|
|
6
|
+
const MIN_JWT_SECRET_LENGTH = 32;
|
|
7
|
+
|
|
8
|
+
function isTooShortJwtSecret(key: string, value: string | undefined): boolean {
|
|
9
|
+
return key === "JWT_SECRET" && (!value || value.length < MIN_JWT_SECRET_LENGTH);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
for (const [key, value] of Object.entries(SCHEMA_ENV_DEFAULTS)) {
|
|
13
|
+
if (isTooShortJwtSecret(key, process.env[key])) {
|
|
14
|
+
process.env[key] = value;
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
process.env[key] ??= value;
|
|
18
|
+
}
|
|
@@ -4,7 +4,6 @@ export const SERVICE_ENV_DEFAULTS = {
|
|
|
4
4
|
REDIS_URL: "redis://localhost:16379",
|
|
5
5
|
MEILI_URL: "http://localhost:17700",
|
|
6
6
|
MEILI_MASTER_KEY: "kumiko-dev-key",
|
|
7
|
-
JWT_SECRET: "test-jwt-secret-at-least-32-characters-long",
|
|
8
7
|
MINIO_ENDPOINT: "http://localhost:19000",
|
|
9
8
|
MINIO_ACCESS_KEY: "kumiko",
|
|
10
9
|
MINIO_SECRET_KEY: "kumiko-dev-secret",
|
package/src/scaffold.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// @runtime tooling
|
|
2
|
+
import { REAL_PROVIDERS_ENV } from "@cosmicdrift/kumiko-framework/testing/real-providers";
|
|
2
3
|
import { BUNFIG_FILES, renderBunfigFiles, TEST_TIMEOUT_MS } from "./bunfig";
|
|
3
|
-
import { REAL_PROVIDERS_ENV } from "./e2e/constants";
|
|
4
4
|
|
|
5
5
|
export type ScaffoldTestSetup = {
|
|
6
6
|
readonly files: Readonly<Record<string, string>>;
|
package/src/seed-tenant.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
createSystemUser,
|
|
8
8
|
type SessionUser,
|
|
9
9
|
type TenantId,
|
|
10
|
+
tenantChannel,
|
|
10
11
|
type WriteResult,
|
|
11
12
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
12
13
|
import type { TestStack } from "@cosmicdrift/kumiko-framework/stack";
|
|
@@ -177,6 +178,13 @@ export async function seedTenant(
|
|
|
177
178
|
const rows = persist ? await persistTenantRows(write, opts) : lightTenantRows(opts);
|
|
178
179
|
const { id } = rows;
|
|
179
180
|
|
|
181
|
+
// setupTestStack subscribes events.sse only to test tenant 1; a seeded tenant has a fresh random id.
|
|
182
|
+
stack.sseBroker.addClient(
|
|
183
|
+
tenantChannel(id),
|
|
184
|
+
(event) => stack.events.sse.push(event),
|
|
185
|
+
() => {},
|
|
186
|
+
);
|
|
187
|
+
|
|
180
188
|
const admin = withSession(rows.admin, id, [ROLES.TenantAdmin]);
|
|
181
189
|
const tenant: SeededTenant = {
|
|
182
190
|
id,
|