@cosmicdrift/kumiko-testing 0.368.0 → 0.370.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/package.json +5 -5
- package/src/bunfig.ts +4 -1
- package/src/changes.json +18 -0
- package/src/e2e/define-app-e2e-config.ts +1 -1
- package/src/e2e/index.ts +1 -1
- package/src/e2e/screenshots.ts +1 -1
- package/src/helpers/closed-connection-error.ts +1 -1
- package/src/helpers/index.ts +22 -21
- package/src/helpers/stored-event-fixture.ts +20 -0
- package/src/preload/real.ts +1 -1
- package/src/scaffold.ts +54 -22
- package/src/seed-tenant.ts +1 -1
- package/src/setup-app-test-stack.ts +1 -1
- package/src/stack/drain-event-consumers.ts +1 -1
- package/src/stack/index.ts +8 -8
- package/src/stack/redis.ts +1 -1
- package/src/stack/test-stack.ts +29 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-testing",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.370.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>",
|
|
@@ -95,8 +95,8 @@
|
|
|
95
95
|
"kumiko-testing": "./bin/kumiko-testing.ts"
|
|
96
96
|
},
|
|
97
97
|
"dependencies": {
|
|
98
|
-
"@cosmicdrift/kumiko-framework": "0.
|
|
99
|
-
"@cosmicdrift/kumiko-types": "0.
|
|
98
|
+
"@cosmicdrift/kumiko-framework": "0.370.0",
|
|
99
|
+
"@cosmicdrift/kumiko-types": "0.370.0",
|
|
100
100
|
"zod": "^4.4.3"
|
|
101
101
|
},
|
|
102
102
|
"peerDependencies": {
|
|
@@ -128,8 +128,8 @@
|
|
|
128
128
|
}
|
|
129
129
|
},
|
|
130
130
|
"devDependencies": {
|
|
131
|
-
"@cosmicdrift/kumiko-bundled-features": "0.
|
|
132
|
-
"@cosmicdrift/kumiko-dev-server": "0.
|
|
131
|
+
"@cosmicdrift/kumiko-bundled-features": "0.370.0",
|
|
132
|
+
"@cosmicdrift/kumiko-dev-server": "0.370.0",
|
|
133
133
|
"@happy-dom/global-registrator": "^20.11.2",
|
|
134
134
|
"@playwright/test": "^1.62.1",
|
|
135
135
|
"@testing-library/dom": "^10.4.1",
|
package/src/bunfig.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
// @runtime tooling
|
|
2
|
+
// Pure TOML rendering with no test-stack imports: dev-server's managed-files
|
|
3
|
+
// sync renders and merges app bunfigs with it outside of any test run.
|
|
1
4
|
import { isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
|
|
2
|
-
import { E2E_TIMEOUT_MS } from "./e2e-timeouts";
|
|
5
|
+
import { E2E_TIMEOUT_MS } from "./e2e-timeouts.ts";
|
|
3
6
|
|
|
4
7
|
export const TEST_TIMEOUT_MS = {
|
|
5
8
|
unit: 5000,
|
package/src/changes.json
CHANGED
|
@@ -1,4 +1,22 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.370.0",
|
|
4
|
+
"type": "improvement",
|
|
5
|
+
"title": "setupTestStack binds the MFA token secrets",
|
|
6
|
+
"detail": "setupTestStack binds the auth-mfa token secrets from the stack's JWT secret, independent of the host env, so tests that mount auth-mfa without explicit secrets keep working."
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"version": "0.370.0",
|
|
10
|
+
"type": "improvement",
|
|
11
|
+
"title": "renderTestSetup writes the standard test scripts, bunfig.dom.toml and the helper homes",
|
|
12
|
+
"detail": "The scaffolded test setup now adds test:unit, test:integration:ci, test:dom and screenshots, writes bunfig.dom.toml (with @happy-dom/global-registrator as devDependency), e2e/_helpers/index.ts and src/__tests__/_setup.ts."
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"version": "0.370.0",
|
|
16
|
+
"type": "improvement",
|
|
17
|
+
"title": "createTestStoredEvent builds a StoredEvent fixture",
|
|
18
|
+
"detail": "createTestStoredEvent(overrides) from @cosmicdrift/kumiko-testing/helpers returns a complete StoredEvent with stable defaults, for hook tests that construct SaveContext or DeleteContext directly."
|
|
19
|
+
},
|
|
2
20
|
{
|
|
3
21
|
"version": "0.365.0",
|
|
4
22
|
"type": "fix",
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
type Project,
|
|
14
14
|
} from "@playwright/test";
|
|
15
15
|
import { E2E_TIMEOUT_MS } from "../e2e-timeouts";
|
|
16
|
-
import { isRealProviderRun } from "../helpers/real-providers.
|
|
16
|
+
import { isRealProviderRun } from "../helpers/real-providers.ts";
|
|
17
17
|
import { SERVICE_ENV_DEFAULTS } from "../preload/service-env-defaults-values";
|
|
18
18
|
import {
|
|
19
19
|
DESKTOP_VIEWPORT,
|
package/src/e2e/index.ts
CHANGED
package/src/e2e/screenshots.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { mkdirSync, statSync } from "node:fs";
|
|
|
3
3
|
import { dirname } from "node:path";
|
|
4
4
|
import { expect, type Page, errors as playwrightErrors, type Request } from "@playwright/test";
|
|
5
5
|
import { E2E_TIMEOUT_MS } from "../e2e-timeouts";
|
|
6
|
-
import { isRealProviderRun } from "../helpers/real-providers.
|
|
6
|
+
import { isRealProviderRun } from "../helpers/real-providers.ts";
|
|
7
7
|
import { DESKTOP_VIEWPORT } from "./constants";
|
|
8
8
|
import { pinEnglishLocale } from "./pin-english-locale";
|
|
9
9
|
import { requireScreenshotDir, SCREENSHOT_DIR_ENV } from "./screenshot-dir";
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
import { isClosedConnectionError } from "@cosmicdrift/kumiko-framework/internal/stack-support";
|
|
5
5
|
import postgres from "postgres";
|
|
6
|
-
import { requireEnv } from "../stack/db.
|
|
6
|
+
import { requireEnv } from "../stack/db.ts";
|
|
7
7
|
|
|
8
8
|
export function testDatabaseUrl(): string {
|
|
9
9
|
return requireEnv("TEST_DATABASE_URL");
|
package/src/helpers/index.ts
CHANGED
|
@@ -14,45 +14,45 @@ export {
|
|
|
14
14
|
resetPiiSubjectKmsForTests,
|
|
15
15
|
} from "@cosmicdrift/kumiko-framework/internal/testing";
|
|
16
16
|
|
|
17
|
-
export { rolesOf } from "./access-assertions.
|
|
18
|
-
export { expectError, expectSuccess } from "./assertions.
|
|
19
|
-
export { withBootValidatorFixture } from "./boot-validator-fixture.
|
|
17
|
+
export { rolesOf } from "./access-assertions.ts";
|
|
18
|
+
export { expectError, expectSuccess } from "./assertions.ts";
|
|
19
|
+
export { withBootValidatorFixture } from "./boot-validator-fixture.ts";
|
|
20
20
|
export {
|
|
21
21
|
captureClosedConnectionError,
|
|
22
22
|
terminateBackendsByApplicationName,
|
|
23
23
|
testDatabaseUrl,
|
|
24
|
-
} from "./closed-connection-error.
|
|
25
|
-
export { type ClearableTable, clearTables, resetTestTables } from "./db-cleanup.
|
|
24
|
+
} from "./closed-connection-error.ts";
|
|
25
|
+
export { type ClearableTable, clearTables, resetTestTables } from "./db-cleanup.ts";
|
|
26
26
|
export {
|
|
27
27
|
type E2EGeneratorOptions,
|
|
28
28
|
type E2ETestSpec,
|
|
29
29
|
type EditFillOp,
|
|
30
30
|
generateE2ESpec,
|
|
31
31
|
generateZodFixture,
|
|
32
|
-
} from "./e2e-generator.
|
|
33
|
-
export { expectErrorIncludes } from "./expect-error.
|
|
34
|
-
export { describeFileProviderContract } from "./file-provider-contract.
|
|
32
|
+
} from "./e2e-generator.ts";
|
|
33
|
+
export { expectErrorIncludes } from "./expect-error.ts";
|
|
34
|
+
export { describeFileProviderContract } from "./file-provider-contract.ts";
|
|
35
35
|
export {
|
|
36
36
|
getSetCookieRaw,
|
|
37
37
|
getSetCookies,
|
|
38
38
|
getSetCookieValue,
|
|
39
39
|
type ParsedSetCookie,
|
|
40
|
-
} from "./http-cookies.
|
|
41
|
-
export { createLateBoundHolder, type LateBoundHolder } from "./late-bound.
|
|
42
|
-
export { createManualClock, type ManualClock } from "./manual-clock.
|
|
43
|
-
export { buildMultipartBody, patchFileInstanceofForBunTest } from "./multipart-helper.
|
|
40
|
+
} from "./http-cookies.ts";
|
|
41
|
+
export { createLateBoundHolder, type LateBoundHolder } from "./late-bound.ts";
|
|
42
|
+
export { createManualClock, type ManualClock } from "./manual-clock.ts";
|
|
43
|
+
export { buildMultipartBody, patchFileInstanceofForBunTest } from "./multipart-helper.ts";
|
|
44
44
|
export {
|
|
45
45
|
createMutableMasterKeyProvider,
|
|
46
46
|
createTestEnvelopeCipher,
|
|
47
47
|
createTestMasterKeyProvider,
|
|
48
48
|
type MutableMasterKeyProvider,
|
|
49
|
-
} from "./mutable-master-key-provider.
|
|
49
|
+
} from "./mutable-master-key-provider.ts";
|
|
50
50
|
export {
|
|
51
51
|
createRecordingProvider,
|
|
52
52
|
type RecordingProvider,
|
|
53
|
-
} from "./observability-recorder.
|
|
54
|
-
export { isRealProviderRun, REAL_PROVIDERS_ENV, requireRealProviders } from "./real-providers.
|
|
55
|
-
export { deleteRows, seedRow, seedRows, updateRows } from "./seed.
|
|
53
|
+
} from "./observability-recorder.ts";
|
|
54
|
+
export { isRealProviderRun, REAL_PROVIDERS_ENV, requireRealProviders } from "./real-providers.ts";
|
|
55
|
+
export { deleteRows, seedRow, seedRows, updateRows } from "./seed.ts";
|
|
56
56
|
export {
|
|
57
57
|
sharedItemEntity,
|
|
58
58
|
sharedItemTable,
|
|
@@ -60,8 +60,9 @@ export {
|
|
|
60
60
|
sharedUserTable,
|
|
61
61
|
sharedWidgetEntity,
|
|
62
62
|
sharedWidgetTable,
|
|
63
|
-
} from "./shared-entities.
|
|
64
|
-
export {
|
|
65
|
-
export {
|
|
66
|
-
export {
|
|
67
|
-
export {
|
|
63
|
+
} from "./shared-entities.ts";
|
|
64
|
+
export { createTestStoredEvent } from "./stored-event-fixture.ts";
|
|
65
|
+
export { sleep } from "./utils.ts";
|
|
66
|
+
export { waitFor } from "./wait-for.ts";
|
|
67
|
+
export { withoutAmbientTemporal } from "./without-ambient-temporal.ts";
|
|
68
|
+
export { zipWithEntries } from "./zip-fixture.ts";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { StoredEvent } from "@cosmicdrift/kumiko-types/event-store-types";
|
|
2
|
+
|
|
3
|
+
// SaveContext and DeleteContext always carry the event that produced them, so a
|
|
4
|
+
// hand-built context in a hook test needs one too.
|
|
5
|
+
export function createTestStoredEvent(overrides: Partial<StoredEvent> = {}): StoredEvent {
|
|
6
|
+
return {
|
|
7
|
+
id: "test-event-1",
|
|
8
|
+
aggregateId: "test-aggregate-1",
|
|
9
|
+
aggregateType: "test-aggregate",
|
|
10
|
+
tenantId: "test-tenant-1",
|
|
11
|
+
version: 1,
|
|
12
|
+
type: "test:event:saved",
|
|
13
|
+
eventVersion: 1,
|
|
14
|
+
payload: {},
|
|
15
|
+
metadata: { userId: "test-user-1" },
|
|
16
|
+
createdAt: Temporal.Instant.from("2026-01-01T00:00:00Z"),
|
|
17
|
+
createdBy: "test-user-1",
|
|
18
|
+
...overrides,
|
|
19
|
+
};
|
|
20
|
+
}
|
package/src/preload/real.ts
CHANGED
package/src/scaffold.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// @runtime tooling
|
|
2
2
|
import { BUNFIG_FILES, renderBunfigFiles, TEST_TIMEOUT_MS } from "./bunfig";
|
|
3
|
-
import { REAL_PROVIDERS_ENV } from "./helpers/real-providers
|
|
3
|
+
import { REAL_PROVIDERS_ENV } from "./helpers/real-providers";
|
|
4
4
|
|
|
5
5
|
export type ScaffoldTestSetup = {
|
|
6
6
|
readonly files: Readonly<Record<string, string>>;
|
|
@@ -21,6 +21,8 @@ export const SCAFFOLD_E2E_PORT = 4174;
|
|
|
21
21
|
export const SCAFFOLD_INTEGRATION_PARALLEL = 4;
|
|
22
22
|
|
|
23
23
|
const PLAYWRIGHT_VERSION = "^1.62.1";
|
|
24
|
+
// bunfig.dom.toml preloads kumiko-testing's dom preload, which registers happy-dom.
|
|
25
|
+
const HAPPY_DOM_VERSION = "^20.11.2";
|
|
24
26
|
|
|
25
27
|
const TEST_RUNTIME_DIRECTIVE = "// @runtime test";
|
|
26
28
|
|
|
@@ -81,22 +83,8 @@ function renderE2eServer(appName: string): string {
|
|
|
81
83
|
function renderE2eSmokeSpec(): string {
|
|
82
84
|
return [
|
|
83
85
|
TEST_RUNTIME_DIRECTIVE,
|
|
84
|
-
'import type { SeedPart } from "@cosmicdrift/kumiko-testing";',
|
|
85
86
|
'import { expect, test } from "@cosmicdrift/kumiko-testing/e2e";',
|
|
86
|
-
'import
|
|
87
|
-
"",
|
|
88
|
-
`const TASK_CREATE = ${JSON.stringify(TASK_CREATE)};`,
|
|
89
|
-
"",
|
|
90
|
-
"function withTask(title: string): SeedPart {",
|
|
91
|
-
" return async ({ tenant }) => {",
|
|
92
|
-
' await tenant.api.writeOk(TASK_CREATE, { title, status: "todo", priority: 1 });',
|
|
93
|
-
" };",
|
|
94
|
-
"}",
|
|
95
|
-
"",
|
|
96
|
-
"async function openTasks(page: Page): Promise<void> {",
|
|
97
|
-
' await page.goto("/");',
|
|
98
|
-
' await page.getByRole("link", { name: "Tasks" }).first().click();',
|
|
99
|
-
"}",
|
|
87
|
+
'import { openTasks, withTask } from "./_helpers";',
|
|
100
88
|
"",
|
|
101
89
|
'test("a seeded task shows up on the tasks screen", async ({ seedTenant, page }) => {',
|
|
102
90
|
' const tenant = await seedTenant({ with: [withTask("Water the plants")] });',
|
|
@@ -117,6 +105,41 @@ function renderE2eSmokeSpec(): string {
|
|
|
117
105
|
].join("\n");
|
|
118
106
|
}
|
|
119
107
|
|
|
108
|
+
function renderE2eHelpers(): string {
|
|
109
|
+
return [
|
|
110
|
+
TEST_RUNTIME_DIRECTIVE,
|
|
111
|
+
'import type { SeedPart } from "@cosmicdrift/kumiko-testing";',
|
|
112
|
+
'import type { Page } from "@playwright/test";',
|
|
113
|
+
"",
|
|
114
|
+
`const TASK_CREATE = ${JSON.stringify(TASK_CREATE)};`,
|
|
115
|
+
"",
|
|
116
|
+
"export function withTask(title: string): SeedPart {",
|
|
117
|
+
" return async ({ tenant }) => {",
|
|
118
|
+
' await tenant.api.writeOk(TASK_CREATE, { title, status: "todo", priority: 1 });',
|
|
119
|
+
" };",
|
|
120
|
+
"}",
|
|
121
|
+
"",
|
|
122
|
+
"export async function openTasks(page: Page): Promise<void> {",
|
|
123
|
+
' await page.goto("/");',
|
|
124
|
+
' await page.getByRole("link", { name: "Tasks" }).first().click();',
|
|
125
|
+
"}",
|
|
126
|
+
"",
|
|
127
|
+
].join("\n");
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function renderIntegrationSetup(): string {
|
|
131
|
+
return [
|
|
132
|
+
'import { setupAppTestStack } from "@cosmicdrift/kumiko-testing";',
|
|
133
|
+
'import type { TestStack } from "@cosmicdrift/kumiko-testing/stack";',
|
|
134
|
+
'import { tasksFeature } from "../features/tasks";',
|
|
135
|
+
"",
|
|
136
|
+
"export function setupTasksStack(): Promise<TestStack> {",
|
|
137
|
+
" return setupAppTestStack([tasksFeature]);",
|
|
138
|
+
"}",
|
|
139
|
+
"",
|
|
140
|
+
].join("\n");
|
|
141
|
+
}
|
|
142
|
+
|
|
120
143
|
function renderUnitTest(): string {
|
|
121
144
|
return [
|
|
122
145
|
'import { describe, expect, test } from "bun:test";',
|
|
@@ -140,9 +163,9 @@ function renderUnitTest(): string {
|
|
|
140
163
|
function renderIntegrationTest(): string {
|
|
141
164
|
return [
|
|
142
165
|
'import { afterAll, beforeAll, describe, expect, test } from "bun:test";',
|
|
143
|
-
'import { seedTenant
|
|
166
|
+
'import { seedTenant } from "@cosmicdrift/kumiko-testing";',
|
|
144
167
|
'import type { TestStack } from "@cosmicdrift/kumiko-testing/stack";',
|
|
145
|
-
'import {
|
|
168
|
+
'import { setupTasksStack } from "./_setup";',
|
|
146
169
|
"",
|
|
147
170
|
`const TASK_CREATE = ${JSON.stringify(TASK_CREATE)};`,
|
|
148
171
|
`const TASK_LIST = ${JSON.stringify(TASK_LIST)};`,
|
|
@@ -152,7 +175,7 @@ function renderIntegrationTest(): string {
|
|
|
152
175
|
"let stack: TestStack;",
|
|
153
176
|
"",
|
|
154
177
|
"beforeAll(async () => {",
|
|
155
|
-
" stack = await
|
|
178
|
+
" stack = await setupTasksStack();",
|
|
156
179
|
"});",
|
|
157
180
|
"",
|
|
158
181
|
"afterAll(async () => {",
|
|
@@ -178,18 +201,25 @@ function renderIntegrationTest(): string {
|
|
|
178
201
|
|
|
179
202
|
export function renderTestSetup(input: RenderTestSetupInput): ScaffoldTestSetup {
|
|
180
203
|
const { appName, frameworkVersion, port = SCAFFOLD_E2E_PORT } = input;
|
|
204
|
+
const unitTestScript = `bun --config=${BUNFIG_FILES.unit} test --timeout=${TEST_TIMEOUT_MS.unit} --dots`;
|
|
205
|
+
const integrationTestScript = `bun kumiko-testing integration --parallel ${SCAFFOLD_INTEGRATION_PARALLEL}`;
|
|
181
206
|
return {
|
|
182
207
|
files: {
|
|
183
|
-
...renderBunfigFiles({ install: { linker: "hoisted" } }),
|
|
208
|
+
...renderBunfigFiles({ install: { linker: "hoisted" }, dom: true }),
|
|
184
209
|
"playwright.config.ts": renderPlaywrightConfig(port),
|
|
210
|
+
"e2e/_helpers/index.ts": renderE2eHelpers(),
|
|
185
211
|
"e2e/server.ts": renderE2eServer(appName),
|
|
186
212
|
"e2e/smoke.spec.ts": renderE2eSmokeSpec(),
|
|
213
|
+
"src/__tests__/_setup.ts": renderIntegrationSetup(),
|
|
187
214
|
"src/__tests__/run-config.test.ts": renderUnitTest(),
|
|
188
215
|
"src/__tests__/tasks.integration.test.ts": renderIntegrationTest(),
|
|
189
216
|
},
|
|
190
217
|
scripts: {
|
|
191
|
-
test:
|
|
192
|
-
"test:
|
|
218
|
+
test: unitTestScript,
|
|
219
|
+
"test:unit": unitTestScript,
|
|
220
|
+
"test:integration": integrationTestScript,
|
|
221
|
+
"test:integration:ci": integrationTestScript,
|
|
222
|
+
"test:dom": `bun --config=${BUNFIG_FILES.dom} test --timeout=${TEST_TIMEOUT_MS.unit} --dots .test.tsx`,
|
|
193
223
|
// bunfig.real.toml's pathIgnorePatterns can only blacklist (no "!" negation,
|
|
194
224
|
// verified against bun 1.4), so it can't express "match only *.real.test.ts"
|
|
195
225
|
// on its own — every *.test.ts file matches bun's default test glob too,
|
|
@@ -201,9 +231,11 @@ export function renderTestSetup(input: RenderTestSetupInput): ScaffoldTestSetup
|
|
|
201
231
|
"test:bunfig": "bun kumiko-testing bunfig --hoisted",
|
|
202
232
|
e2e: "bunx --bun playwright test",
|
|
203
233
|
"e2e:real": `${REAL_PROVIDERS_ENV}=1 bunx --bun playwright test`,
|
|
234
|
+
screenshots: "SCREENSHOT_DIR=docs/screenshots bunx --bun playwright test",
|
|
204
235
|
},
|
|
205
236
|
devDependencies: {
|
|
206
237
|
"@cosmicdrift/kumiko-testing": frameworkVersion,
|
|
238
|
+
"@happy-dom/global-registrator": HAPPY_DOM_VERSION,
|
|
207
239
|
"@playwright/test": PLAYWRIGHT_VERSION,
|
|
208
240
|
},
|
|
209
241
|
rulesMarkdown: RULES_MARKDOWN,
|
package/src/seed-tenant.ts
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
setupTestStackFromFeatures,
|
|
4
4
|
} from "@cosmicdrift/kumiko-dev-server/setup-test-stack-from-features";
|
|
5
5
|
import type { FeatureDefinition } from "@cosmicdrift/kumiko-framework/engine";
|
|
6
|
-
import { pushEntityProjectionTables, type TestStack } from "./stack/index.
|
|
6
|
+
import { pushEntityProjectionTables, type TestStack } from "./stack/index.ts";
|
|
7
7
|
|
|
8
8
|
export type SetupAppTestStackOptions = SetupTestStackFromFeaturesOptions & {
|
|
9
9
|
readonly registryTables?: boolean;
|
package/src/stack/index.ts
CHANGED
|
@@ -18,11 +18,11 @@ export {
|
|
|
18
18
|
type CreateTestDbOptions,
|
|
19
19
|
createTestDb,
|
|
20
20
|
type TestDb,
|
|
21
|
-
} from "./db.
|
|
22
|
-
export { drainEventConsumers } from "./drain-event-consumers.
|
|
23
|
-
export { createEventCollector, type EventCollector } from "./event-collector.
|
|
24
|
-
export { pushEntityProjectionTables } from "./push-entity-projection-tables.
|
|
25
|
-
export { createTestRedis, type TestRedis } from "./redis.
|
|
26
|
-
export { createRequestHelper, type RequestHelper } from "./request-helper.
|
|
27
|
-
export { resetEventStore } from "./table-helpers.
|
|
28
|
-
export { setupTestStack, type TestStack, type TestStackOptions } from "./test-stack.
|
|
21
|
+
} from "./db.ts";
|
|
22
|
+
export { drainEventConsumers } from "./drain-event-consumers.ts";
|
|
23
|
+
export { createEventCollector, type EventCollector } from "./event-collector.ts";
|
|
24
|
+
export { pushEntityProjectionTables } from "./push-entity-projection-tables.ts";
|
|
25
|
+
export { createTestRedis, type TestRedis } from "./redis.ts";
|
|
26
|
+
export { createRequestHelper, type RequestHelper } from "./request-helper.ts";
|
|
27
|
+
export { resetEventStore } from "./table-helpers.ts";
|
|
28
|
+
export { setupTestStack, type TestStack, type TestStackOptions } from "./test-stack.ts";
|
package/src/stack/redis.ts
CHANGED
package/src/stack/test-stack.ts
CHANGED
|
@@ -50,11 +50,11 @@ import type { SearchAdapter } from "@cosmicdrift/kumiko-framework/search";
|
|
|
50
50
|
import { createInMemorySearchAdapter } from "@cosmicdrift/kumiko-framework/search";
|
|
51
51
|
import { propertyOf } from "@cosmicdrift/kumiko-types/type-guards";
|
|
52
52
|
import type { Hono } from "hono";
|
|
53
|
-
import { createTestDb } from "./db.
|
|
54
|
-
import { createJobFailureTracker, drainJobs } from "./drain-jobs.
|
|
55
|
-
import { createEventCollector, type EventCollector } from "./event-collector.
|
|
56
|
-
import { createTestRedis, queueNamePrefixForTestRedis, type TestRedis } from "./redis.
|
|
57
|
-
import { createRequestHelper, type RequestHelper } from "./request-helper.
|
|
53
|
+
import { createTestDb } from "./db.ts";
|
|
54
|
+
import { createJobFailureTracker, drainJobs } from "./drain-jobs.ts";
|
|
55
|
+
import { createEventCollector, type EventCollector } from "./event-collector.ts";
|
|
56
|
+
import { createTestRedis, queueNamePrefixForTestRedis, type TestRedis } from "./redis.ts";
|
|
57
|
+
import { createRequestHelper, type RequestHelper } from "./request-helper.ts";
|
|
58
58
|
|
|
59
59
|
export type TestStack = {
|
|
60
60
|
app: Hono;
|
|
@@ -279,6 +279,29 @@ export type TestStackOptions = {
|
|
|
279
279
|
|
|
280
280
|
const DEFAULT_JWT_SECRET = "test-stack-secret-minimum-32-characters!!";
|
|
281
281
|
|
|
282
|
+
type MfaTokenSecretBinder = (
|
|
283
|
+
masterSecret: string,
|
|
284
|
+
env: Readonly<Record<string, string | undefined>>,
|
|
285
|
+
) => void;
|
|
286
|
+
|
|
287
|
+
// Mirrors bindMfaTokenSecretsFromFeature from bundled-features/auth-mfa,
|
|
288
|
+
// which this package cannot import: bundled-features is an optional peer.
|
|
289
|
+
function isMfaTokenSecretBinder(value: unknown): value is MfaTokenSecretBinder {
|
|
290
|
+
return typeof value === "function";
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// The env is empty on purpose: test secrets derive from jwtSecret,
|
|
294
|
+
// independent of the host env.
|
|
295
|
+
function bindMfaTokenSecrets(features: readonly FeatureDefinition[], jwtSecret: string): void {
|
|
296
|
+
for (const { exports } of features) {
|
|
297
|
+
if (typeof exports !== "object" || exports === null || !("bindTokenSecrets" in exports)) {
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
const { bindTokenSecrets } = exports;
|
|
301
|
+
if (isMfaTokenSecretBinder(bindTokenSecrets)) bindTokenSecrets(jwtSecret, {});
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
282
305
|
export async function setupTestStack(options: TestStackOptions): Promise<TestStack> {
|
|
283
306
|
const jwtSecret = options.jwtSecret ?? DEFAULT_JWT_SECRET;
|
|
284
307
|
const enabledHooks = options.systemHooks ?? ["sse", "search"];
|
|
@@ -298,6 +321,7 @@ export async function setupTestStack(options: TestStackOptions): Promise<TestSta
|
|
|
298
321
|
// Nav/workspace refs fail the prod boot too; the subset check tolerates refs into unmounted features.
|
|
299
322
|
validateNavBootSubset(options.features);
|
|
300
323
|
if (options.validateBoot === "full") validateBoot(options.features);
|
|
324
|
+
bindMfaTokenSecrets(options.features, jwtSecret);
|
|
301
325
|
|
|
302
326
|
// Forward db-name/persistent-flag through to createTestDb. The
|
|
303
327
|
// defaults (undefined dbName, persistent:false) keep the legacy
|