@endora-commerce/test-kit 0.100.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.
Files changed (39) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +108 -0
  3. package/dist/database/index.d.ts +17 -0
  4. package/dist/database/index.d.ts.map +1 -0
  5. package/dist/database/index.js +17 -0
  6. package/dist/database/index.js.map +1 -0
  7. package/dist/database/lease.d.ts +115 -0
  8. package/dist/database/lease.d.ts.map +1 -0
  9. package/dist/database/lease.js +204 -0
  10. package/dist/database/lease.js.map +1 -0
  11. package/dist/database/provision.d.ts +135 -0
  12. package/dist/database/provision.d.ts.map +1 -0
  13. package/dist/database/provision.js +457 -0
  14. package/dist/database/provision.js.map +1 -0
  15. package/dist/database/run-isolation.d.ts +356 -0
  16. package/dist/database/run-isolation.d.ts.map +1 -0
  17. package/dist/database/run-isolation.js +490 -0
  18. package/dist/database/run-isolation.js.map +1 -0
  19. package/dist/server/compose-test-server.d.ts +251 -0
  20. package/dist/server/compose-test-server.d.ts.map +1 -0
  21. package/dist/server/compose-test-server.js +359 -0
  22. package/dist/server/compose-test-server.js.map +1 -0
  23. package/dist/server/composition.d.ts +112 -0
  24. package/dist/server/composition.d.ts.map +1 -0
  25. package/dist/server/composition.js +32 -0
  26. package/dist/server/composition.js.map +1 -0
  27. package/dist/server/index.d.ts +13 -0
  28. package/dist/server/index.d.ts.map +1 -0
  29. package/dist/server/index.js +12 -0
  30. package/dist/server/index.js.map +1 -0
  31. package/dist/support/entity-index.d.ts +91 -0
  32. package/dist/support/entity-index.d.ts.map +1 -0
  33. package/dist/support/entity-index.js +56 -0
  34. package/dist/support/entity-index.js.map +1 -0
  35. package/dist/support/index.d.ts +134 -0
  36. package/dist/support/index.d.ts.map +1 -0
  37. package/dist/support/index.js +153 -0
  38. package/dist/support/index.js.map +1 -0
  39. package/package.json +66 -0
