@geekmidas/constructs 10.0.0-alpha.21 → 10.0.0-alpha.22
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 +29 -23
- package/dist/{Endpoint-BxkRWQ8G.d.mts → Endpoint-Bmi2-PBN.d.mts} +2 -2
- package/dist/{Endpoint-BxkRWQ8G.d.mts.map → Endpoint-Bmi2-PBN.d.mts.map} +1 -1
- package/dist/{Endpoint-DbYQJgb-.d.cts → Endpoint-CjvraSfp.d.cts} +2 -2
- package/dist/{Endpoint-DbYQJgb-.d.cts.map → Endpoint-CjvraSfp.d.cts.map} +1 -1
- package/dist/{EndpointFactory-T06kTFHg.d.mts → EndpointFactory-BFfvgko6.d.mts} +2 -2
- package/dist/{EndpointFactory-DE6nKtFZ.d.cts.map → EndpointFactory-BFfvgko6.d.mts.map} +1 -1
- package/dist/{EndpointFactory-DE6nKtFZ.d.cts → EndpointFactory-vfC3NiJi.d.cts} +2 -2
- package/dist/{EndpointFactory-T06kTFHg.d.mts.map → EndpointFactory-vfC3NiJi.d.cts.map} +1 -1
- package/dist/Queue-BB2-DgqJ.cjs +123 -0
- package/dist/Queue-BB2-DgqJ.cjs.map +1 -0
- package/dist/Queue-Dfumm1WR.mjs +118 -0
- package/dist/Queue-Dfumm1WR.mjs.map +1 -0
- package/dist/Topic-BaNCgP61.cjs +107 -0
- package/dist/Topic-BaNCgP61.cjs.map +1 -0
- package/dist/Topic-QqnO_6Dl.mjs +102 -0
- package/dist/Topic-QqnO_6Dl.mjs.map +1 -0
- package/dist/adaptors/aws.d.cts +1 -1
- package/dist/adaptors/aws.d.mts +1 -1
- package/dist/adaptors/hono.d.cts +1 -1
- package/dist/adaptors/hono.d.mts +1 -1
- package/dist/adaptors/testing.cjs +136 -32
- package/dist/adaptors/testing.cjs.map +1 -1
- package/dist/adaptors/testing.d.cts +94 -13
- package/dist/adaptors/testing.d.cts.map +1 -1
- package/dist/adaptors/testing.d.mts +94 -13
- package/dist/adaptors/testing.d.mts.map +1 -1
- package/dist/adaptors/testing.mjs +133 -33
- package/dist/adaptors/testing.mjs.map +1 -1
- package/dist/{auth-BlO8MUkR.d.cts → auth-C_fHD1We.d.cts} +9 -1
- package/dist/{auth-BlO8MUkR.d.cts.map → auth-C_fHD1We.d.cts.map} +1 -1
- package/dist/{auth-CHWIdcQO.d.mts → auth-D_Ntp1Nj.d.mts} +9 -1
- package/dist/{auth-CHWIdcQO.d.mts.map → auth-D_Ntp1Nj.d.mts.map} +1 -1
- package/dist/auth.cjs +15 -2
- package/dist/auth.cjs.map +1 -1
- package/dist/auth.d.cts +1 -1
- package/dist/auth.d.mts +1 -1
- package/dist/auth.mjs +15 -2
- package/dist/auth.mjs.map +1 -1
- package/dist/endpoints/index.d.cts +2 -2
- package/dist/endpoints/index.d.mts +2 -2
- package/dist/queue/index.cjs +3 -116
- package/dist/queue/index.cjs.map +1 -1
- package/dist/queue/index.mjs +1 -114
- package/dist/queue/index.mjs.map +1 -1
- package/dist/rest-api.d.cts +2 -2
- package/dist/rest-api.d.mts +2 -2
- package/dist/topic/index.cjs +3 -100
- package/dist/topic/index.cjs.map +1 -1
- package/dist/topic/index.mjs +1 -98
- package/dist/topic/index.mjs.map +1 -1
- package/package.json +31 -31
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { t as StandardSchemaV1 } from "../index-CeEtxDmK.mjs";
|
|
2
2
|
import { r as Function } from "../index-DU3Twmwh.mjs";
|
|
3
3
|
import { t as HttpMethod } from "../types-DMuSQeLL.mjs";
|
|
4
|
-
import { c as EndpointSchemas, i as Endpoint } from "../Endpoint-
|
|
4
|
+
import { c as EndpointSchemas, i as Endpoint } from "../Endpoint-Bmi2-PBN.mjs";
|
|
5
5
|
import { t as Queue } from "../Queue-BoUA-e83.mjs";
|
|
6
6
|
import { t as Subscriber } from "../Subscriber-CIO3pwji.mjs";
|
|
7
7
|
import { t as KyselyDatabase } from "../kysely-D3-G5b0j.mjs";
|
|
@@ -186,20 +186,70 @@ type TestSubscriberRequest<TEventPublisher extends EventPublisher<any> | undefin
|
|
|
186
186
|
services?: ServiceRecord<TServices>;
|
|
187
187
|
};
|
|
188
188
|
//#endregion
|
|
189
|
+
//#region src/testing/manifest.d.ts
|
|
190
|
+
/**
|
|
191
|
+
* The test manifest: what `gkm test` knows about an app, written down for the
|
|
192
|
+
* suite it starts.
|
|
193
|
+
*
|
|
194
|
+
* `gkm test` discovers the constructs, reconciles the test stage and resolves
|
|
195
|
+
* its environment — then, until this existed, threw all of it away and left a
|
|
196
|
+
* test to declare it again by hand. The manifest keeps it: which construct and
|
|
197
|
+
* endpoint is exported where, and the environment the test stage resolved,
|
|
198
|
+
* under the keys the constructs themselves derive. A feature test is built from
|
|
199
|
+
* it, so nothing a test says can drift from what the app declares.
|
|
200
|
+
*/
|
|
201
|
+
/** Where an export lives: what an import statement has to say. */
|
|
202
|
+
interface TestManifestSource {
|
|
203
|
+
/** Absolute path to the module. */
|
|
204
|
+
file: string;
|
|
205
|
+
/** The name it is exported as. */
|
|
206
|
+
export: string;
|
|
207
|
+
}
|
|
208
|
+
interface TestManifest {
|
|
209
|
+
/** The stage the environment was resolved for — `test`. */
|
|
210
|
+
stage: string;
|
|
211
|
+
/** Every construct the app declares, by id. */
|
|
212
|
+
constructs: Record<string, {
|
|
213
|
+
kind: string;
|
|
214
|
+
source: TestManifestSource;
|
|
215
|
+
}>;
|
|
216
|
+
/** Every endpoint, and the surface that serves it. */
|
|
217
|
+
endpoints: {
|
|
218
|
+
surface: string;
|
|
219
|
+
source: TestManifestSource;
|
|
220
|
+
}[];
|
|
221
|
+
/**
|
|
222
|
+
* The test stage's environment — reconcile's output, keyed as the
|
|
223
|
+
* constructs derive their keys.
|
|
224
|
+
*/
|
|
225
|
+
env: Record<string, string>;
|
|
226
|
+
}
|
|
227
|
+
/** The environment variable `gkm test` names the manifest in. */
|
|
228
|
+
export declare const TEST_MANIFEST_ENV = "GKM_TEST_MANIFEST";
|
|
229
|
+
/**
|
|
230
|
+
* Read a test manifest: from a path, a `file:` URL, or — given nothing — the
|
|
231
|
+
* one `gkm test` named in `GKM_TEST_MANIFEST`.
|
|
232
|
+
*/
|
|
233
|
+
export declare function loadTestManifest(from?: string | URL): TestManifest;
|
|
234
|
+
/** A feature test ran without a manifest to build itself from. */
|
|
235
|
+
export declare class NoTestManifest extends Error {
|
|
236
|
+
constructor();
|
|
237
|
+
}
|
|
238
|
+
//#endregion
|
|
189
239
|
//#region src/testing/featureTest.d.ts
|
|
190
|
-
interface FeatureTestOptions<TBrowser extends Browser
|
|
240
|
+
interface FeatureTestOptions<TBrowser extends Browser> {
|
|
191
241
|
/**
|
|
192
|
-
*
|
|
193
|
-
*
|
|
194
|
-
* Discovery reads exports, as the build does.
|
|
242
|
+
* What the app declares and the environment its test stage resolved.
|
|
243
|
+
* Defaults to the manifest `gkm test` names in `GKM_TEST_MANIFEST`.
|
|
195
244
|
*/
|
|
196
|
-
|
|
197
|
-
/** The
|
|
198
|
-
database?: KyselyDatabase<DB>;
|
|
199
|
-
/** The browser each test gets — the app's subclass, with its clients. */
|
|
245
|
+
manifest?: TestManifest;
|
|
246
|
+
/** The browser each test gets — the generated one, with the app's clients. */
|
|
200
247
|
browser?: new () => TBrowser;
|
|
201
|
-
/**
|
|
202
|
-
|
|
248
|
+
/**
|
|
249
|
+
* The database a test receives as `db`, by construct id. Defaults to the
|
|
250
|
+
* one the app's endpoints name with `.database()`.
|
|
251
|
+
*/
|
|
252
|
+
database?: string;
|
|
203
253
|
}
|
|
204
254
|
interface FeatureContext<TBrowser extends Browser, DB> {
|
|
205
255
|
/** This test's browser, already the global `fetch`. */
|
|
@@ -208,6 +258,31 @@ interface FeatureContext<TBrowser extends Browser, DB> {
|
|
|
208
258
|
db: Kysely<DB>;
|
|
209
259
|
/** The mail sent to an address during this test, read from Mailpit. */
|
|
210
260
|
mailbox: (address: string) => Mailbox;
|
|
261
|
+
/**
|
|
262
|
+
* What this test published to a topic or enqueued on a queue, in order.
|
|
263
|
+
* Nothing is delivered — a subscriber or a worker is tested on its own, by
|
|
264
|
+
* handing it events — so this is where publishing is asserted.
|
|
265
|
+
*/
|
|
266
|
+
published: (channel: {
|
|
267
|
+
id: string;
|
|
268
|
+
}) => PublishedMessage[];
|
|
269
|
+
/**
|
|
270
|
+
* A subscriber, run on its own — handed events rather than delivered them —
|
|
271
|
+
* with this test's services: its databases resolve to this test's
|
|
272
|
+
* transactions, as an endpoint's do.
|
|
273
|
+
*/
|
|
274
|
+
subscriber: <S extends Subscriber<any, any, any, any, any, any>>(subscriber: S) => SubscriberAdaptorOf<S>;
|
|
275
|
+
/** A queue's worker, run on its own with this test's services. */
|
|
276
|
+
queue: <Q extends Queue<any, any, any, any>>(queue: Q) => QueueAdaptorOf<Q>;
|
|
277
|
+
}
|
|
278
|
+
type SubscriberAdaptorOf<S> = S extends Subscriber<infer TServices, infer TLogger, infer OutSchema, infer TEventPublisher, infer TEventPublisherServiceName, infer TSubscribedEvents> ? TestSubscriberAdaptor<TServices, TLogger, OutSchema, TEventPublisher, TEventPublisherServiceName, TSubscribedEvents> : never;
|
|
279
|
+
type QueueAdaptorOf<Q> = Q extends Queue<infer TName, infer TMessage, infer TServices, infer TLogger> ? TestQueueAdaptor<TName, TMessage, TServices, TLogger> : never;
|
|
280
|
+
/** The schema a database construct was declared with — what `db` is typed by. */
|
|
281
|
+
type DatabaseOf<C> = C extends KyselyDatabase<infer DB, any> ? DB : unknown;
|
|
282
|
+
/** One message as it was published: its type and its payload. */
|
|
283
|
+
interface PublishedMessage {
|
|
284
|
+
type: string;
|
|
285
|
+
payload: unknown;
|
|
211
286
|
}
|
|
212
287
|
type FeatureFn<TBrowser extends Browser, DB> = (context: FeatureContext<TBrowser, DB>) => unknown;
|
|
213
288
|
interface FeatureIt<TBrowser extends Browser, DB> {
|
|
@@ -215,7 +290,7 @@ interface FeatureIt<TBrowser extends Browser, DB> {
|
|
|
215
290
|
only(name: string, fn: FeatureFn<TBrowser, DB>, timeout?: number): void;
|
|
216
291
|
skip(name: string, fn: FeatureFn<TBrowser, DB>, timeout?: number): void;
|
|
217
292
|
}
|
|
218
|
-
export declare function featureTest<TBrowser extends Browser = Browser, DB = unknown>(options
|
|
293
|
+
export declare function featureTest<TBrowser extends Browser = Browser, DB = unknown>(options?: FeatureTestOptions<TBrowser>): FeatureIt<TBrowser, DB>;
|
|
219
294
|
/** A request reached an in-process server without a test to belong to. */
|
|
220
295
|
export declare class UnknownTestContext extends Error {
|
|
221
296
|
readonly url: string;
|
|
@@ -226,6 +301,12 @@ export declare class UnknownTestContext extends Error {
|
|
|
226
301
|
export declare class NoInbox extends Error {
|
|
227
302
|
constructor();
|
|
228
303
|
}
|
|
304
|
+
/** `featureTest({ database })` named a database the app does not declare. */
|
|
305
|
+
export declare class UnknownDatabase extends Error {
|
|
306
|
+
readonly id: string;
|
|
307
|
+
readonly declared: string[];
|
|
308
|
+
constructor(id: string, declared: string[]);
|
|
309
|
+
}
|
|
229
310
|
//#endregion
|
|
230
|
-
export { type CreateMswHandlersOptions, type CreateMswHandlersResult, type FeatureContext, type FeatureIt, type FeatureTestOptions, type RegisterContextOptions, TEST_CONTEXT_HEADER };
|
|
311
|
+
export { type CreateMswHandlersOptions, type CreateMswHandlersResult, type DatabaseOf, type FeatureContext, type FeatureIt, type FeatureTestOptions, type RegisterContextOptions, TEST_CONTEXT_HEADER, type TestManifest, type TestManifestSource };
|
|
231
312
|
//# sourceMappingURL=testing.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing.d.mts","names":["TestBrowser"],"sources":["../../src/endpoints/MswEndpointAdaptor.ts","../../src/endpoints/TestEndpointAdaptor.ts","../../src/functions/TestFunctionAdaptor.ts","../../src/queue/TestQueueAdaptor.ts","../../src/subscribers/TestSubscriberAdaptor.ts","../../src/testing/featureTest.ts"],"mappings":"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
|
|
1
|
+
{"version":3,"file":"testing.d.mts","names":["TestBrowser"],"sources":["../../src/endpoints/MswEndpointAdaptor.ts","../../src/endpoints/TestEndpointAdaptor.ts","../../src/functions/TestFunctionAdaptor.ts","../../src/queue/TestQueueAdaptor.ts","../../src/subscribers/TestSubscriberAdaptor.ts","../../src/testing/manifest.ts","../../src/testing/featureTest.ts"],"mappings":"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"}
|
|
@@ -4,6 +4,8 @@ import { n as ResponseBuilder, t as Endpoint } from "../Endpoint-DCr5tlbJ.mjs";
|
|
|
4
4
|
import { Email } from "../email.mjs";
|
|
5
5
|
import { KyselyDatabase } from "../database/kysely.mjs";
|
|
6
6
|
import { c as createObjectHeaders, l as publishConstructEvents, r as createCookieHeaderAccessor, u as publishEvents } from "../lazyAccessors-BHtcqgz2.mjs";
|
|
7
|
+
import { t as Queue } from "../Queue-Dfumm1WR.mjs";
|
|
8
|
+
import { t as Topic } from "../Topic-QqnO_6Dl.mjs";
|
|
7
9
|
import { t as HonoEndpoint } from "../HonoEndpointAdaptor-Be6UdK0r.mjs";
|
|
8
10
|
import { n as executeWithAuditTransaction } from "../processAudits-BpRlPQMW.mjs";
|
|
9
11
|
import { provideKey } from "@geekmidas/manifest";
|
|
@@ -20,6 +22,8 @@ import { createMailbox } from "@geekmidas/testkit/mailbox";
|
|
|
20
22
|
import { TransactionRegistry } from "@geekmidas/testkit/transactions";
|
|
21
23
|
import { setupServer } from "msw/node";
|
|
22
24
|
import { afterAll, beforeAll, test } from "vitest";
|
|
25
|
+
import { readFileSync } from "node:fs";
|
|
26
|
+
import { fileURLToPath } from "node:url";
|
|
23
27
|
//#region src/endpoints/MswEndpointAdaptor.ts
|
|
24
28
|
const MSW_HTTP_METHODS = [
|
|
25
29
|
"get",
|
|
@@ -415,6 +419,38 @@ var TestSubscriberAdaptor = class TestSubscriberAdaptor {
|
|
|
415
419
|
}
|
|
416
420
|
};
|
|
417
421
|
//#endregion
|
|
422
|
+
//#region src/testing/manifest.ts
|
|
423
|
+
/**
|
|
424
|
+
* The test manifest: what `gkm test` knows about an app, written down for the
|
|
425
|
+
* suite it starts.
|
|
426
|
+
*
|
|
427
|
+
* `gkm test` discovers the constructs, reconciles the test stage and resolves
|
|
428
|
+
* its environment — then, until this existed, threw all of it away and left a
|
|
429
|
+
* test to declare it again by hand. The manifest keeps it: which construct and
|
|
430
|
+
* endpoint is exported where, and the environment the test stage resolved,
|
|
431
|
+
* under the keys the constructs themselves derive. A feature test is built from
|
|
432
|
+
* it, so nothing a test says can drift from what the app declares.
|
|
433
|
+
*/
|
|
434
|
+
/** The environment variable `gkm test` names the manifest in. */
|
|
435
|
+
const TEST_MANIFEST_ENV = "GKM_TEST_MANIFEST";
|
|
436
|
+
/**
|
|
437
|
+
* Read a test manifest: from a path, a `file:` URL, or — given nothing — the
|
|
438
|
+
* one `gkm test` named in `GKM_TEST_MANIFEST`.
|
|
439
|
+
*/
|
|
440
|
+
function loadTestManifest(from) {
|
|
441
|
+
const location = from ?? process.env["GKM_TEST_MANIFEST"];
|
|
442
|
+
if (!location) throw new NoTestManifest();
|
|
443
|
+
const path = location instanceof URL || location.startsWith("file:") ? fileURLToPath(location) : location;
|
|
444
|
+
return JSON.parse(readFileSync(path, "utf-8"));
|
|
445
|
+
}
|
|
446
|
+
/** A feature test ran without a manifest to build itself from. */
|
|
447
|
+
var NoTestManifest = class extends Error {
|
|
448
|
+
constructor() {
|
|
449
|
+
super(`No test manifest: run the suite with \`gkm test\`, which writes one and names it in ${TEST_MANIFEST_ENV}.`);
|
|
450
|
+
this.name = "NoTestManifest";
|
|
451
|
+
}
|
|
452
|
+
};
|
|
453
|
+
//#endregion
|
|
418
454
|
//#region src/testing/featureTest.ts
|
|
419
455
|
/**
|
|
420
456
|
* `featureTest`: an app, driven the way it runs deployed.
|
|
@@ -425,15 +461,14 @@ var TestSubscriberAdaptor = class TestSubscriberAdaptor {
|
|
|
425
461
|
* from the real handler, for the test that made the request. Everything else
|
|
426
462
|
* with a real local equivalent — Postgres, Mailpit, MinIO — is used directly.
|
|
427
463
|
*
|
|
464
|
+
* It is built from the test manifest `gkm test` writes — the constructs and
|
|
465
|
+
* endpoints the app exports, and the environment the test stage resolved — so
|
|
466
|
+
* a test declares none of it. `gkm test` also generates the configured `it`
|
|
467
|
+
* and a `Browser` with a typed client per surface:
|
|
468
|
+
*
|
|
428
469
|
* ```ts
|
|
429
|
-
*
|
|
430
|
-
* export const it = featureTest({
|
|
431
|
-
* modules: import.meta.glob(['../../constructs/*.ts', '../src/endpoints/**\/*.ts'], { eager: true }),
|
|
432
|
-
* database,
|
|
433
|
-
* browser: Browser,
|
|
434
|
-
* });
|
|
470
|
+
* import { it } from '#test';
|
|
435
471
|
*
|
|
436
|
-
* // a test
|
|
437
472
|
* it('shows the signed-in user their profile', async ({ browser, mailbox }) => {
|
|
438
473
|
* await browser.auth.signIn.magicLink({ email: 'ada@shop.test' });
|
|
439
474
|
* await browser.visit((await mailbox('ada@shop.test').last()).link!);
|
|
@@ -445,17 +480,16 @@ var TestSubscriberAdaptor = class TestSubscriberAdaptor {
|
|
|
445
480
|
const contexts = /* @__PURE__ */ new Map();
|
|
446
481
|
/** Databases already resolving to test transactions — patched once each. */
|
|
447
482
|
const bound = /* @__PURE__ */ new WeakSet();
|
|
448
|
-
function featureTest(options) {
|
|
449
|
-
const
|
|
450
|
-
const envParser = new EnvironmentParser({ ...env });
|
|
451
|
-
const found = discover(options.modules);
|
|
483
|
+
function featureTest(options = {}) {
|
|
484
|
+
const manifest = options.manifest ?? loadTestManifest();
|
|
452
485
|
const BrowserClass = options.browser ?? Browser;
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
const inbox = found.emails.map((email) => env[provideKey(email.id, "inboxUrl")]).find(Boolean);
|
|
456
|
-
const readMail = inbox ? createMailbox({ inbox }) : void 0;
|
|
486
|
+
let app;
|
|
487
|
+
let network;
|
|
457
488
|
let restoreFetch = () => {};
|
|
458
|
-
beforeAll(() => {
|
|
489
|
+
beforeAll(async () => {
|
|
490
|
+
app = await load(manifest, options.database);
|
|
491
|
+
for (const database of app.databases) bindToTests(database);
|
|
492
|
+
network = setupServer(...surfaceHandlers(app.endpoints, manifest.env), ...app.auths.map((auth) => authHandler(auth, manifest.env, app.envParser)));
|
|
459
493
|
network.listen({ onUnhandledRequest(request, print) {
|
|
460
494
|
if (isLocal(new URL(request.url))) return;
|
|
461
495
|
print.error();
|
|
@@ -464,18 +498,20 @@ function featureTest(options) {
|
|
|
464
498
|
});
|
|
465
499
|
afterAll(() => {
|
|
466
500
|
restoreFetch();
|
|
467
|
-
network
|
|
501
|
+
network?.close();
|
|
468
502
|
});
|
|
469
503
|
const run = (fn) => async () => {
|
|
470
504
|
const id = randomUUID();
|
|
471
505
|
const state = {
|
|
472
506
|
transactions: new TransactionRegistry(),
|
|
473
|
-
discovery: new ServiceDiscovery(envParser),
|
|
507
|
+
discovery: new ServiceDiscovery(app.envParser),
|
|
474
508
|
surfaces: /* @__PURE__ */ new Map(),
|
|
475
509
|
auth: /* @__PURE__ */ new Map(),
|
|
476
|
-
addresses: /* @__PURE__ */ new Set()
|
|
510
|
+
addresses: /* @__PURE__ */ new Set(),
|
|
511
|
+
published: /* @__PURE__ */ new Map()
|
|
477
512
|
};
|
|
478
513
|
contexts.set(id, state);
|
|
514
|
+
await state.discovery.register(recorders(app.channels, state));
|
|
479
515
|
try {
|
|
480
516
|
await runInTestContext(id, async () => {
|
|
481
517
|
const browser = new BrowserClass();
|
|
@@ -483,12 +519,15 @@ function featureTest(options) {
|
|
|
483
519
|
try {
|
|
484
520
|
await fn({
|
|
485
521
|
browser,
|
|
486
|
-
db:
|
|
522
|
+
db: app.database ? await state.transactions.get(app.database.id, urlOf(app.database, app.envParser)) : void 0,
|
|
487
523
|
mailbox: (address) => {
|
|
488
|
-
if (!readMail) throw new NoInbox();
|
|
524
|
+
if (!app.readMail) throw new NoInbox();
|
|
489
525
|
state.addresses.add(address);
|
|
490
|
-
return readMail(address);
|
|
491
|
-
}
|
|
526
|
+
return app.readMail(address);
|
|
527
|
+
},
|
|
528
|
+
published: (channel) => [...state.published.get(channel.id) ?? []],
|
|
529
|
+
subscriber: (subscriber) => new TestSubscriberAdaptor(subscriber, state.discovery),
|
|
530
|
+
queue: (queue) => new TestQueueAdaptor(queue, state.discovery)
|
|
492
531
|
});
|
|
493
532
|
} finally {
|
|
494
533
|
restore();
|
|
@@ -496,7 +535,7 @@ function featureTest(options) {
|
|
|
496
535
|
});
|
|
497
536
|
} finally {
|
|
498
537
|
contexts.delete(id);
|
|
499
|
-
await Promise.all([state.transactions.rollbackAll(), ...[...state.addresses].map((address) => readMail?.(address).clear())]);
|
|
538
|
+
await Promise.all([state.transactions.rollbackAll(), ...[...state.addresses].map((address) => app.readMail?.(address).clear())]);
|
|
500
539
|
}
|
|
501
540
|
};
|
|
502
541
|
const it = ((name, fn, timeout) => test(name, run(fn), timeout));
|
|
@@ -504,17 +543,67 @@ function featureTest(options) {
|
|
|
504
543
|
it.skip = (name, fn, timeout) => test.skip(name, run(fn), timeout);
|
|
505
544
|
return it;
|
|
506
545
|
}
|
|
507
|
-
/**
|
|
508
|
-
|
|
509
|
-
|
|
546
|
+
/**
|
|
547
|
+
* Import what the manifest points at.
|
|
548
|
+
*
|
|
549
|
+
* The same modules the app runs, so a construct here *is* the app's construct —
|
|
550
|
+
* the one its endpoints were built from — never a copy declared again.
|
|
551
|
+
*/
|
|
552
|
+
async function load(manifest, database) {
|
|
553
|
+
const envParser = new EnvironmentParser({ ...manifest.env });
|
|
554
|
+
const imported = async ({ file, export: name }) => (await import(file))[name];
|
|
555
|
+
const constructs = await Promise.all(Object.values(manifest.constructs).map(({ source }) => imported(source)));
|
|
556
|
+
const endpoints = (await Promise.all(manifest.endpoints.map(({ source }) => imported(source)))).filter((value) => Endpoint.isEndpoint(value));
|
|
557
|
+
const unique = (values) => [...new Set(values)];
|
|
558
|
+
const databases = unique(constructs.filter((value) => value instanceof KyselyDatabase));
|
|
559
|
+
const auths = unique(constructs.filter((value) => value instanceof BetterAuth));
|
|
560
|
+
const emails = unique(constructs.filter((value) => value instanceof Email));
|
|
561
|
+
const channels = unique(constructs.filter((value) => value instanceof Topic || value instanceof Queue));
|
|
562
|
+
const inbox = emails.map((email) => manifest.env[provideKey(email.id, "inboxUrl")]).find(Boolean);
|
|
510
563
|
return {
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
564
|
+
envParser,
|
|
565
|
+
endpoints,
|
|
566
|
+
databases,
|
|
567
|
+
auths,
|
|
568
|
+
emails,
|
|
569
|
+
channels,
|
|
570
|
+
database: databaseOf(databases, endpoints, database),
|
|
571
|
+
...inbox ? { readMail: createMailbox({ inbox }) } : {}
|
|
515
572
|
};
|
|
516
573
|
}
|
|
517
574
|
/**
|
|
575
|
+
* A publisher that records rather than sends, under every name a topic or
|
|
576
|
+
* queue is injected by — `users`, and the older `usersPublisher`.
|
|
577
|
+
*/
|
|
578
|
+
function recorders(channels, state) {
|
|
579
|
+
return channels.flatMap((channel) => {
|
|
580
|
+
const publisher = { async publish(messages) {
|
|
581
|
+
state.published.set(channel.id, [...state.published.get(channel.id) ?? [], ...messages.map(({ type, payload }) => ({
|
|
582
|
+
type,
|
|
583
|
+
payload
|
|
584
|
+
}))]);
|
|
585
|
+
} };
|
|
586
|
+
return [channel.service.serviceName, channel.publisher.serviceName].map((serviceName) => ({
|
|
587
|
+
serviceName,
|
|
588
|
+
register: () => publisher
|
|
589
|
+
}));
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* The database a test sees as `db`: the one named, or else the one the
|
|
594
|
+
* endpoints were given with `.database()` — found by the service they hold,
|
|
595
|
+
* which is the construct's own.
|
|
596
|
+
*/
|
|
597
|
+
function databaseOf(databases, endpoints, named) {
|
|
598
|
+
if (named) {
|
|
599
|
+
const found = databases.find(({ id }) => id === named);
|
|
600
|
+
if (!found) throw new UnknownDatabase(named, databases.map(({ id }) => id));
|
|
601
|
+
return found;
|
|
602
|
+
}
|
|
603
|
+
const services = new Set(endpoints.map((e) => e.databaseService).filter(Boolean));
|
|
604
|
+
return databases.find((database) => services.has(database.service));
|
|
605
|
+
}
|
|
606
|
+
/**
|
|
518
607
|
* Make a database resolve, inside a test, to that test's transaction.
|
|
519
608
|
*
|
|
520
609
|
* Everything reaches a database through its service — an endpoint through
|
|
@@ -565,7 +654,7 @@ function surfaceHandlers(endpoints, env) {
|
|
|
565
654
|
/** An auth server, served at its URL, on the transaction of the test that asked. */
|
|
566
655
|
function authHandler(auth, env, envParser) {
|
|
567
656
|
const base = trim(env[provideKey(auth.id, "url")] ?? "");
|
|
568
|
-
return http.all(`${base}
|
|
657
|
+
return http.all(`${base}/*`, ({ request }) => serve(request, async (state) => {
|
|
569
658
|
let app = state.auth.get(auth);
|
|
570
659
|
if (!app) {
|
|
571
660
|
app = auth.server({
|
|
@@ -608,7 +697,18 @@ var NoInbox = class extends Error {
|
|
|
608
697
|
this.name = "NoInbox";
|
|
609
698
|
}
|
|
610
699
|
};
|
|
700
|
+
/** `featureTest({ database })` named a database the app does not declare. */
|
|
701
|
+
var UnknownDatabase = class extends Error {
|
|
702
|
+
id;
|
|
703
|
+
declared;
|
|
704
|
+
constructor(id, declared) {
|
|
705
|
+
super(`featureTest was asked for the database '${id}', and the app declares ${declared.length ? declared.map((d) => `'${d}'`).join(", ") : "none"}.`);
|
|
706
|
+
this.id = id;
|
|
707
|
+
this.declared = declared;
|
|
708
|
+
this.name = "UnknownDatabase";
|
|
709
|
+
}
|
|
710
|
+
};
|
|
611
711
|
//#endregion
|
|
612
|
-
export { NoInbox, TEST_CONTEXT_HEADER, TestEndpointAdaptor, TestFunctionAdaptor, TestQueueAdaptor, TestSubscriberAdaptor, UnknownTestContext, createMswHandlers, featureTest };
|
|
712
|
+
export { NoInbox, NoTestManifest, TEST_CONTEXT_HEADER, TEST_MANIFEST_ENV, TestEndpointAdaptor, TestFunctionAdaptor, TestQueueAdaptor, TestSubscriberAdaptor, UnknownDatabase, UnknownTestContext, createMswHandlers, featureTest, loadTestManifest };
|
|
613
713
|
|
|
614
714
|
//# sourceMappingURL=testing.mjs.map
|