@classytic/arc-testkit 0.1.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 +65 -0
- package/dist/index.d.mts +54 -0
- package/dist/index.mjs +73 -0
- package/package.json +65 -0
package/README.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# @classytic/arc-testkit
|
|
2
|
+
|
|
3
|
+
> Boot a real [`@classytic/arc`](https://www.npmjs.com/package/@classytic/arc) app around the module(s) under test, on a real (in-memory) database.
|
|
4
|
+
|
|
5
|
+
The ecosystem's contributor contract for the `@classytic/arc-*` module fleet: **if your module boots green here, it composes into any arc host.**
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm i -D @classytic/arc-testkit @classytic/mongokit mongodb-memory-server
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
`@classytic/mongokit` + `mongodb-memory-server` are **optional peers** — needed only for the default backend, and dynamically imported so they never enter a production bundle. Pass your own `backend` to skip them entirely.
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { bootModuleApp } from '@classytic/arc-testkit';
|
|
19
|
+
import { createAccountingModule } from '@classytic/arc-accounting';
|
|
20
|
+
|
|
21
|
+
const t = await bootModuleApp(({ connection }) => [
|
|
22
|
+
createAccountingModule({ connection, permissions }),
|
|
23
|
+
]);
|
|
24
|
+
|
|
25
|
+
const res = await t.app.inject({ method: 'GET', url: '/accounting/accounts' });
|
|
26
|
+
expect(res.statusCode).toBe(200);
|
|
27
|
+
|
|
28
|
+
await t.close();
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The module input is a factory receiving `{ connection, mongoUri }` (or a plain module / array). It boots arc with `preset: 'testing'`, `auth: false` — override via `options.app`.
|
|
32
|
+
|
|
33
|
+
## Transactions
|
|
34
|
+
|
|
35
|
+
Modules that open multi-document transactions need a replica set:
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
const t = await bootModuleApp(({ connection }) => [createReservationModule({ connection, permissions })],
|
|
39
|
+
{ replset: true });
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Bring your own backend
|
|
43
|
+
|
|
44
|
+
The default backend is `mongoMemoryBackend()` from `@classytic/mongokit/testkit`. Override it to point at a shared server, a real Atlas cluster, or a different kit's backend — anything that satisfies the `TestBackend` shape (`setup() → { ctx: { connection, uri }, teardown }`):
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import { mongoMemoryBackend } from '@classytic/mongokit/testkit';
|
|
48
|
+
|
|
49
|
+
const t = await bootModuleApp(mods, {
|
|
50
|
+
backend: mongoMemoryBackend({ uri: process.env.ATLAS_TEST_URI }), // real cluster
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## API
|
|
55
|
+
|
|
56
|
+
| Export | |
|
|
57
|
+
| --- | --- |
|
|
58
|
+
| `bootModuleApp(modules, options?)` | boot arc around modules on a backend; returns `{ app, connection, mongoUri, close }` |
|
|
59
|
+
| `TestBackend` | the backend seam — `setup() → { ctx: { connection, uri }, teardown }` |
|
|
60
|
+
| `TestkitContext` | what a module factory receives: `{ connection, mongoUri }` |
|
|
61
|
+
| `BootModuleAppOptions` | `{ replset?, app?, backend? }` |
|
|
62
|
+
|
|
63
|
+
## License
|
|
64
|
+
|
|
65
|
+
MIT © Classytic
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { ArcModuleInput, CreateAppOptions } from "@classytic/arc/factory";
|
|
2
|
+
import { FastifyInstance } from "fastify";
|
|
3
|
+
import { Connection } from "mongoose";
|
|
4
|
+
|
|
5
|
+
//#region src/index.d.ts
|
|
6
|
+
/** The context a backend yields — a live connection + the URI it connects to. */
|
|
7
|
+
interface BackendContext {
|
|
8
|
+
connection: Connection;
|
|
9
|
+
uri: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* A pluggable database backend. Structurally satisfied by
|
|
13
|
+
* `@classytic/mongokit/testkit`'s `mongoMemoryBackend()` — no import of it in
|
|
14
|
+
* the type, so arc-testkit never hard-depends on the kit (one-way: arc-testkit
|
|
15
|
+
* consumes a backend by shape).
|
|
16
|
+
*/
|
|
17
|
+
interface TestBackend {
|
|
18
|
+
setup(): Promise<{
|
|
19
|
+
ctx: BackendContext;
|
|
20
|
+
teardown: () => Promise<void>;
|
|
21
|
+
}>;
|
|
22
|
+
}
|
|
23
|
+
/** What a module factory receives — the live connection + its URI. */
|
|
24
|
+
interface TestkitContext {
|
|
25
|
+
connection: Connection;
|
|
26
|
+
mongoUri: string;
|
|
27
|
+
}
|
|
28
|
+
type ModulesInput = ArcModuleInput | readonly ArcModuleInput[] | ((ctx: TestkitContext) => ArcModuleInput | readonly ArcModuleInput[] | Promise<ArcModuleInput | readonly ArcModuleInput[]>);
|
|
29
|
+
interface BootModuleAppOptions {
|
|
30
|
+
/**
|
|
31
|
+
* Use a single-node replica set instead of a standalone mongod. Required when
|
|
32
|
+
* the module under test opens multi-document transactions. Ignored when a
|
|
33
|
+
* custom `backend` is supplied. Default false.
|
|
34
|
+
*/
|
|
35
|
+
replset?: boolean;
|
|
36
|
+
/** Merged over the testkit defaults (`preset: 'testing'`, `auth: false`). */
|
|
37
|
+
app?: Partial<CreateAppOptions>;
|
|
38
|
+
/**
|
|
39
|
+
* Override the database backend. Default: `mongoMemoryBackend()` from
|
|
40
|
+
* `@classytic/mongokit/testkit`. Supply your own to point at a shared server,
|
|
41
|
+
* a real Atlas cluster, or a different kit's backend.
|
|
42
|
+
*/
|
|
43
|
+
backend?: TestBackend;
|
|
44
|
+
}
|
|
45
|
+
interface ModuleTestApp {
|
|
46
|
+
app: FastifyInstance;
|
|
47
|
+
connection: Connection;
|
|
48
|
+
mongoUri: string;
|
|
49
|
+
/** Close app → tear the backend down (disconnect + stop server). Idempotent. */
|
|
50
|
+
close(): Promise<void>;
|
|
51
|
+
}
|
|
52
|
+
declare function bootModuleApp(modules: ModulesInput, options?: BootModuleAppOptions): Promise<ModuleTestApp>;
|
|
53
|
+
//#endregion
|
|
54
|
+
export { BackendContext, BootModuleAppOptions, ModuleTestApp, ModulesInput, TestBackend, TestkitContext, bootModuleApp };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { createApp } from "@classytic/arc/factory";
|
|
2
|
+
//#region src/index.ts
|
|
3
|
+
/**
|
|
4
|
+
* `@classytic/arc-testkit` — boot a REAL arc app around the module(s) under
|
|
5
|
+
* test, on a real (in-memory) database. The ecosystem's contributor contract:
|
|
6
|
+
* **if your module boots green here, it composes into any arc host.**
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* const t = await bootModuleApp(({ connection }) => [
|
|
10
|
+
* createAccountingModule({ connection, permissions }),
|
|
11
|
+
* ]);
|
|
12
|
+
* const res = await t.app.inject({ method: 'GET', url: '/accounting/accounts' });
|
|
13
|
+
* await t.close();
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* This is a thin generic core: `bootModuleApp` resolves the module list, opens a
|
|
17
|
+
* database via a pluggable **backend**, and boots arc around it. The default
|
|
18
|
+
* backend is `mongoMemoryBackend()` from `@classytic/mongokit/testkit` — loaded
|
|
19
|
+
* via dynamic `import()` so `@classytic/mongokit` + `mongodb-memory-server` stay
|
|
20
|
+
* OPTIONAL peers (needed only when you use the default backend). Pass your own
|
|
21
|
+
* `backend` — e.g. one pointed at a shared server, or a real Atlas cluster — to
|
|
22
|
+
* override it.
|
|
23
|
+
*
|
|
24
|
+
* Modules usually need the live DB connection in their deps, so the primary
|
|
25
|
+
* input is a FACTORY receiving `{ connection, mongoUri }`. Plain module lists
|
|
26
|
+
* are also accepted.
|
|
27
|
+
*/
|
|
28
|
+
/** Load the default backend lazily so mongokit + memory-server stay optional. */
|
|
29
|
+
async function defaultBackend(replset) {
|
|
30
|
+
try {
|
|
31
|
+
const { mongoMemoryBackend } = await import("@classytic/mongokit/testkit");
|
|
32
|
+
return mongoMemoryBackend({ replset });
|
|
33
|
+
} catch (err) {
|
|
34
|
+
throw new Error(`[arc-testkit] the default backend needs \`@classytic/mongokit\` (>=3.19.0) and \`mongodb-memory-server\` installed as devDependencies. Install them, or pass your own \`backend\` to bootModuleApp. Original error: ${err.message}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
async function bootModuleApp(modules, options = {}) {
|
|
38
|
+
const { ctx, teardown } = await (options.backend ?? await defaultBackend(options.replset ?? false)).setup();
|
|
39
|
+
const testkitCtx = {
|
|
40
|
+
connection: ctx.connection,
|
|
41
|
+
mongoUri: ctx.uri
|
|
42
|
+
};
|
|
43
|
+
const resolved = typeof modules === "function" ? await modules(testkitCtx) : modules;
|
|
44
|
+
const list = Array.isArray(resolved) ? resolved : [resolved];
|
|
45
|
+
let app;
|
|
46
|
+
try {
|
|
47
|
+
app = await createApp({
|
|
48
|
+
preset: "testing",
|
|
49
|
+
auth: false,
|
|
50
|
+
sensible: false,
|
|
51
|
+
...options.app,
|
|
52
|
+
modules: list
|
|
53
|
+
});
|
|
54
|
+
await app.ready();
|
|
55
|
+
} catch (err) {
|
|
56
|
+
await teardown().catch(() => void 0);
|
|
57
|
+
throw err;
|
|
58
|
+
}
|
|
59
|
+
let closed = false;
|
|
60
|
+
return {
|
|
61
|
+
app,
|
|
62
|
+
connection: ctx.connection,
|
|
63
|
+
mongoUri: ctx.uri,
|
|
64
|
+
close: async () => {
|
|
65
|
+
if (closed) return;
|
|
66
|
+
closed = true;
|
|
67
|
+
await app.close().catch(() => void 0);
|
|
68
|
+
await teardown();
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
//#endregion
|
|
73
|
+
export { bootModuleApp };
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@classytic/arc-testkit",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Boot a real @classytic/arc app around the module(s) under test on in-memory MongoDB — the ecosystem test harness. If your arc module boots green here, it composes into any arc host.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./dist/index.d.mts",
|
|
9
|
+
"default": "./dist/index.mjs"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"main": "./dist/index.mjs",
|
|
13
|
+
"types": "./dist/index.d.mts",
|
|
14
|
+
"sideEffects": false,
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"access": "public"
|
|
21
|
+
},
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=22"
|
|
24
|
+
},
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "tsdown",
|
|
27
|
+
"typecheck": "tsc --noEmit",
|
|
28
|
+
"lint": "biome check src/",
|
|
29
|
+
"prepublishOnly": "node ../../scripts/prepublish-gate.mjs"
|
|
30
|
+
},
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"@classytic/arc": ">=2.20.0",
|
|
33
|
+
"@classytic/mongokit": ">=3.19.0",
|
|
34
|
+
"fastify": ">=5.0.0",
|
|
35
|
+
"mongodb-memory-server": ">=10.0.0",
|
|
36
|
+
"mongoose": ">=9.4.1"
|
|
37
|
+
},
|
|
38
|
+
"peerDependenciesMeta": {
|
|
39
|
+
"@classytic/mongokit": {
|
|
40
|
+
"optional": true
|
|
41
|
+
},
|
|
42
|
+
"mongodb-memory-server": {
|
|
43
|
+
"optional": true
|
|
44
|
+
},
|
|
45
|
+
"mongoose": {
|
|
46
|
+
"optional": true
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"keywords": [
|
|
50
|
+
"arc",
|
|
51
|
+
"fastify",
|
|
52
|
+
"testing",
|
|
53
|
+
"test-harness",
|
|
54
|
+
"mongodb",
|
|
55
|
+
"in-memory",
|
|
56
|
+
"classytic"
|
|
57
|
+
],
|
|
58
|
+
"license": "MIT",
|
|
59
|
+
"author": "Classytic",
|
|
60
|
+
"repository": {
|
|
61
|
+
"type": "git",
|
|
62
|
+
"url": "https://github.com/classytic/arc-ecosystem.git",
|
|
63
|
+
"directory": "packages/arc-testkit"
|
|
64
|
+
}
|
|
65
|
+
}
|