@@ -0,0 +1,112 @@
1
+ /**
2
+ * `PlatformComposition` — what a caller hands the kit (feature 109, contract
3
+ * R2.1).
4
+ *
5
+ * ## The inversion, and why it is the whole feature
6
+ *
7
+ * `backend/test/helpers/test-server.ts` composes a platform by *finding* one:
8
+ * it imports the generated module list, the application's ORM configuration,
9
+ * the resolved manifest registry, the overlay loader and the package loader —
10
+ * seven bindings, every one of them a fact about **this repository's tree**
11
+ * (§2.1). A package cannot have any of them, so a harness that reaches for them
12
+ * cannot be a package, and a module's server-bound test therefore had nowhere
13
+ * to run but `backend/test`.
14
+ *
15
+ * So the kit is *handed* its composition and never builds one. That is the same
16
+ * inversion the platform already applies to every other host value: a
17
+ * composition root registers `redis`, `eventBus` and `processRunsWorkers`
18
+ * above the compose call because no module defaults them (AGENTS.md
19
+ * § Composition item 8), and the composition itself is one more of those.
20
+ *
21
+ * ## What the kit deliberately will not do (R2.2)
22
+ *
23
+ * It does not read `process.env.DEPLOYMENT`, walk `node_modules` or read a
24
+ * generated artefact. Every one of those is a fact about the caller's process —
25
+ * D-104's predicate — and a kit that answered them would answer them
26
+ * differently from the platform that composes for real. Discovery lives with
27
+ * the host, which is where `loadOverlayModuleEntries` and
28
+ * `loadPackageModuleEntries` already are; a second copy in the kit would be a
29
+ * second answer waiting to disagree.
30
+ */
31
+ import type { MikroORM } from '@mikro-orm/postgresql';
32
+ import type { ModuleManifest } from '@endora-commerce/contracts';
33
+ import type { composeModules } from '@endora-commerce/platform/composition';
34
+ import type { TestSupportContribution } from '../support/index.js';
35
+ /**
36
+ * One composed module, named through `composeModules`' own signature.
37
+ *
38
+ * `ModuleEntry` is not one of the 27 symbols `./composition` carries and this
39
+ * feature widens no barrel (R3.1), so the type is taken from the function that
40
+ * *is* published rather than by publishing a second symbol to spell it. It
41
+ * stays correct by construction: a field added to the entry is a field here in
42
+ * the same compile.
43
+ */
44
+ type ModuleEntry = Parameters<typeof composeModules>[0][number];
45
+ /**
46
+ * How this composition opens and closes its ORM.
47
+ *
48
+ * A pair rather than an instance, for the reason the harness's own
49
+ * `initOrm`/`closeOrm` are a pair: the ORM configuration captures
50
+ * `DATABASE_URL` at import, so *when* it is opened is part of what the caller
51
+ * decides. The application supplies its own; a module package's test supplies
52
+ * one built over the entity classes its installed set declares.
53
+ */
54
+ export interface TestOrmLifecycle {
55
+ open(): Promise<MikroORM>;
56
+ close(): Promise<void>;
57
+ }
58
+ /**
59
+ * One entry of the loaded manifest registry, as the kit reads it.
60
+ *
61
+ * **Structural on purpose.** The application's own entry type carries a file
62
+ * path, an origin, install hooks and the operator commands a module declares —
63
+ * none of which the composition seam needs, and all of which are host-owned
64
+ * (`backend/src/lifecycle/registered-manifests.ts`, which a package may not
65
+ * name). Declaring the one member the kit reads keeps the application's array
66
+ * assignable without the kit knowing what else is on it.
67
+ */
68
+ export interface ComposedManifestEntry {
69
+ readonly manifest: ModuleManifest;
70
+ }
71
+ /**
72
+ * The four things only the caller knows.
73
+ *
74
+ * Each member is one of §2.1's rows, and the table there is the normative
75
+ * statement of what belongs here: a fifth member would have to be something a
76
+ * *stranger's* test could not answer, and there is nothing left in that class.
77
+ */
78
+ export interface PlatformComposition {
79
+ /**
80
+ * The module entries to compose — this repository's `MODULES` plus its
81
+ * overlay and its discovered packages, or a third party's own set.
82
+ *
83
+ * A composition lacking a module that declares `activation.nonDeactivatable`
84
+ * is refused by `composeModules` before the first module registers (issue
85
+ * #258), and the kit adds no second check and swallows no refusal (R2.4): a
86
+ * stranger composing an incomplete set gets the platform's own sentence,
87
+ * naming the module, the reason its manifest gives and the remedy.
88
+ */
89
+ readonly modules: readonly ModuleEntry[];
90
+ /** An opener and a closer, per {@link TestOrmLifecycle}. */
91
+ readonly orm: TestOrmLifecycle;
92
+ /**
93
+ * The loaded manifest registry the lifecycle needs.
94
+ *
95
+ * It is what the required-module set, the activation declarations and the
96
+ * seeded enabled set are all derived from — three derivations from one input,
97
+ * on every composition, so a caller that withdraws a module changes all three
98
+ * in the same run and there is no list anywhere (D-100).
99
+ */
100
+ readonly manifests: readonly ComposedManifestEntry[];
101
+ /**
102
+ * The test-support contributions of **exactly the modules in `modules`**,
103
+ * collected by the caller (contract §4).
104
+ *
105
+ * The kit does not discover them for the same reason it does not discover
106
+ * modules. In Phase 1b only `registrations` is applied; `volatileTables` and
107
+ * `seed` are declared and not yet collected — see `../support/index.ts`.
108
+ */
109
+ readonly testSupport?: readonly TestSupportContribution[];
110
+ }
111
+ export {};
112
+ //# sourceMappingURL=composition.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"composition.d.ts","sourceRoot":"","sources":["../../src/server/composition.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAE5E,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAEnE;;;;;;;;GAQG;AACH,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;AAEhE;;;;;;;;GAQG;AACH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,IAAI,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC1B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;CACnC;AAED;;;;;;GAMG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;;;;;;OASG;IACH,QAAQ,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,CAAC;IACzC,4DAA4D;IAC5D,QAAQ,CAAC,GAAG,EAAE,gBAAgB,CAAC;IAC/B;;;;;;;OAOG;IACH,QAAQ,CAAC,SAAS,EAAE,SAAS,qBAAqB,EAAE,CAAC;IACrD;;;;;;;OAOG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,uBAAuB,EAAE,CAAC;CAC3D"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * `PlatformComposition` — what a caller hands the kit (feature 109, contract
3
+ * R2.1).
4
+ *
5
+ * ## The inversion, and why it is the whole feature
6
+ *
7
+ * `backend/test/helpers/test-server.ts` composes a platform by *finding* one:
8
+ * it imports the generated module list, the application's ORM configuration,
9
+ * the resolved manifest registry, the overlay loader and the package loader —
10
+ * seven bindings, every one of them a fact about **this repository's tree**
11
+ * (§2.1). A package cannot have any of them, so a harness that reaches for them
12
+ * cannot be a package, and a module's server-bound test therefore had nowhere
13
+ * to run but `backend/test`.
14
+ *
15
+ * So the kit is *handed* its composition and never builds one. That is the same
16
+ * inversion the platform already applies to every other host value: a
17
+ * composition root registers `redis`, `eventBus` and `processRunsWorkers`
18
+ * above the compose call because no module defaults them (AGENTS.md
19
+ * § Composition item 8), and the composition itself is one more of those.
20
+ *
21
+ * ## What the kit deliberately will not do (R2.2)
22
+ *
23
+ * It does not read `process.env.DEPLOYMENT`, walk `node_modules` or read a
24
+ * generated artefact. Every one of those is a fact about the caller's process —
25
+ * D-104's predicate — and a kit that answered them would answer them
26
+ * differently from the platform that composes for real. Discovery lives with
27
+ * the host, which is where `loadOverlayModuleEntries` and
28
+ * `loadPackageModuleEntries` already are; a second copy in the kit would be a
29
+ * second answer waiting to disagree.
30
+ */
31
+ export {};
32
+ //# sourceMappingURL=composition.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"composition.js","sourceRoot":"","sources":["../../src/server/composition.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * `./server` — compose a platform for a test, and take it down again
3
+ * (feature 109, R1.1).
4
+ *
5
+ * `composeTestServer` and `teardownTestServer`, and the two types they take and
6
+ * return. Everything the caller supplies is `PlatformComposition`
7
+ * (`./composition.ts`) plus the four hooks the boot order makes load-bearing;
8
+ * everything the kit owns is on `TestServerHandle`, and **not one field of it
9
+ * names a module**.
10
+ */
11
+ export { composeTestServer, teardownTestServer, type ComposedPlatformContext, type ComposeTestServerOptions, type TestPlatformContext, type TestServerHandle, } from './compose-test-server.js';
12
+ export type { ComposedManifestEntry, PlatformComposition, TestOrmLifecycle, } from './composition.js';
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,KAAK,uBAAuB,EAC5B,KAAK,wBAAwB,EAC7B,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,GACtB,MAAM,0BAA0B,CAAC;AAElC,YAAY,EACV,qBAAqB,EACrB,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * `./server` — compose a platform for a test, and take it down again
3
+ * (feature 109, R1.1).
4
+ *
5
+ * `composeTestServer` and `teardownTestServer`, and the two types they take and
6
+ * return. Everything the caller supplies is `PlatformComposition`
7
+ * (`./composition.ts`) plus the four hooks the boot order makes load-bearing;
8
+ * everything the kit owns is on `TestServerHandle`, and **not one field of it
9
+ * names a module**.
10
+ */
11
+ export { composeTestServer, teardownTestServer, } from './compose-test-server.js';
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EACL,iBAAiB,EACjB,kBAAkB,GAKnB,MAAM,0BAA0B,CAAC"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * The entity index — its **type** and its **lookup**, and no module's name
3
+ * (feature 109, T065; contract R2.2, FR-001).
4
+ *
5
+ * ## The one fact this file is built around not knowing
6
+ *
7
+ * A server-bound test writes rows, and to write a row it needs the entity
8
+ * **class the ORM registered** — not a structurally identical copy read out of a
9
+ * package's source, which is a class the ORM never discovered (D-160.6.1). A
10
+ * module package publishes one `entities` array and **no entity class by name**
11
+ * (D-168), so the class is picked out of that array by name, and the array comes
12
+ * off the package's own `./backend`.
13
+ *
14
+ * Which packages are there is where this stops being a package's business.
15
+ * *Which modules one deployment installed* is the single fact a kit that may
16
+ * name no module is forbidden to know (R2.2, FR-001), and it is why
17
+ * `backend/test/helpers/package-entities.ts` is permanently host-owned and may
18
+ * not move here — `specs/084-small-f4-package-layout/contracts/module-package-layout.md`
19
+ * R10's closing paragraph, which asks in as many words that this not be
20
+ * re-opened on import volume.
21
+ *
22
+ * So the population arrives as an **argument**, exactly as
23
+ * `PlatformComposition.modules` does, and it is rendered by the host's own
24
+ * generator — `@endora-commerce/cli/lib/entity-index-artefact.js`, run by
25
+ * `endora generate` in an instance and by `composer:generate` in this
26
+ * repository. One derivation, two populations
27
+ * (`contracts/instance-repository.md` R3.5).
28
+ *
29
+ * ## Why the lookup delegates rather than re-implements
30
+ *
31
+ * `entityNamed` is `@endora-commerce/platform/packages`', and two
32
+ * implementations of one lookup are two answers waiting to disagree about what a
33
+ * missing name does. What this file adds is the **module** dimension the
34
+ * platform's lookup has no opinion about: the index is keyed by module id, so a
35
+ * name that is absent has two different causes — the module is not installed, or
36
+ * the module publishes no such class — and they have different remedies. A host
37
+ * that gets `Cannot read properties of undefined` for the first has been told
38
+ * nothing.
39
+ */
40
+ import type { EntityClass } from '@mikro-orm/core';
41
+ import { type EntityRowTypeIsRequired } from '@endora-commerce/platform/packages';
42
+ /**
43
+ * What a published `entities` array holds.
44
+ *
45
+ * A construct signature rather than `unknown`, for the platform lookup's own
46
+ * reason: it refuses an argument that is not an array of classes at all, which
47
+ * is the shape a package that lost its array answers with — the host reads a
48
+ * missing `entities` export as `[]` and reports nothing.
49
+ */
50
+ type PublishedEntityClass = abstract new (...args: never[]) => object;
51
+ /**
52
+ * The index's type, and the whole of what the kit may say about its content.
53
+ *
54
+ * Keyed by **module id** — `endora.id`, the identity of record (D-142) — and
55
+ * never by package name: a test names the module it is testing, and the package
56
+ * name is a fact about how that module was delivered.
57
+ *
58
+ * The kit declares this shape and never a member of it. A union of the module
59
+ * ids that happen to exist today would be the list R2.2 forbids, and it would
60
+ * make a stranger's own module unnameable in the index their own host rendered.
61
+ */
62
+ export type InstalledEntityIndex = Readonly<Record<string, readonly PublishedEntityClass[]>>;
63
+ /**
64
+ * One entity class out of an installed module's published array, with the row
65
+ * type the caller supplies.
66
+ *
67
+ * The row type is **required** and its default is unsatisfiable, which is the
68
+ * platform lookup's decision and is inherited here rather than restated: with
69
+ * `T` free, `em.create` checks the payload against `object` and accepts
70
+ * everything, which is worse than the constructor-union collapse the mechanism
71
+ * exists for. A caller takes it from an `import type` of the module's own
72
+ * `./test-support`, which publishes the row types `export type`-only for exactly
73
+ * this (R10 property 3).
74
+ */
75
+ export declare function entityNamedIn<T extends object = EntityRowTypeIsRequired>(index: InstalledEntityIndex, moduleId: string, name: string): EntityClass<T>;
76
+ /**
77
+ * A module id the index does not carry.
78
+ *
79
+ * Its own class rather than a bare `Error` because the two causes of "no such
80
+ * entity" have different remedies and a caller may legitimately branch on which
81
+ * it got: this one means *this host composed a different set of modules*, and
82
+ * the platform lookup's means *that module publishes no such class*.
83
+ */
84
+ export declare class ModuleNotInstalledError extends Error {
85
+ readonly moduleId: string;
86
+ readonly installed: readonly string[];
87
+ readonly name = "ModuleNotInstalledError";
88
+ constructor(moduleId: string, installed: readonly string[]);
89
+ }
90
+ export {};
91
+ //# sourceMappingURL=entity-index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entity-index.d.ts","sourceRoot":"","sources":["../../src/support/entity-index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEnD,OAAO,EAAe,KAAK,uBAAuB,EAAE,MAAM,oCAAoC,CAAC;AAE/F;;;;;;;GAOG;AACH,KAAK,oBAAoB,GAAG,QAAQ,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,MAAM,CAAC;AAEtE;;;;;;;;;;GAUG;AACH,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,oBAAoB,EAAE,CAAC,CAAC,CAAC;AAE7F;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM,GAAG,uBAAuB,EACtE,KAAK,EAAE,oBAAoB,EAC3B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GACX,WAAW,CAAC,CAAC,CAAC,CAiBhB;AASD;;;;;;;GAOG;AACH,qBAAa,uBAAwB,SAAQ,KAAK;IAI9C,QAAQ,CAAC,QAAQ,EAAE,MAAM;IACzB,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE;IAJvC,SAAkB,IAAI,6BAA6B;gBAGxC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,SAAS,MAAM,EAAE;CAUxC"}
@@ -0,0 +1,56 @@
1
+ import { entityNamed } from '@endora-commerce/platform/packages';
2
+ /**
3
+ * One entity class out of an installed module's published array, with the row
4
+ * type the caller supplies.
5
+ *
6
+ * The row type is **required** and its default is unsatisfiable, which is the
7
+ * platform lookup's decision and is inherited here rather than restated: with
8
+ * `T` free, `em.create` checks the payload against `object` and accepts
9
+ * everything, which is worse than the constructor-union collapse the mechanism
10
+ * exists for. A caller takes it from an `import type` of the module's own
11
+ * `./test-support`, which publishes the row types `export type`-only for exactly
12
+ * this (R10 property 3).
13
+ */
14
+ export function entityNamedIn(index, moduleId, name) {
15
+ // `Object.hasOwn`, not a truthiness test: the index is a plain object a
16
+ // generator rendered, so `index['toString']` answers with a function off
17
+ // `Object.prototype` — a truthy value where an array belongs, and on
18
+ // `constructor` a value that would reach the lookup and fail somewhere else.
19
+ if (!Object.hasOwn(index, moduleId)) {
20
+ throw new ModuleNotInstalledError(moduleId, installedModuleIds(index));
21
+ }
22
+ const published = index[moduleId];
23
+ if (!Array.isArray(published)) {
24
+ throw new ModuleNotInstalledError(moduleId, installedModuleIds(index));
25
+ }
26
+ return entityNamed(published, name, `the installed module '${moduleId}'`);
27
+ }
28
+ /** Every module id the index carries, sorted, so a refusal reads the same way twice. */
29
+ function installedModuleIds(index) {
30
+ return Object.keys(index)
31
+ .filter((key) => Object.hasOwn(index, key))
32
+ .sort();
33
+ }
34
+ /**
35
+ * A module id the index does not carry.
36
+ *
37
+ * Its own class rather than a bare `Error` because the two causes of "no such
38
+ * entity" have different remedies and a caller may legitimately branch on which
39
+ * it got: this one means *this host composed a different set of modules*, and
40
+ * the platform lookup's means *that module publishes no such class*.
41
+ */
42
+ export class ModuleNotInstalledError extends Error {
43
+ moduleId;
44
+ installed;
45
+ name = 'ModuleNotInstalledError';
46
+ constructor(moduleId, installed) {
47
+ super(`the entity index carries no module '${moduleId}', so no entity class of it can be ` +
48
+ `named. This host installed: ${installed.length === 0 ? '(no module at all)' : installed.join(', ')}. ` +
49
+ `The index is rendered from the packages this host installed, so either install ` +
50
+ `'${moduleId}' and re-run the generator, or name a module that is in the list — a ` +
51
+ `stale index is a test asking the ORM about a table nothing mapped.`);
52
+ this.moduleId = moduleId;
53
+ this.installed = installed;
54
+ }
55
+ }
56
+ //# sourceMappingURL=entity-index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entity-index.js","sourceRoot":"","sources":["../../src/support/entity-index.ts"],"names":[],"mappings":"AAyCA,OAAO,EAAE,WAAW,EAAgC,MAAM,oCAAoC,CAAC;AAyB/F;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,aAAa,CAC3B,KAA2B,EAC3B,QAAgB,EAChB,IAAY;IAEZ,wEAAwE;IACxE,yEAAyE;IACzE,qEAAqE;IACrE,6EAA6E;IAC7E,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,EAAE,CAAC;QACpC,MAAM,IAAI,uBAAuB,CAAC,QAAQ,EAAE,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC;IACzE,CAAC;IACD,MAAM,SAAS,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC;IAClC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,uBAAuB,CAAC,QAAQ,EAAE,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC;IACzE,CAAC;IACD,OAAO,WAAW,CAChB,SAA4C,EAC5C,IAAI,EACJ,yBAAyB,QAAQ,GAAG,CACrC,CAAC;AACJ,CAAC;AAED,wFAAwF;AACxF,SAAS,kBAAkB,CAAC,KAA2B;IACrD,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC;SACtB,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;SAC1C,IAAI,EAAE,CAAC;AACZ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,OAAO,uBAAwB,SAAQ,KAAK;IAIrC;IACA;IAJO,IAAI,GAAG,yBAAyB,CAAC;IAEnD,YACW,QAAgB,EAChB,SAA4B;QAErC,KAAK,CACH,uCAAuC,QAAQ,qCAAqC;YAClF,+BAA+B,SAAS,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI;YACvG,iFAAiF;YACjF,IAAI,QAAQ,uEAAuE;YACnF,oEAAoE,CACvE,CAAC;QATO,aAAQ,GAAR,QAAQ,CAAQ;QAChB,cAAS,GAAT,SAAS,CAAmB;IASvC,CAAC;CACF"}
@@ -0,0 +1,134 @@
1
+ /**
2
+ * `./support` — what a module contributes to a test composition (feature 109,
3
+ * contract §4).
4
+ *
5
+ * A module package that needs test support declares it at
6
+ * `src/test-support/index.ts` and publishes it as `./test-support`; the caller
7
+ * collects the declarations of **the modules it composed** and hands them to
8
+ * `composeTestServer` as `PlatformComposition.testSupport`. The kit never
9
+ * discovers them: discovery walks `node_modules`, which is a fact about the
10
+ * caller's process (R2.2).
11
+ *
12
+ * ## What is here in Phase 1b, and what is not
13
+ *
14
+ * The **declaration shape** and the **registration** member, because
15
+ * `PlatformComposition` takes contributions and a type it could not name would
16
+ * make that member `unknown`. The three collectors — the truncate ordering
17
+ * derived from the foreign-key graph (R4.2), the per-module seed decomposition
18
+ * (R4.6) and the fixture re-export — are Phase 3, and the members they read are
19
+ * declared here now so that a module author writing a `./test-support` layer
20
+ * writes one declaration rather than two.
21
+ *
22
+ * `volatileTables` and `seed` are therefore **declared and not yet collected**,
23
+ * which is stated rather than left to be discovered: `composeTestServer`
24
+ * applies `registrations` and reads neither, and a module that declares tables
25
+ * today gets no truncate from them. The alternative — leaving them out of the
26
+ * type until the collector exists — would make every module that ships a
27
+ * contribution in Phase 3 edit a declaration it had already written.
28
+ *
29
+ * ## The entity index is here too, and it is the same inversion
30
+ *
31
+ * `./entity-index.js` carries the **type** of a host's entity index and the
32
+ * **lookup** over it, and no module's name (T065). It is on this subpath rather
33
+ * than a new one because it is the same kind of thing as everything above: a
34
+ * shape the kit declares and a population the caller supplies. Its own header
35
+ * is where the reasoning is.
36
+ */
37
+ export { entityNamedIn, ModuleNotInstalledError, type InstalledEntityIndex, } from './entity-index.js';
38
+ /**
39
+ * One module's test support.
40
+ *
41
+ * Four optional members and nothing else (R4.1). Every one of them is that
42
+ * module's own policy: the taxonomy fetcher that refuses and the PIM client
43
+ * that throws exist to make a test which reaches the network fail loudly, and
44
+ * that is `product_feeds`' and `pim_ergonode`' decision rather than a harness's.
45
+ */
46
+ export interface TestSupportContribution {
47
+ /**
48
+ * The module this contribution belongs to. It is the module's own manifest
49
+ * id, and the kit uses it for one thing: naming the module in a refusal.
50
+ */
51
+ readonly moduleId: string;
52
+ /**
53
+ * Container registrations this module's tests substitute by default, keyed by
54
+ * the **registration name its owner registers** — the same key
55
+ * `composedModules.contribute` takes.
56
+ *
57
+ * A module may not contribute over a name it does not own (R4.4), for the
58
+ * reason D-176 and issue #203 give about decoration: a substitution rewrites
59
+ * what every consumer of that name resolves. A test that needs to substitute
60
+ * *another* module's collaborator does it at the call site, in its own file,
61
+ * through `composeTestServer`'s own `contribute` option — where the coupling
62
+ * is visible in the test that wanted it.
63
+ */
64
+ readonly registrations?: Readonly<Record<string, unknown>>;
65
+ /**
66
+ * The tables this module owns whose rows must not survive between tests.
67
+ *
68
+ * A **set, not a sequence** (R4.2): the order they are emptied in is derived
69
+ * from the foreign-key graph, and a set the derivation cannot order is a
70
+ * failure naming the cycle. A hand-written order is a fact about the schema
71
+ * that the schema can state for itself.
72
+ */
73
+ readonly volatileTables?: readonly string[];
74
+ /**
75
+ * Rows this module contributes to a named seed.
76
+ *
77
+ * Additive and order-independent (R4.6): a seed whose result depends on the
78
+ * order two modules contributed in is a defect, not a configuration.
79
+ */
80
+ readonly seed?: Readonly<Record<string, (em: unknown) => Promise<void>>>;
81
+ /** Anything else this module's tests build, re-exported for its own use. */
82
+ readonly fixtures?: Readonly<Record<string, unknown>>;
83
+ }
84
+ /**
85
+ * Every registration the given contributions substitute, merged.
86
+ *
87
+ * Refuses two modules claiming one name rather than letting the last one win:
88
+ * a substitution the reader cannot attribute is the shape D-176 refuses for
89
+ * decoration, and "whichever was collected last" is not an answer anybody can
90
+ * act on.
91
+ */
92
+ export declare function mergeTestSupportRegistrations(contributions: readonly TestSupportContribution[]): Readonly<Record<string, unknown>>;
93
+ /** Two modules substituting one registration name. */
94
+ export declare class ConflictingTestSupportError extends Error {
95
+ readonly registrationName: string;
96
+ readonly firstModuleId: string;
97
+ readonly secondModuleId: string;
98
+ constructor(registrationName: string, firstModuleId: string, secondModuleId: string);
99
+ }
100
+ /**
101
+ * Every table the given contributions declare volatile, deduped and sorted.
102
+ *
103
+ * **Sorted rather than ordered** (R4.2). The declaration is a set, and the
104
+ * ordering the contract asks the foreign-key graph to derive is not needed by
105
+ * the one consumer there is: the caller empties them with a single
106
+ * `truncate table … cascade`, which is one statement over the whole set and
107
+ * takes no order — PostgreSQL resolves the dependencies itself, which is why the
108
+ * harness's hand-written children-first list has always been a comment rather
109
+ * than a mechanism. A sorted set makes the result a function of the composition
110
+ * and not of the order the contributions were collected in, which is the
111
+ * property R4.2 is actually protecting. The day a consumer empties them one
112
+ * statement at a time, the derivation the contract describes is what it has to
113
+ * take, and this function is where it goes.
114
+ *
115
+ * Two refusals, both about a declaration rather than about a statement: two
116
+ * modules claiming one table, and a name that is not an identifier. The second
117
+ * matters because the collected set is interpolated into SQL by its caller, so a
118
+ * quote or a semicolon has to be refused where it is written down.
119
+ */
120
+ export declare function collectVolatileTables(contributions: readonly TestSupportContribution[]): readonly string[];
121
+ /** Two modules declaring one table volatile. */
122
+ export declare class ConflictingVolatileTableError extends Error {
123
+ readonly tableName: string;
124
+ readonly firstModuleId: string;
125
+ readonly secondModuleId: string;
126
+ constructor(tableName: string, firstModuleId: string, secondModuleId: string);
127
+ }
128
+ /** A declared volatile table whose name is not an identifier. */
129
+ export declare class UndeclarableVolatileTableError extends Error {
130
+ readonly moduleId: string;
131
+ readonly tableName: string;
132
+ constructor(moduleId: string, tableName: string);
133
+ }
134
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/support/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,OAAO,EACL,aAAa,EACb,uBAAuB,EACvB,KAAK,oBAAoB,GAC1B,MAAM,mBAAmB,CAAC;AAE3B;;;;;;;GAOG;AACH,MAAM,WAAW,uBAAuB;IACtC;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC3D;;;;;;;OAOG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5C;;;;;OAKG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACzE,4EAA4E;IAC5E,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACvD;AAED;;;;;;;GAOG;AACH,wBAAgB,6BAA6B,CAC3C,aAAa,EAAE,SAAS,uBAAuB,EAAE,GAChD,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAcnC;AAED,sDAAsD;AACtD,qBAAa,2BAA4B,SAAQ,KAAK;IAElD,QAAQ,CAAC,gBAAgB,EAAE,MAAM;IACjC,QAAQ,CAAC,aAAa,EAAE,MAAM;IAC9B,QAAQ,CAAC,cAAc,EAAE,MAAM;gBAFtB,gBAAgB,EAAE,MAAM,EACxB,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,MAAM;CAWlC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,qBAAqB,CACnC,aAAa,EAAE,SAAS,uBAAuB,EAAE,GAChD,SAAS,MAAM,EAAE,CAiBnB;AAQD,gDAAgD;AAChD,qBAAa,6BAA8B,SAAQ,KAAK;IAEpD,QAAQ,CAAC,SAAS,EAAE,MAAM;IAC1B,QAAQ,CAAC,aAAa,EAAE,MAAM;IAC9B,QAAQ,CAAC,cAAc,EAAE,MAAM;gBAFtB,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,MAAM;CAWlC;AAED,iEAAiE;AACjE,qBAAa,8BAA+B,SAAQ,KAAK;IAErD,QAAQ,CAAC,QAAQ,EAAE,MAAM;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM;gBADjB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM;CAW7B"}
@@ -0,0 +1,153 @@
1
+ /**
2
+ * `./support` — what a module contributes to a test composition (feature 109,
3
+ * contract §4).
4
+ *
5
+ * A module package that needs test support declares it at
6
+ * `src/test-support/index.ts` and publishes it as `./test-support`; the caller
7
+ * collects the declarations of **the modules it composed** and hands them to
8
+ * `composeTestServer` as `PlatformComposition.testSupport`. The kit never
9
+ * discovers them: discovery walks `node_modules`, which is a fact about the
10
+ * caller's process (R2.2).
11
+ *
12
+ * ## What is here in Phase 1b, and what is not
13
+ *
14
+ * The **declaration shape** and the **registration** member, because
15
+ * `PlatformComposition` takes contributions and a type it could not name would
16
+ * make that member `unknown`. The three collectors — the truncate ordering
17
+ * derived from the foreign-key graph (R4.2), the per-module seed decomposition
18
+ * (R4.6) and the fixture re-export — are Phase 3, and the members they read are
19
+ * declared here now so that a module author writing a `./test-support` layer
20
+ * writes one declaration rather than two.
21
+ *
22
+ * `volatileTables` and `seed` are therefore **declared and not yet collected**,
23
+ * which is stated rather than left to be discovered: `composeTestServer`
24
+ * applies `registrations` and reads neither, and a module that declares tables
25
+ * today gets no truncate from them. The alternative — leaving them out of the
26
+ * type until the collector exists — would make every module that ships a
27
+ * contribution in Phase 3 edit a declaration it had already written.
28
+ *
29
+ * ## The entity index is here too, and it is the same inversion
30
+ *
31
+ * `./entity-index.js` carries the **type** of a host's entity index and the
32
+ * **lookup** over it, and no module's name (T065). It is on this subpath rather
33
+ * than a new one because it is the same kind of thing as everything above: a
34
+ * shape the kit declares and a population the caller supplies. Its own header
35
+ * is where the reasoning is.
36
+ */
37
+ export { entityNamedIn, ModuleNotInstalledError, } from './entity-index.js';
38
+ /**
39
+ * Every registration the given contributions substitute, merged.
40
+ *
41
+ * Refuses two modules claiming one name rather than letting the last one win:
42
+ * a substitution the reader cannot attribute is the shape D-176 refuses for
43
+ * decoration, and "whichever was collected last" is not an answer anybody can
44
+ * act on.
45
+ */
46
+ export function mergeTestSupportRegistrations(contributions) {
47
+ const merged = {};
48
+ const claimedBy = new Map();
49
+ for (const contribution of contributions) {
50
+ for (const [name, value] of Object.entries(contribution.registrations ?? {})) {
51
+ const owner = claimedBy.get(name);
52
+ if (owner !== undefined) {
53
+ throw new ConflictingTestSupportError(name, owner, contribution.moduleId);
54
+ }
55
+ claimedBy.set(name, contribution.moduleId);
56
+ merged[name] = value;
57
+ }
58
+ }
59
+ return merged;
60
+ }
61
+ /** Two modules substituting one registration name. */
62
+ export class ConflictingTestSupportError extends Error {
63
+ registrationName;
64
+ firstModuleId;
65
+ secondModuleId;
66
+ constructor(registrationName, firstModuleId, secondModuleId) {
67
+ super(`test support for '${registrationName}' is contributed by both '${firstModuleId}' and ` +
68
+ `'${secondModuleId}'. A registration has one owner, and a substitution rewrites what ` +
69
+ `every consumer of that name resolves — so only the module that registers it may ` +
70
+ `substitute it by default. A test that needs the other module's substitution asks for ` +
71
+ `it at its own call site.`);
72
+ this.registrationName = registrationName;
73
+ this.firstModuleId = firstModuleId;
74
+ this.secondModuleId = secondModuleId;
75
+ this.name = 'ConflictingTestSupportError';
76
+ }
77
+ }
78
+ /**
79
+ * Every table the given contributions declare volatile, deduped and sorted.
80
+ *
81
+ * **Sorted rather than ordered** (R4.2). The declaration is a set, and the
82
+ * ordering the contract asks the foreign-key graph to derive is not needed by
83
+ * the one consumer there is: the caller empties them with a single
84
+ * `truncate table … cascade`, which is one statement over the whole set and
85
+ * takes no order — PostgreSQL resolves the dependencies itself, which is why the
86
+ * harness's hand-written children-first list has always been a comment rather
87
+ * than a mechanism. A sorted set makes the result a function of the composition
88
+ * and not of the order the contributions were collected in, which is the
89
+ * property R4.2 is actually protecting. The day a consumer empties them one
90
+ * statement at a time, the derivation the contract describes is what it has to
91
+ * take, and this function is where it goes.
92
+ *
93
+ * Two refusals, both about a declaration rather than about a statement: two
94
+ * modules claiming one table, and a name that is not an identifier. The second
95
+ * matters because the collected set is interpolated into SQL by its caller, so a
96
+ * quote or a semicolon has to be refused where it is written down.
97
+ */
98
+ export function collectVolatileTables(contributions) {
99
+ const claimedBy = new Map();
100
+ for (const contribution of contributions) {
101
+ for (const table of contribution.volatileTables ?? []) {
102
+ if (!VALID_TABLE_NAME.test(table)) {
103
+ throw new UndeclarableVolatileTableError(contribution.moduleId, table);
104
+ }
105
+ const owner = claimedBy.get(table);
106
+ // One module naming its own table twice has made no claim it had not
107
+ // already made; only a *second* module is a conflict.
108
+ if (owner !== undefined && owner !== contribution.moduleId) {
109
+ throw new ConflictingVolatileTableError(table, owner, contribution.moduleId);
110
+ }
111
+ claimedBy.set(table, contribution.moduleId);
112
+ }
113
+ }
114
+ return [...claimedBy.keys()].sort();
115
+ }
116
+ /**
117
+ * What a table name may be: the unquoted PostgreSQL identifier every migration
118
+ * in this repository writes. Anything else is refused rather than quoted around.
119
+ */
120
+ const VALID_TABLE_NAME = /^[a-z_][a-z0-9_]*$/;
121
+ /** Two modules declaring one table volatile. */
122
+ export class ConflictingVolatileTableError extends Error {
123
+ tableName;
124
+ firstModuleId;
125
+ secondModuleId;
126
+ constructor(tableName, firstModuleId, secondModuleId) {
127
+ super(`the table '${tableName}' is declared volatile by both '${firstModuleId}' and ` +
128
+ `'${secondModuleId}'. A table has one owner (R4.3), and emptying another module's ` +
129
+ `table between tests deletes rows that module's own tests rely on — so only the ` +
130
+ `module that owns it may declare it. A test that needs a neighbour's table emptied ` +
131
+ `does it in its own file, where the coupling is visible.`);
132
+ this.tableName = tableName;
133
+ this.firstModuleId = firstModuleId;
134
+ this.secondModuleId = secondModuleId;
135
+ this.name = 'ConflictingVolatileTableError';
136
+ }
137
+ }
138
+ /** A declared volatile table whose name is not an identifier. */
139
+ export class UndeclarableVolatileTableError extends Error {
140
+ moduleId;
141
+ tableName;
142
+ constructor(moduleId, tableName) {
143
+ super(`'${moduleId}' declares the volatile table '${tableName}', which is not an unquoted ` +
144
+ `PostgreSQL identifier${tableName.trim() === '' ? ' (it is empty)' : ''}. The collected ` +
145
+ `set is interpolated into a \`truncate table … cascade\` statement, so a name carrying ` +
146
+ `a quote, a semicolon or nothing at all is refused where it is written down rather ` +
147
+ `than where it would run.`);
148
+ this.moduleId = moduleId;
149
+ this.tableName = tableName;
150
+ this.name = 'UndeclarableVolatileTableError';
151
+ }
152
+ }
153
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/support/index.ts"],"names":[],"mappings":"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"}