@elench/testkit 0.1.76 → 0.1.78
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 +49 -30
- package/lib/app/doctor.mjs +43 -0
- package/lib/config/runtime.test.mjs +2 -2
- package/lib/config-api/index.d.ts +226 -78
- package/lib/config-api/index.mjs +137 -271
- package/lib/config-api/index.test.mjs +347 -155
- package/lib/config-api/profiles.mjs +640 -0
- package/lib/coverage/index.test.mjs +2 -2
- package/lib/shared/build-config.test.mjs +1 -1
- package/lib/shared/configured-steps.mjs +9 -7
- package/lib/shared/configured-steps.test.mjs +3 -3
- package/node_modules/@elench/next-analysis/package.json +1 -1
- package/node_modules/@elench/testkit-bridge/package.json +2 -2
- package/node_modules/@elench/testkit-protocol/package.json +1 -1
- package/node_modules/@elench/ts-analysis/package.json +1 -1
- package/package.json +5 -5
|
@@ -1,35 +1,301 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
2
|
import {
|
|
3
|
-
|
|
3
|
+
app,
|
|
4
|
+
database,
|
|
4
5
|
defineConfig,
|
|
5
6
|
defineFile,
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
nextBuild,
|
|
9
|
-
nodeToolchain,
|
|
10
|
-
nodeApp,
|
|
11
|
-
postgresDatabase,
|
|
12
|
-
postgresFixture,
|
|
13
|
-
schemaSql,
|
|
14
|
-
seedCommand,
|
|
15
|
-
seedModule,
|
|
16
|
-
stepsBuild,
|
|
17
|
-
templateDatabase,
|
|
18
|
-
tscBuild,
|
|
19
|
-
verifyCommand,
|
|
20
|
-
verifyModule,
|
|
7
|
+
profiles,
|
|
8
|
+
toolchain,
|
|
21
9
|
} from "./index.mjs";
|
|
10
|
+
import { clearRuntimeContext, registerRuntimeContext } from "./runtime.mjs";
|
|
22
11
|
|
|
23
|
-
describe("config
|
|
12
|
+
describe("config api", () => {
|
|
24
13
|
it("defines repo config plainly", () => {
|
|
25
14
|
expect(defineConfig({ execution: { workers: 4 } })).toEqual({
|
|
26
15
|
execution: { workers: 4 },
|
|
27
16
|
});
|
|
28
17
|
});
|
|
29
18
|
|
|
30
|
-
it("
|
|
31
|
-
const profile =
|
|
32
|
-
|
|
19
|
+
it("builds raw HTTP profiles with deterministic forwarded headers", () => {
|
|
20
|
+
const profile = profiles.raw({
|
|
21
|
+
headers: {
|
|
22
|
+
contentTypeJson: true,
|
|
23
|
+
forwardedFor: "deterministic",
|
|
24
|
+
values: { "X-Testkit-Mode": "raw" },
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const headers = profile.headers?.(null, { env: { BASE: "http://api.test" } });
|
|
29
|
+
const rawHeaders = profile.rawHeaders?.(null, { env: { BASE: "http://api.test" } });
|
|
30
|
+
|
|
31
|
+
expect(headers).toMatchObject({
|
|
32
|
+
"Content-Type": "application/json",
|
|
33
|
+
"X-Testkit-Mode": "raw",
|
|
34
|
+
});
|
|
35
|
+
expect(headers["X-Forwarded-For"]).toMatch(/^10\.\d+\.\d+\.\d+$/);
|
|
36
|
+
expect(rawHeaders).toEqual(headers);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("builds session profiles that perform bootstrap/login and derive auth/session headers", () => {
|
|
40
|
+
const requests = [];
|
|
41
|
+
registerRuntimeContext({
|
|
42
|
+
env: {
|
|
43
|
+
BASE: "http://api.test",
|
|
44
|
+
routeParams: { "x-route": "route-a" },
|
|
45
|
+
},
|
|
46
|
+
http: {
|
|
47
|
+
post(url, body, params) {
|
|
48
|
+
requests.push({ url, body, params });
|
|
49
|
+
if (url.endsWith("/signup")) {
|
|
50
|
+
return {
|
|
51
|
+
status: 201,
|
|
52
|
+
body: JSON.stringify({ data: { organizations: [{ id: "org-signup" }] } }),
|
|
53
|
+
headers: {
|
|
54
|
+
"set-cookie": ["fixture_session=signup-token; Path=/", "fixture_refresh=signup-refresh; Path=/"],
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
status: 200,
|
|
60
|
+
body: JSON.stringify({ data: { organizations: [{ id: "org-123" }] } }),
|
|
61
|
+
headers: {
|
|
62
|
+
"set-cookie": ["fixture_session=jwt-123; Path=/", "fixture_refresh=refresh-123; Path=/"],
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const profile = profiles.session({
|
|
70
|
+
actor: {
|
|
71
|
+
bootstrap: {
|
|
72
|
+
path: "/signup",
|
|
73
|
+
expect: [201, 409],
|
|
74
|
+
body: ({ actor }) => ({ email: `${actor}@example.com` }),
|
|
75
|
+
},
|
|
76
|
+
login: {
|
|
77
|
+
path: "/login",
|
|
78
|
+
expect: 200,
|
|
79
|
+
body: ({ actor }) => ({ email: `${actor}@example.com` }),
|
|
80
|
+
},
|
|
81
|
+
session: {
|
|
82
|
+
cookies: {
|
|
83
|
+
jwt: "fixture_session",
|
|
84
|
+
refreshToken: "fixture_refresh",
|
|
85
|
+
},
|
|
86
|
+
fields: {
|
|
87
|
+
organizationId: "data.organizations[0].id",
|
|
88
|
+
},
|
|
89
|
+
auth: {
|
|
90
|
+
source: { key: "jwt" },
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
headers: {
|
|
95
|
+
contentTypeJson: true,
|
|
96
|
+
forwardedFor: "deterministic",
|
|
97
|
+
fromSession: [{ header: "X-Organization-Id", field: "organizationId" }],
|
|
98
|
+
values: ({ actor }) => ({ "X-Testkit-Actor": actor || "primary" }),
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const session = profile.auth.setup({
|
|
103
|
+
env: { BASE: "http://api.test", routeParams: { "x-route": "route-a" } },
|
|
104
|
+
});
|
|
105
|
+
const authHeaders = profile.auth.headers(session, { env: { BASE: "http://api.test" } });
|
|
106
|
+
const requestHeaders = profile.headers(session, { env: { BASE: "http://api.test" } });
|
|
107
|
+
|
|
108
|
+
expect(requests).toHaveLength(2);
|
|
109
|
+
expect(requests[0].params.headers["x-route"]).toBe("route-a");
|
|
110
|
+
expect(session).toEqual({
|
|
111
|
+
jwt: "jwt-123",
|
|
112
|
+
refreshToken: "refresh-123",
|
|
113
|
+
organizationId: "org-123",
|
|
114
|
+
});
|
|
115
|
+
expect(authHeaders).toEqual({
|
|
116
|
+
Authorization: "Bearer jwt-123",
|
|
117
|
+
});
|
|
118
|
+
expect(requestHeaders).toMatchObject({
|
|
119
|
+
"Content-Type": "application/json",
|
|
120
|
+
"X-Organization-Id": "org-123",
|
|
121
|
+
"X-Testkit-Actor": "primary",
|
|
122
|
+
});
|
|
123
|
+
expect(requestHeaders["X-Forwarded-For"]).toMatch(/^10\.\d+\.\d+\.\d+$/);
|
|
124
|
+
clearRuntimeContext();
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("builds local-json profile presets that derive session, multi-actor, and raw variants", () => {
|
|
128
|
+
const requests = [];
|
|
129
|
+
registerRuntimeContext({
|
|
130
|
+
env: {
|
|
131
|
+
BASE: "http://api.test",
|
|
132
|
+
},
|
|
133
|
+
http: {
|
|
134
|
+
post(url, body) {
|
|
135
|
+
const payload = JSON.parse(body);
|
|
136
|
+
requests.push({ url, payload });
|
|
137
|
+
const actor = payload.email.startsWith("primary")
|
|
138
|
+
? "primary"
|
|
139
|
+
: payload.email.startsWith("user-a")
|
|
140
|
+
? "userA"
|
|
141
|
+
: "userB";
|
|
142
|
+
return {
|
|
143
|
+
status: url.endsWith("/signup") ? 201 : 200,
|
|
144
|
+
body: JSON.stringify({
|
|
145
|
+
data: {
|
|
146
|
+
organizations: [{ id: `org-${actor}` }],
|
|
147
|
+
},
|
|
148
|
+
}),
|
|
149
|
+
headers: {
|
|
150
|
+
"set-cookie": [
|
|
151
|
+
`fixture_session=jwt-${actor}; Path=/`,
|
|
152
|
+
`fixture_refresh=refresh-${actor}; Path=/`,
|
|
153
|
+
],
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
const auth = profiles.localJson({
|
|
161
|
+
password: "TestkitPass2026",
|
|
162
|
+
identities: {
|
|
163
|
+
primary: {
|
|
164
|
+
email: "primary@example.com",
|
|
165
|
+
name: "Primary User",
|
|
166
|
+
organizationName: "Primary Org",
|
|
167
|
+
},
|
|
168
|
+
userA: {
|
|
169
|
+
email: "user-a@example.com",
|
|
170
|
+
name: "User A",
|
|
171
|
+
organizationName: "Org A",
|
|
172
|
+
},
|
|
173
|
+
userB: {
|
|
174
|
+
email: "user-b@example.com",
|
|
175
|
+
name: "User B",
|
|
176
|
+
organizationName: "Org B",
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
session: {
|
|
180
|
+
authCookie: "fixture_session",
|
|
181
|
+
refreshCookie: "fixture_refresh",
|
|
182
|
+
organizationIdPath: "data.organizations[0].id",
|
|
183
|
+
},
|
|
184
|
+
headers: {
|
|
185
|
+
contentTypeJson: true,
|
|
186
|
+
forwardedFor: "deterministic",
|
|
187
|
+
organization: "X-Organization-Id",
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
const defaultProfile = auth.session();
|
|
192
|
+
const defaultSetup = defaultProfile.auth.setup({ env: { BASE: "http://api.test" } });
|
|
193
|
+
expect(defaultSetup).toEqual({
|
|
194
|
+
jwt: "jwt-primary",
|
|
195
|
+
refreshToken: "refresh-primary",
|
|
196
|
+
organizationId: "org-primary",
|
|
197
|
+
});
|
|
198
|
+
expect(defaultProfile.auth.headers(defaultSetup)).toEqual({
|
|
199
|
+
Authorization: "Bearer jwt-primary",
|
|
200
|
+
});
|
|
201
|
+
expect(defaultProfile.headers(defaultSetup, { env: { BASE: "http://api.test" } })).toMatchObject({
|
|
202
|
+
"Content-Type": "application/json",
|
|
203
|
+
"X-Organization-Id": "org-primary",
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
const dualProfile = auth.multiActor({
|
|
207
|
+
primaryActor: "userA",
|
|
208
|
+
actors: ["userA", "userB"],
|
|
209
|
+
});
|
|
210
|
+
const dualSetup = dualProfile.auth.setup({ env: { BASE: "http://api.test" } });
|
|
211
|
+
expect(dualSetup.userA.organizationId).toBe("org-userA");
|
|
212
|
+
expect(dualSetup.userB.organizationId).toBe("org-userB");
|
|
213
|
+
expect(dualProfile.auth.headers(dualSetup)).toEqual({
|
|
214
|
+
Authorization: "Bearer jwt-userA",
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
const rawProfile = auth.raw();
|
|
218
|
+
expect(rawProfile.rawHeaders(null, { env: { BASE: "http://api.test" } })).toMatchObject({
|
|
219
|
+
"Content-Type": "application/json",
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
expect(requests.map((entry) => entry.url)).toEqual([
|
|
223
|
+
"http://api.test/api/v1/auth/signup",
|
|
224
|
+
"http://api.test/api/v1/auth/login",
|
|
225
|
+
"http://api.test/api/v1/auth/signup",
|
|
226
|
+
"http://api.test/api/v1/auth/login",
|
|
227
|
+
"http://api.test/api/v1/auth/signup",
|
|
228
|
+
"http://api.test/api/v1/auth/login",
|
|
229
|
+
]);
|
|
230
|
+
clearRuntimeContext();
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it("builds multi-actor profiles and derives headers from the primary actor by default", () => {
|
|
234
|
+
const responses = {
|
|
235
|
+
alpha: {
|
|
236
|
+
status: 200,
|
|
237
|
+
body: JSON.stringify({ data: { organizations: [{ id: "org-alpha" }] } }),
|
|
238
|
+
headers: { "set-cookie": "fixture_session=token-alpha; Path=/" },
|
|
239
|
+
},
|
|
240
|
+
beta: {
|
|
241
|
+
status: 200,
|
|
242
|
+
body: JSON.stringify({ data: { organizations: [{ id: "org-beta" }] } }),
|
|
243
|
+
headers: { "set-cookie": "fixture_session=token-beta; Path=/" },
|
|
244
|
+
},
|
|
245
|
+
};
|
|
246
|
+
registerRuntimeContext({
|
|
247
|
+
env: {
|
|
248
|
+
BASE: "http://api.test",
|
|
249
|
+
},
|
|
250
|
+
http: {
|
|
251
|
+
post(_url, body) {
|
|
252
|
+
const payload = JSON.parse(body);
|
|
253
|
+
return payload.email.startsWith("alpha") ? responses.alpha : responses.beta;
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
const profile = profiles.multiActor({
|
|
259
|
+
primaryActor: "userA",
|
|
260
|
+
actors: {
|
|
261
|
+
userA: {
|
|
262
|
+
login: {
|
|
263
|
+
path: "/login",
|
|
264
|
+
body: () => ({ email: "alpha@example.com" }),
|
|
265
|
+
},
|
|
266
|
+
session: {
|
|
267
|
+
cookies: { jwt: "fixture_session" },
|
|
268
|
+
fields: { organizationId: "data.organizations[0].id" },
|
|
269
|
+
auth: { source: { key: "jwt" } },
|
|
270
|
+
},
|
|
271
|
+
},
|
|
272
|
+
userB: {
|
|
273
|
+
login: {
|
|
274
|
+
path: "/login",
|
|
275
|
+
body: () => ({ email: "beta@example.com" }),
|
|
276
|
+
},
|
|
277
|
+
session: {
|
|
278
|
+
cookies: { jwt: "fixture_session" },
|
|
279
|
+
fields: { organizationId: "data.organizations[0].id" },
|
|
280
|
+
auth: { source: { key: "jwt" } },
|
|
281
|
+
},
|
|
282
|
+
},
|
|
283
|
+
},
|
|
284
|
+
headers: {
|
|
285
|
+
fromSession: [{ header: "X-Organization-Id", field: "organizationId" }],
|
|
286
|
+
},
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
const setupData = profile.auth.setup({ env: { BASE: "http://api.test" } });
|
|
290
|
+
expect(setupData.userA.organizationId).toBe("org-alpha");
|
|
291
|
+
expect(setupData.userB.organizationId).toBe("org-beta");
|
|
292
|
+
expect(profile.auth.headers(setupData)).toEqual({
|
|
293
|
+
Authorization: "Bearer token-alpha",
|
|
294
|
+
});
|
|
295
|
+
expect(profile.headers(setupData, { env: { BASE: "http://api.test" } })).toMatchObject({
|
|
296
|
+
"X-Organization-Id": "org-alpha",
|
|
297
|
+
});
|
|
298
|
+
clearRuntimeContext();
|
|
33
299
|
});
|
|
34
300
|
|
|
35
301
|
it("defines file-local metadata plainly", () => {
|
|
@@ -40,13 +306,13 @@ describe("config helpers", () => {
|
|
|
40
306
|
});
|
|
41
307
|
|
|
42
308
|
it("builds a Next app preset for dev mode", () => {
|
|
43
|
-
const config =
|
|
309
|
+
const config = app.next({ port: 3000 });
|
|
44
310
|
|
|
45
311
|
expect(config.local.start).toBe("./node_modules/.bin/next dev -p {port}");
|
|
46
312
|
});
|
|
47
313
|
|
|
48
314
|
it("builds a Next app preset for start mode with managed runtime env defaults", () => {
|
|
49
|
-
const config =
|
|
315
|
+
const config = app.next({ cwd: "frontend", port: 3000, mode: "start" });
|
|
50
316
|
|
|
51
317
|
expect(config.local.start).toBe("./node_modules/.bin/next start --port {port}");
|
|
52
318
|
expect(config.local.env).toMatchObject({
|
|
@@ -63,7 +329,7 @@ describe("config helpers", () => {
|
|
|
63
329
|
});
|
|
64
330
|
|
|
65
331
|
it("allows Next start apps to disable managed builds explicitly", () => {
|
|
66
|
-
const config =
|
|
332
|
+
const config = app.next({ cwd: "frontend", port: 3000, mode: "start", build: null });
|
|
67
333
|
|
|
68
334
|
expect(config.runtime.build).toBeNull();
|
|
69
335
|
expect(config.local.env).toMatchObject({
|
|
@@ -73,7 +339,7 @@ describe("config helpers", () => {
|
|
|
73
339
|
});
|
|
74
340
|
|
|
75
341
|
it("builds a Node app preset with tsc build defaults", () => {
|
|
76
|
-
const config =
|
|
342
|
+
const config = app.node({ port: 3000, entry: "src/server.ts" });
|
|
77
343
|
|
|
78
344
|
expect(config.local.start).toBe("node {prepareDir}/dist/server.js");
|
|
79
345
|
expect(config.runtime.build).toEqual({
|
|
@@ -87,88 +353,22 @@ describe("config helpers", () => {
|
|
|
87
353
|
});
|
|
88
354
|
|
|
89
355
|
it("builds node toolchain profiles with a node kind", () => {
|
|
90
|
-
expect(
|
|
356
|
+
expect(toolchain.node({ node: "20.19.5", install: "download" })).toEqual({
|
|
91
357
|
kind: "node",
|
|
92
358
|
node: "20.19.5",
|
|
93
359
|
install: "download",
|
|
94
360
|
});
|
|
95
361
|
});
|
|
96
362
|
|
|
97
|
-
it("builds
|
|
98
|
-
expect(tscBuild({ entry: "src/server.ts", outDir: "build" })).toEqual({
|
|
99
|
-
kind: "tsc",
|
|
100
|
-
cwd: undefined,
|
|
101
|
-
entry: "src/server.ts",
|
|
102
|
-
tsconfig: "tsconfig.json",
|
|
103
|
-
outDir: "build",
|
|
104
|
-
inputs: undefined,
|
|
105
|
-
});
|
|
106
|
-
expect(nextBuild({ cwd: "frontend" })).toEqual({
|
|
107
|
-
kind: "next",
|
|
108
|
-
cwd: "frontend",
|
|
109
|
-
distDir: "dist",
|
|
110
|
-
tsconfig: "tsconfig.json",
|
|
111
|
-
inputs: undefined,
|
|
112
|
-
});
|
|
363
|
+
it("builds declarative postgres database templates from plain objects", () => {
|
|
113
364
|
expect(
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
inputs: ["scripts/prepare.mjs"],
|
|
121
|
-
steps: [
|
|
122
|
-
{
|
|
123
|
-
kind: "command",
|
|
124
|
-
cmd: "node scripts/prepare.mjs",
|
|
125
|
-
cwd: undefined,
|
|
126
|
-
inputs: undefined,
|
|
365
|
+
database.postgres({
|
|
366
|
+
template: {
|
|
367
|
+
inputs: ["db/schema.sql", "scripts/seed.ts"],
|
|
368
|
+
schema: "db/schema.sql",
|
|
369
|
+
seed: { kind: "command", run: "npm run db:seed" },
|
|
370
|
+
verify: { kind: "module", target: "scripts/verify.ts#verifySeed" },
|
|
127
371
|
},
|
|
128
|
-
],
|
|
129
|
-
});
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it("emits semantic database template steps using the underlying step shapes", () => {
|
|
133
|
-
expect(schemaSql("db/schema.sql")).toEqual({
|
|
134
|
-
kind: "sql-file",
|
|
135
|
-
path: "db/schema.sql",
|
|
136
|
-
cwd: undefined,
|
|
137
|
-
inputs: undefined,
|
|
138
|
-
});
|
|
139
|
-
expect(seedCommand("npm run db:seed")).toEqual({
|
|
140
|
-
kind: "command",
|
|
141
|
-
cmd: "npm run db:seed",
|
|
142
|
-
cwd: undefined,
|
|
143
|
-
inputs: undefined,
|
|
144
|
-
});
|
|
145
|
-
expect(seedModule("scripts/seed.ts#seed")).toEqual({
|
|
146
|
-
kind: "module",
|
|
147
|
-
specifier: "scripts/seed.ts#seed",
|
|
148
|
-
cwd: undefined,
|
|
149
|
-
inputs: undefined,
|
|
150
|
-
});
|
|
151
|
-
expect(verifyCommand("npm run db:verify")).toEqual({
|
|
152
|
-
kind: "command",
|
|
153
|
-
cmd: "npm run db:verify",
|
|
154
|
-
cwd: undefined,
|
|
155
|
-
inputs: undefined,
|
|
156
|
-
});
|
|
157
|
-
expect(verifyModule("scripts/verify.ts#verify")).toEqual({
|
|
158
|
-
kind: "module",
|
|
159
|
-
specifier: "scripts/verify.ts#verify",
|
|
160
|
-
cwd: undefined,
|
|
161
|
-
inputs: undefined,
|
|
162
|
-
});
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
it("builds declarative template databases from schema, seed, and verify intents", () => {
|
|
166
|
-
expect(
|
|
167
|
-
templateDatabase({
|
|
168
|
-
inputs: ["db/schema.sql", "scripts/seed.ts"],
|
|
169
|
-
schema: "db/schema.sql",
|
|
170
|
-
seed: seedCommand("npm run db:seed"),
|
|
171
|
-
verify: verifyModule("scripts/verify.ts#verifySeed"),
|
|
172
372
|
})
|
|
173
373
|
).toEqual({
|
|
174
374
|
provider: "local",
|
|
@@ -178,24 +378,18 @@ describe("config helpers", () => {
|
|
|
178
378
|
{
|
|
179
379
|
kind: "sql-file",
|
|
180
380
|
path: "db/schema.sql",
|
|
181
|
-
cwd: undefined,
|
|
182
|
-
inputs: undefined,
|
|
183
381
|
},
|
|
184
382
|
],
|
|
185
383
|
seed: [
|
|
186
384
|
{
|
|
187
385
|
kind: "command",
|
|
188
|
-
|
|
189
|
-
cwd: undefined,
|
|
190
|
-
inputs: undefined,
|
|
386
|
+
run: "npm run db:seed",
|
|
191
387
|
},
|
|
192
388
|
],
|
|
193
389
|
verify: [
|
|
194
390
|
{
|
|
195
391
|
kind: "module",
|
|
196
|
-
|
|
197
|
-
cwd: undefined,
|
|
198
|
-
inputs: undefined,
|
|
392
|
+
target: "scripts/verify.ts#verifySeed",
|
|
199
393
|
},
|
|
200
394
|
],
|
|
201
395
|
},
|
|
@@ -204,9 +398,11 @@ describe("config helpers", () => {
|
|
|
204
398
|
|
|
205
399
|
it("prepends schema before explicit migrate steps and normalizes singletons to arrays", () => {
|
|
206
400
|
expect(
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
401
|
+
database.postgres({
|
|
402
|
+
template: {
|
|
403
|
+
schema: { kind: "sql-file", path: "db/schema.sql", cwd: "db" },
|
|
404
|
+
migrate: { kind: "command", run: "echo migrate" },
|
|
405
|
+
},
|
|
210
406
|
})
|
|
211
407
|
).toEqual({
|
|
212
408
|
provider: "local",
|
|
@@ -217,13 +413,10 @@ describe("config helpers", () => {
|
|
|
217
413
|
kind: "sql-file",
|
|
218
414
|
path: "db/schema.sql",
|
|
219
415
|
cwd: "db",
|
|
220
|
-
inputs: undefined,
|
|
221
416
|
},
|
|
222
417
|
{
|
|
223
418
|
kind: "command",
|
|
224
|
-
|
|
225
|
-
cwd: undefined,
|
|
226
|
-
inputs: undefined,
|
|
419
|
+
run: "echo migrate",
|
|
227
420
|
},
|
|
228
421
|
],
|
|
229
422
|
seed: [],
|
|
@@ -232,46 +425,9 @@ describe("config helpers", () => {
|
|
|
232
425
|
});
|
|
233
426
|
});
|
|
234
427
|
|
|
235
|
-
it("builds
|
|
236
|
-
expect(postgresDatabase({ reset: false })).toEqual({
|
|
237
|
-
provider: "local",
|
|
238
|
-
reset: false,
|
|
239
|
-
});
|
|
240
|
-
expect(
|
|
241
|
-
templateDatabase({
|
|
242
|
-
reset: true,
|
|
243
|
-
schema: "db/schema.sql",
|
|
244
|
-
seed: seedCommand("npm run db:seed"),
|
|
245
|
-
})
|
|
246
|
-
).toEqual({
|
|
247
|
-
provider: "local",
|
|
248
|
-
reset: true,
|
|
249
|
-
template: {
|
|
250
|
-
inputs: undefined,
|
|
251
|
-
migrate: [
|
|
252
|
-
{
|
|
253
|
-
kind: "sql-file",
|
|
254
|
-
path: "db/schema.sql",
|
|
255
|
-
cwd: undefined,
|
|
256
|
-
inputs: undefined,
|
|
257
|
-
},
|
|
258
|
-
],
|
|
259
|
-
seed: [
|
|
260
|
-
{
|
|
261
|
-
kind: "command",
|
|
262
|
-
cmd: "npm run db:seed",
|
|
263
|
-
cwd: undefined,
|
|
264
|
-
inputs: undefined,
|
|
265
|
-
},
|
|
266
|
-
],
|
|
267
|
-
verify: [],
|
|
268
|
-
},
|
|
269
|
-
});
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
it("builds support database presets and env bindings", () => {
|
|
428
|
+
it("builds support database presets and expands database env bindings declaratively", () => {
|
|
273
429
|
expect(
|
|
274
|
-
|
|
430
|
+
database.fixture({
|
|
275
431
|
reset: true,
|
|
276
432
|
})
|
|
277
433
|
).toEqual({
|
|
@@ -285,7 +441,19 @@ describe("config helpers", () => {
|
|
|
285
441
|
reset: true,
|
|
286
442
|
},
|
|
287
443
|
});
|
|
288
|
-
|
|
444
|
+
|
|
445
|
+
const config = app.node({
|
|
446
|
+
port: 3000,
|
|
447
|
+
env: {
|
|
448
|
+
values: { API_KEY: "test" },
|
|
449
|
+
databases: {
|
|
450
|
+
onix: { service: "catalog", prefix: "ONIX" },
|
|
451
|
+
},
|
|
452
|
+
},
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
expect(config.local.env).toEqual({
|
|
456
|
+
API_KEY: "test",
|
|
289
457
|
ONIX_DATABASE_HOST: "{dbHost:catalog}",
|
|
290
458
|
ONIX_DATABASE_PORT: "{dbPort:catalog}",
|
|
291
459
|
ONIX_DATABASE_NAME: "{dbName:catalog}",
|
|
@@ -295,12 +463,25 @@ describe("config helpers", () => {
|
|
|
295
463
|
});
|
|
296
464
|
});
|
|
297
465
|
|
|
466
|
+
it("rejects top-level database template lifecycle fields", () => {
|
|
467
|
+
expect(() =>
|
|
468
|
+
database.postgres({
|
|
469
|
+
schema: "db/schema.sql",
|
|
470
|
+
})
|
|
471
|
+
).toThrow(/no longer accepts top-level "schema"/);
|
|
472
|
+
expect(() =>
|
|
473
|
+
database.fixture({
|
|
474
|
+
seed: { kind: "command", run: "npm run db:seed" },
|
|
475
|
+
})
|
|
476
|
+
).toThrow(/no longer accepts top-level "seed"/);
|
|
477
|
+
});
|
|
478
|
+
|
|
298
479
|
it("does not leak preset-only helper fields into node app configs", () => {
|
|
299
|
-
const config =
|
|
480
|
+
const config = app.node({
|
|
300
481
|
port: 3000,
|
|
301
482
|
entry: "src/server.ts",
|
|
302
483
|
buildInputs: ["src", "package.json"],
|
|
303
|
-
env: { API_KEY: "test" },
|
|
484
|
+
env: { values: { API_KEY: "test" } },
|
|
304
485
|
readyPath: "/live",
|
|
305
486
|
});
|
|
306
487
|
|
|
@@ -310,4 +491,15 @@ describe("config helpers", () => {
|
|
|
310
491
|
expect(config.local.env).toEqual({ API_KEY: "test" });
|
|
311
492
|
expect(config.local.readyUrl).toBe("http://127.0.0.1:{port}/live");
|
|
312
493
|
});
|
|
494
|
+
|
|
495
|
+
it("rejects flat preset env maps in favor of env.values and env.databases", () => {
|
|
496
|
+
expect(() =>
|
|
497
|
+
app.node({
|
|
498
|
+
port: 3000,
|
|
499
|
+
env: {
|
|
500
|
+
API_KEY: "test",
|
|
501
|
+
},
|
|
502
|
+
})
|
|
503
|
+
).toThrow(/Preset env only supports "values" and "databases"/);
|
|
504
|
+
});
|
|
313
505
|
});
|