@codefast/di-testing 0.0.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 (68) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +162 -0
  3. package/dist/discovery/dependency-scanner.d.ts +29 -0
  4. package/dist/discovery/dependency-scanner.d.ts.map +1 -0
  5. package/dist/discovery/dependency-scanner.js +51 -0
  6. package/dist/discovery/dependency-scanner.js.map +1 -0
  7. package/dist/discovery/mock-binder.d.ts +71 -0
  8. package/dist/discovery/mock-binder.d.ts.map +1 -0
  9. package/dist/discovery/mock-binder.js +206 -0
  10. package/dist/discovery/mock-binder.js.map +1 -0
  11. package/dist/errors/errors.d.ts +62 -0
  12. package/dist/errors/errors.d.ts.map +1 -0
  13. package/dist/errors/errors.js +83 -0
  14. package/dist/errors/errors.js.map +1 -0
  15. package/dist/index.d.ts +17 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +7 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/mocking/auto-mock.d.ts +47 -0
  20. package/dist/mocking/auto-mock.d.ts.map +1 -0
  21. package/dist/mocking/auto-mock.js +180 -0
  22. package/dist/mocking/auto-mock.js.map +1 -0
  23. package/dist/mocking/mock-factory.d.ts +25 -0
  24. package/dist/mocking/mock-factory.d.ts.map +1 -0
  25. package/dist/mocking/mock-factory.js +7 -0
  26. package/dist/mocking/mock-factory.js.map +1 -0
  27. package/dist/mocking/spy.d.ts +39 -0
  28. package/dist/mocking/spy.d.ts.map +1 -0
  29. package/dist/mocking/spy.js +43 -0
  30. package/dist/mocking/spy.js.map +1 -0
  31. package/dist/test-bed/bed-builder.d.ts +83 -0
  32. package/dist/test-bed/bed-builder.d.ts.map +1 -0
  33. package/dist/test-bed/bed-builder.js +95 -0
  34. package/dist/test-bed/bed-builder.js.map +1 -0
  35. package/dist/test-bed/sociable-builder.d.ts +41 -0
  36. package/dist/test-bed/sociable-builder.d.ts.map +1 -0
  37. package/dist/test-bed/sociable-builder.js +78 -0
  38. package/dist/test-bed/sociable-builder.js.map +1 -0
  39. package/dist/test-bed/solitary-builder.d.ts +34 -0
  40. package/dist/test-bed/solitary-builder.d.ts.map +1 -0
  41. package/dist/test-bed/solitary-builder.js +29 -0
  42. package/dist/test-bed/solitary-builder.js.map +1 -0
  43. package/dist/test-bed/test-bed.d.ts +35 -0
  44. package/dist/test-bed/test-bed.d.ts.map +1 -0
  45. package/dist/test-bed/test-bed.js +18 -0
  46. package/dist/test-bed/test-bed.js.map +1 -0
  47. package/dist/test-bed/unit-test-bed.d.ts +65 -0
  48. package/dist/test-bed/unit-test-bed.d.ts.map +1 -0
  49. package/dist/test-bed/unit-test-bed.js +70 -0
  50. package/dist/test-bed/unit-test-bed.js.map +1 -0
  51. package/dist/types.d.ts +12 -0
  52. package/dist/types.d.ts.map +1 -0
  53. package/dist/types.js +3 -0
  54. package/dist/types.js.map +1 -0
  55. package/package.json +156 -0
  56. package/src/discovery/dependency-scanner.ts +74 -0
  57. package/src/discovery/mock-binder.ts +299 -0
  58. package/src/errors/errors.ts +103 -0
  59. package/src/index.ts +26 -0
  60. package/src/mocking/auto-mock.ts +235 -0
  61. package/src/mocking/mock-factory.ts +29 -0
  62. package/src/mocking/spy.ts +84 -0
  63. package/src/test-bed/bed-builder.ts +164 -0
  64. package/src/test-bed/sociable-builder.ts +144 -0
  65. package/src/test-bed/solitary-builder.ts +66 -0
  66. package/src/test-bed/test-bed.ts +60 -0
  67. package/src/test-bed/unit-test-bed.ts +151 -0
  68. package/src/types.ts +13 -0
@@ -0,0 +1,83 @@
1
+ /** The error taxonomy `@codefast/di-testing` raises, each carrying a machine-readable `code`. */
2
+ /**
3
+ * Base class for every error the testing library throws, each carrying a machine-readable `code`.
4
+ *
5
+ * @remarks Mirrors `@codefast/di`'s own `DiError` shape rather than extending it, so a caller can
6
+ * catch testing-setup failures separately from resolution failures.
7
+ */
8
+ export class TestingError extends Error {
9
+ constructor(message) {
10
+ super(message);
11
+ this.name = this.constructor.name;
12
+ }
13
+ }
14
+ /**
15
+ * A scanned class takes constructor parameters but carries no `@injectable` metadata.
16
+ */
17
+ export class NotInjectableError extends TestingError {
18
+ code = "NOT_INJECTABLE";
19
+ targetName;
20
+ constructor(targetName) {
21
+ super(`Cannot mock the dependencies of '${targetName}': its constructor takes parameters but the class is not decorated with @injectable(...), so those dependencies cannot be discovered. Decorate the class, or bind its collaborators manually with a Container.`);
22
+ this.targetName = targetName;
23
+ }
24
+ }
25
+ /**
26
+ * A `.mock(...)` override or a `mocks.get(...)` lookup named a token or slot the unit does not use.
27
+ *
28
+ * @remarks Almost always a typo or a stale token reference; failing loudly beats silently binding an
29
+ * unused constant or returning `undefined`.
30
+ */
31
+ export class UndeclaredDependencyError extends TestingError {
32
+ code = "UNDECLARED_DEPENDENCY";
33
+ tokenName;
34
+ constructor(tokenName, detail) {
35
+ super(`'${tokenName}'${detail === undefined ? "" : ` (${detail})`} is not a dependency of the class under test. Only tokens the class declares in its constructor or accessor injections can be mocked or retrieved.`);
36
+ this.tokenName = tokenName;
37
+ }
38
+ }
39
+ const SEALED_MESSAGES = {
40
+ value: "it was supplied with .using(), so it has no mock surface. Use the reference the test passed in.",
41
+ absent: "it was declared absent with .absent(), so there is nothing to retrieve.",
42
+ all: "its elements were supplied with .usingAll(). Use the references the test passed in.",
43
+ exposed: "it is exposed as a real collaborator. Retrieve it with bed.exposed(Class) instead.",
44
+ };
45
+ /**
46
+ * A `mocks.get(...)` lookup asked for a dependency that is not a retrievable mock.
47
+ *
48
+ * @remarks `.using()`, `.absent()`, and `.usingAll()` seal the value, and an exposed class is real —
49
+ * none of them carries a mock surface, so handing them back typed as `Mocked` would lie.
50
+ */
51
+ export class SealedDependencyError extends TestingError {
52
+ code = "SEALED_DEPENDENCY";
53
+ tokenName;
54
+ constructor(tokenName, cause) {
55
+ super(`'${tokenName}' is not a retrievable mock: ${SEALED_MESSAGES[cause]}`);
56
+ this.tokenName = tokenName;
57
+ }
58
+ }
59
+ /**
60
+ * An override whose shape does not fit the slot it targets — `.absent()` on a required dependency,
61
+ * or `.usingAll()` where no unconstrained `injectAll()` slot exists.
62
+ */
63
+ export class OverrideMismatchError extends TestingError {
64
+ code = "OVERRIDE_MISMATCH";
65
+ tokenName;
66
+ constructor(tokenName, detail) {
67
+ super(`'${tokenName}': ${detail}`);
68
+ this.tokenName = tokenName;
69
+ }
70
+ }
71
+ /**
72
+ * A sociable bed's exposure could not be honoured — the class is unreachable, already the unit, or
73
+ * was asked for without having been exposed.
74
+ */
75
+ export class ExposureError extends TestingError {
76
+ code = "EXPOSURE";
77
+ targetName;
78
+ constructor(targetName, detail) {
79
+ super(`'${targetName}': ${detail}`);
80
+ this.targetName = targetName;
81
+ }
82
+ }
83
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/errors/errors.ts"],"names":[],"mappings":"AAAA,iGAAiG;AAEjG;;;;;GAKG;AACH,MAAM,OAAgB,YAAa,SAAQ,KAAK;IAG9C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;IACpC,CAAC;CACF;AAED;;GAEG;AACH,MAAM,OAAO,kBAAmB,SAAQ,YAAY;IACzC,IAAI,GAAG,gBAAgB,CAAC;IACxB,UAAU,CAAS;IAE5B,YAAY,UAAkB;QAC5B,KAAK,CACH,oCAAoC,UAAU,gNAAgN,CAC/P,CAAC;QACF,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC/B,CAAC;CACF;AAED;;;;;GAKG;AACH,MAAM,OAAO,yBAA0B,SAAQ,YAAY;IAChD,IAAI,GAAG,uBAAuB,CAAC;IAC/B,SAAS,CAAS;IAE3B,YAAY,SAAiB,EAAE,MAAe;QAC5C,KAAK,CACH,IAAI,SAAS,IAAI,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,MAAM,GAAG,oJAAoJ,CAChN,CAAC;QACF,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC7B,CAAC;CACF;AAKD,MAAM,eAAe,GAA0C;IAC7D,KAAK,EAAE,iGAAiG;IACxG,MAAM,EAAE,yEAAyE;IACjF,GAAG,EAAE,qFAAqF;IAC1F,OAAO,EAAE,oFAAoF;CAC9F,CAAC;AAEF;;;;;GAKG;AACH,MAAM,OAAO,qBAAsB,SAAQ,YAAY;IAC5C,IAAI,GAAG,mBAAmB,CAAC;IAC3B,SAAS,CAAS;IAE3B,YAAY,SAAiB,EAAE,KAAkB;QAC/C,KAAK,CAAC,IAAI,SAAS,gCAAgC,eAAe,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC7E,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC7B,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,OAAO,qBAAsB,SAAQ,YAAY;IAC5C,IAAI,GAAG,mBAAmB,CAAC;IAC3B,SAAS,CAAS;IAE3B,YAAY,SAAiB,EAAE,MAAc;QAC3C,KAAK,CAAC,IAAI,SAAS,MAAM,MAAM,EAAE,CAAC,CAAC;QACnC,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC7B,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,OAAO,aAAc,SAAQ,YAAY;IACpC,IAAI,GAAG,UAAU,CAAC;IAClB,UAAU,CAAS;IAE5B,YAAY,UAAkB,EAAE,MAAc;QAC5C,KAAK,CAAC,IAAI,UAAU,MAAM,MAAM,EAAE,CAAC,CAAC;QACpC,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC/B,CAAC;CACF"}
@@ -0,0 +1,17 @@
1
+ /** Solitary and sociable auto-mocking test beds for `@codefast/di`. */
2
+ export { TestBed } from "#/test-bed/test-bed";
3
+ export type { TestBedStatic } from "#/test-bed/test-bed";
4
+ export type { MockOverrideBuilder, PreparedBed, TestBedOptions } from "#/test-bed/bed-builder";
5
+ export type { SolitaryTestBedBuilder } from "#/test-bed/solitary-builder";
6
+ export type { SociableTestBedBuilder } from "#/test-bed/sociable-builder";
7
+ export type { SociableUnitTestBed, UnitReference, UnitTestBed } from "#/test-bed/unit-test-bed";
8
+ export { createAutoMock, MOCK_RESET } from "#/mocking/auto-mock";
9
+ export type { DeepPartial, Mocked } from "#/mocking/auto-mock";
10
+ export { defaultMockFactory } from "#/mocking/mock-factory";
11
+ export type { MockFactory, MockFunction } from "#/mocking/mock-factory";
12
+ export { createSpy } from "#/mocking/spy";
13
+ export type { Spy, SpyResult, SpyState } from "#/mocking/spy";
14
+ export type { InjectionIdentifier } from "#/types";
15
+ export { ExposureError, NotInjectableError, OverrideMismatchError, SealedDependencyError, TestingError, UndeclaredDependencyError, } from "#/errors/errors";
16
+ export type { SealedCause } from "#/errors/errors";
17
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,uEAAuE;AAEvE,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC9C,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,YAAY,EAAE,mBAAmB,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC/F,YAAY,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAC1E,YAAY,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAC1E,YAAY,EAAE,mBAAmB,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAEhG,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjE,YAAY,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACxE,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,YAAY,EAAE,GAAG,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9D,YAAY,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EACL,aAAa,EACb,kBAAkB,EAClB,qBAAqB,EACrB,qBAAqB,EACrB,YAAY,EACZ,yBAAyB,GAC1B,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ /** Solitary and sociable auto-mocking test beds for `@codefast/di`. */
2
+ export { TestBed } from "#/test-bed/test-bed";
3
+ export { createAutoMock, MOCK_RESET } from "#/mocking/auto-mock";
4
+ export { defaultMockFactory } from "#/mocking/mock-factory";
5
+ export { createSpy } from "#/mocking/spy";
6
+ export { ExposureError, NotInjectableError, OverrideMismatchError, SealedDependencyError, TestingError, UndeclaredDependencyError, } from "#/errors/errors";
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,uEAAuE;AAEvE,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAO9C,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAE5D,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAI1C,OAAO,EACL,aAAa,EACb,kBAAkB,EAClB,qBAAqB,EACrB,qBAAqB,EACrB,YAAY,EACZ,yBAAyB,GAC1B,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,47 @@
1
+ /** The lazy `Proxy` that mocks an erased interface one accessed property at a time. */
2
+ import type { MockFactory, MockFunction } from "#/mocking/mock-factory";
3
+ import type { Spy } from "#/mocking/spy";
4
+ /**
5
+ * A mocked view of `Dependency`: every member becomes a spy, nested objects are mocked in turn.
6
+ *
7
+ * @remarks With the default backend a method is a precisely-typed {@link Spy}; a custom backend
8
+ * (`vi.fn`, `jest.fn`, a Sinon stub) intersects its own native surface with the method's signature,
9
+ * so backend-specific APIs like `mockReturnValueOnce` or `returns` type-check without adapters. The
10
+ * check is mutual, so a backend that merely resembles {@link Spy} still keeps its own surface.
11
+ *
12
+ * @typeParam Dependency - The dependency type being mocked.
13
+ * @typeParam Backend - The spy type the active mock factory produces.
14
+ */
15
+ export type Mocked<Dependency, Backend extends MockFunction = Spy> = Dependency extends (...args: infer Args) => infer Return ? [Backend] extends [Spy] ? [Spy] extends [Backend] ? Spy<Args, Return> : Backend & ((...args: Args) => Return) : Backend & ((...args: Args) => Return) : Dependency extends object ? {
16
+ [Key in keyof Dependency]: Mocked<Dependency[Key], Backend>;
17
+ } : Dependency;
18
+ /**
19
+ * A recursively optional view of `Dependency` — the shape a hand-written `.stub` seed may supply.
20
+ *
21
+ * @remarks Functions are kept whole (a stub replaces a whole method), everything else is made
22
+ * optional so only the members a test cares about need spelling out.
23
+ *
24
+ * @typeParam Dependency - The dependency type being partially stubbed.
25
+ */
26
+ export type DeepPartial<Dependency> = Dependency extends (...args: Array<never>) => unknown ? Dependency : Dependency extends object ? {
27
+ [Key in keyof Dependency]?: DeepPartial<Dependency[Key]>;
28
+ } : Dependency;
29
+ /**
30
+ * The key an auto-mock answers with its reset routine, clearing the root spy, every materialized
31
+ * member, and the spies of its seed.
32
+ */
33
+ export declare const MOCK_RESET: unique symbol;
34
+ /**
35
+ * Builds a lazy auto-mock for an erased interface: each accessed member becomes a cached child mock.
36
+ *
37
+ * @remarks The proxy target is itself a spy, so a function-typed dependency records calls, and every
38
+ * materialized member is another auto-mock, so nested access (`repo.user.create`) works to any depth.
39
+ * A `seed` wins for the members it supplies (inherited ones included; a primitive seed is returned
40
+ * as the value itself); keys the root spy owns pass through, so its backend API stays real — which
41
+ * also means a member sharing a name with a function's own `name`/`length` cannot be auto-mocked.
42
+ *
43
+ * @typeParam Dependency - The dependency type being mocked.
44
+ * @typeParam Backend - The spy type the factory produces, threaded into the returned `Mocked` view.
45
+ */
46
+ export declare function createAutoMock<Dependency, Backend extends MockFunction = Spy>(mockFactory: MockFactory<Backend>, seed?: DeepPartial<Dependency>): Mocked<Dependency, Backend>;
47
+ //# sourceMappingURL=auto-mock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-mock.d.ts","sourceRoot":"","sources":["../../src/mocking/auto-mock.ts"],"names":[],"mappings":"AAAA,uFAAuF;AAEvF,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACxE,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAEzC;;;;;;;;;;GAUG;AACH,MAAM,MAAM,MAAM,CAAC,UAAU,EAAE,OAAO,SAAS,YAAY,GAAG,GAAG,IAAI,UAAU,SAAS,CACtF,GAAG,IAAI,EAAE,MAAM,IAAI,KAChB,MAAM,MAAM,GACb,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,GACrB,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAC,GACrB,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,GACjB,OAAO,GAAG,CAAC,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,MAAM,CAAC,GACvC,OAAO,GAAG,CAAC,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,MAAM,CAAC,GACvC,UAAU,SAAS,MAAM,GACvB;KAAG,GAAG,IAAI,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC;CAAE,GAC/D,UAAU,CAAC;AAEjB;;;;;;;GAOG;AACH,MAAM,MAAM,WAAW,CAAC,UAAU,IAAI,UAAU,SAAS,CAAC,GAAG,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,OAAO,GACvF,UAAU,GACV,UAAU,SAAS,MAAM,GACvB;KAAG,GAAG,IAAI,MAAM,UAAU,CAAC,CAAC,EAAE,WAAW,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;CAAE,GAC5D,UAAU,CAAC;AAEjB;;;GAGG;AACH,eAAO,MAAM,UAAU,EAAE,OAAO,MAAwC,CAAC;AAoDzE;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,OAAO,SAAS,YAAY,GAAG,GAAG,EAC3E,WAAW,EAAE,WAAW,CAAC,OAAO,CAAC,EACjC,IAAI,CAAC,EAAE,WAAW,CAAC,UAAU,CAAC,GAC7B,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,CAyH7B"}
@@ -0,0 +1,180 @@
1
+ /** The lazy `Proxy` that mocks an erased interface one accessed property at a time. */
2
+ /**
3
+ * The key an auto-mock answers with its reset routine, clearing the root spy, every materialized
4
+ * member, and the spies of its seed.
5
+ */
6
+ export const MOCK_RESET = Symbol("di-testing:mock-reset");
7
+ // `then` would make every mock thenable and stall `await`; `toJSON` and `asymmetricMatch` are probed
8
+ // by serializers and expect() and must not answer as callables.
9
+ const UNMOCKED_KEYS = new Set(["then", "toJSON", "asymmetricMatch"]);
10
+ // Inherited members that inspection and printing rely on; every other inherited key (`apply`,
11
+ // `call`, `bind`, the throwing `caller`/`arguments`) is fair game for a domain interface.
12
+ const INHERITED_PASSTHROUGH = new Set([
13
+ "constructor",
14
+ "toString",
15
+ "toLocaleString",
16
+ "valueOf",
17
+ "hasOwnProperty",
18
+ "isPrototypeOf",
19
+ "propertyIsEnumerable",
20
+ ]);
21
+ /** Clears one spy through whichever reset method its backend spells, recursing into auto-mocks. */
22
+ function resetSpy(spy) {
23
+ if (spy === null || (typeof spy !== "object" && typeof spy !== "function")) {
24
+ return;
25
+ }
26
+ const candidate = spy;
27
+ if (typeof candidate[MOCK_RESET] === "function") {
28
+ candidate[MOCK_RESET]();
29
+ }
30
+ else if (typeof candidate.mockReset === "function") {
31
+ candidate.mockReset();
32
+ }
33
+ else if (typeof candidate.reset === "function") {
34
+ candidate.reset();
35
+ }
36
+ else if (typeof candidate.resetHistory === "function") {
37
+ candidate.resetHistory();
38
+ }
39
+ }
40
+ /** Whether the seed supplies `key` itself — its prototype chain counts, the global prototypes don't. */
41
+ function seedProvides(seed, key) {
42
+ let current = seed;
43
+ while (current !== null && current !== Object.prototype && current !== Function.prototype) {
44
+ if (Object.hasOwn(current, key)) {
45
+ return true;
46
+ }
47
+ current = Object.getPrototypeOf(current);
48
+ }
49
+ return false;
50
+ }
51
+ /**
52
+ * Builds a lazy auto-mock for an erased interface: each accessed member becomes a cached child mock.
53
+ *
54
+ * @remarks The proxy target is itself a spy, so a function-typed dependency records calls, and every
55
+ * materialized member is another auto-mock, so nested access (`repo.user.create`) works to any depth.
56
+ * A `seed` wins for the members it supplies (inherited ones included; a primitive seed is returned
57
+ * as the value itself); keys the root spy owns pass through, so its backend API stays real — which
58
+ * also means a member sharing a name with a function's own `name`/`length` cannot be auto-mocked.
59
+ *
60
+ * @typeParam Dependency - The dependency type being mocked.
61
+ * @typeParam Backend - The spy type the factory produces, threaded into the returned `Mocked` view.
62
+ */
63
+ export function createAutoMock(mockFactory, seed) {
64
+ // A primitive stub has no members to mock — the seed is the dependency's whole value.
65
+ if (seed !== undefined && (typeof seed !== "object" || seed === null) && typeof seed !== "function") {
66
+ return seed;
67
+ }
68
+ const cache = new Map();
69
+ const methodWrappers = new Map();
70
+ const seedRecord = seed;
71
+ const target = mockFactory();
72
+ let self;
73
+ const reset = () => {
74
+ resetSpy(target);
75
+ for (const child of cache.values()) {
76
+ resetSpy(child);
77
+ }
78
+ if (seedRecord !== undefined) {
79
+ for (const value of Object.values(seedRecord)) {
80
+ resetSpy(value);
81
+ }
82
+ }
83
+ };
84
+ const proxy = new Proxy(target, {
85
+ get(fnTarget, key, receiver) {
86
+ if (key === MOCK_RESET) {
87
+ return reset;
88
+ }
89
+ if (seedRecord !== undefined && seedProvides(seedRecord, key)) {
90
+ // A non-configurable, non-writable target property must report its own value (proxy invariant).
91
+ const own = Reflect.getOwnPropertyDescriptor(fnTarget, key);
92
+ if (own !== undefined && own.configurable === false && own.writable === false) {
93
+ return own.value;
94
+ }
95
+ return Reflect.get(seedRecord, key);
96
+ }
97
+ if (typeof key === "symbol" || UNMOCKED_KEYS.has(key)) {
98
+ return Reflect.get(fnTarget, key, receiver);
99
+ }
100
+ if (Object.hasOwn(fnTarget, key)) {
101
+ const value = Reflect.get(fnTarget, key);
102
+ if (typeof value !== "function") {
103
+ return value;
104
+ }
105
+ // The backend's own methods run against the target, and a chainable return re-enters the mock.
106
+ let wrapper = methodWrappers.get(key);
107
+ if (wrapper === undefined) {
108
+ wrapper = (...args) => {
109
+ const result = value.call(fnTarget, ...args);
110
+ return result === fnTarget ? self : result;
111
+ };
112
+ methodWrappers.set(key, wrapper);
113
+ }
114
+ return wrapper;
115
+ }
116
+ if (INHERITED_PASSTHROUGH.has(key)) {
117
+ return Reflect.get(fnTarget, key);
118
+ }
119
+ let child = cache.get(key);
120
+ if (child === undefined) {
121
+ child = createAutoMock(mockFactory);
122
+ cache.set(key, child);
123
+ }
124
+ return child;
125
+ },
126
+ // `in` agrees with `get`: any mockable string key answers true, everything else asks the target.
127
+ has(fnTarget, key) {
128
+ if (key === MOCK_RESET) {
129
+ return true;
130
+ }
131
+ if (seedRecord !== undefined && seedProvides(seedRecord, key)) {
132
+ return true;
133
+ }
134
+ if (typeof key === "symbol" || UNMOCKED_KEYS.has(key)) {
135
+ return Reflect.has(fnTarget, key);
136
+ }
137
+ return true;
138
+ },
139
+ // Enumeration shows the interface's materialized members, not the spy backend's internals.
140
+ ownKeys(fnTarget) {
141
+ const keys = new Set();
142
+ if (seedRecord !== undefined) {
143
+ for (const key of Reflect.ownKeys(seedRecord)) {
144
+ keys.add(key);
145
+ }
146
+ }
147
+ for (const key of cache.keys()) {
148
+ keys.add(key);
149
+ }
150
+ // Non-configurable target keys must be reported (proxy invariant); the rest stay hidden.
151
+ for (const key of Reflect.ownKeys(fnTarget)) {
152
+ const descriptor = Reflect.getOwnPropertyDescriptor(fnTarget, key);
153
+ if (descriptor !== undefined && descriptor.configurable === false) {
154
+ keys.add(key);
155
+ }
156
+ }
157
+ return [...keys];
158
+ },
159
+ getOwnPropertyDescriptor(fnTarget, key) {
160
+ if (typeof key === "string" && (cache.has(key) || (seedRecord !== undefined && seedProvides(seedRecord, key)))) {
161
+ return {
162
+ configurable: true,
163
+ enumerable: true,
164
+ writable: true,
165
+ value: proxy[key],
166
+ };
167
+ }
168
+ return Reflect.getOwnPropertyDescriptor(fnTarget, key);
169
+ },
170
+ deleteProperty(fnTarget, key) {
171
+ if (typeof key === "string" && cache.delete(key)) {
172
+ return true;
173
+ }
174
+ return Reflect.deleteProperty(fnTarget, key);
175
+ },
176
+ });
177
+ self = proxy;
178
+ return proxy;
179
+ }
180
+ //# sourceMappingURL=auto-mock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-mock.js","sourceRoot":"","sources":["../../src/mocking/auto-mock.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /** The pluggable seam that decides which spy backend the auto-mocks are built from. */
2
+ import type { Spy } from "#/mocking/spy";
3
+ /**
4
+ * The loose callable an auto-mock materializes for each accessed property.
5
+ *
6
+ * @remarks Deliberately structural so any backend qualifies: the built-in {@link Spy}, a Vitest
7
+ * `vi.fn()`, a `jest.fn()`, or a Sinon stub all satisfy it.
8
+ */
9
+ export type MockFunction = (...args: ReadonlyArray<unknown>) => unknown;
10
+ /**
11
+ * The factory each auto-mocked member is created by.
12
+ *
13
+ * @remarks `Backend` is the spy type the factory returns, and it flows through the whole test bed:
14
+ * `Mocked` members, `mocks.get`, and the `.stub` callback are all typed against it, so
15
+ * `() => vi.fn()` gives every mock Vitest's own surface and `() => sinon.stub()` gives Sinon's —
16
+ * with no adapter package and no module augmentation.
17
+ *
18
+ * @typeParam Backend - The spy type one factory call produces; defaults to the loose callable.
19
+ */
20
+ export type MockFactory<Backend extends MockFunction = MockFunction> = () => Backend;
21
+ /**
22
+ * The default `MockFactory` — one built-in {@link Spy} per call, with no test-framework dependency.
23
+ */
24
+ export declare const defaultMockFactory: MockFactory<Spy>;
25
+ //# sourceMappingURL=mock-factory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mock-factory.d.ts","sourceRoot":"","sources":["../../src/mocking/mock-factory.ts"],"names":[],"mappings":"AAAA,uFAAuF;AAEvF,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAGzC;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,GAAG,IAAI,EAAE,aAAa,CAAC,OAAO,CAAC,KAAK,OAAO,CAAC;AAExE;;;;;;;;;GASG;AACH,MAAM,MAAM,WAAW,CAAC,OAAO,SAAS,YAAY,GAAG,YAAY,IAAI,MAAM,OAAO,CAAC;AAErF;;GAEG;AACH,eAAO,MAAM,kBAAkB,EAAE,WAAW,CAAC,GAAG,CAAqB,CAAC"}
@@ -0,0 +1,7 @@
1
+ /** The pluggable seam that decides which spy backend the auto-mocks are built from. */
2
+ import { createSpy } from "#/mocking/spy";
3
+ /**
4
+ * The default `MockFactory` — one built-in {@link Spy} per call, with no test-framework dependency.
5
+ */
6
+ export const defaultMockFactory = () => createSpy();
7
+ //# sourceMappingURL=mock-factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mock-factory.js","sourceRoot":"","sources":["../../src/mocking/mock-factory.ts"],"names":[],"mappings":"AAAA,uFAAuF;AAGvF,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAsB1C;;GAEG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAqB,GAAG,EAAE,CAAC,SAAS,EAAE,CAAC"}
@@ -0,0 +1,39 @@
1
+ /** The built-in zero-dependency spy the default mock factory hands out. */
2
+ /**
3
+ * One recorded invocation's outcome — a returned value or a thrown error.
4
+ */
5
+ export interface SpyResult {
6
+ readonly type: "return" | "throw";
7
+ readonly value: unknown;
8
+ }
9
+ /**
10
+ * The call log a spy exposes for assertions.
11
+ *
12
+ * @typeParam Args - The spy's argument tuple.
13
+ */
14
+ export interface SpyState<Args extends ReadonlyArray<unknown>> {
15
+ readonly calls: ReadonlyArray<Args>;
16
+ readonly results: ReadonlyArray<SpyResult>;
17
+ }
18
+ /**
19
+ * The built-in zero-dependency spy: callable, records calls, and takes a return value or implementation.
20
+ *
21
+ * @remarks `Return` defaults to `any` so a spy drops into any typed method slot, exactly as Vitest's
22
+ * own mock type does — the package's one deliberate `any`.
23
+ */
24
+ export interface Spy<Args extends ReadonlyArray<unknown> = ReadonlyArray<unknown>, Return = any> {
25
+ (...args: Args): Return;
26
+ /** The recorded calls and their outcomes. */
27
+ readonly mock: SpyState<Args>;
28
+ /** Sets a fixed value returned by every subsequent call. */
29
+ mockReturnValue(value: Return): this;
30
+ /** Replaces the spy's behaviour with `fn`, still recording each call. */
31
+ mockImplementation(fn: (...args: Args) => Return): this;
32
+ /** Clears the recorded calls and any configured return value or implementation. */
33
+ mockReset(): void;
34
+ }
35
+ /**
36
+ * Creates a fresh zero-dependency spy that records its calls and returns `undefined` until configured.
37
+ */
38
+ export declare function createSpy(): Spy;
39
+ //# sourceMappingURL=spy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spy.d.ts","sourceRoot":"","sources":["../../src/mocking/spy.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAE3E;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC;IAClC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB;AAED;;;;GAIG;AACH,MAAM,WAAW,QAAQ,CAAC,IAAI,SAAS,aAAa,CAAC,OAAO,CAAC;IAC3D,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC;IACpC,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;CAC5C;AAGD;;;;;GAKG;AACH,MAAM,WAAW,GAAG,CAAC,IAAI,SAAS,aAAa,CAAC,OAAO,CAAC,GAAG,aAAa,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,GAAG;IAC7F,CAAC,GAAG,IAAI,EAAE,IAAI,GAAG,MAAM,CAAC;IACxB,6CAA6C;IAC7C,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC9B,4DAA4D;IAC5D,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,yEAAyE;IACzE,kBAAkB,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,MAAM,GAAG,IAAI,CAAC;IACxD,mFAAmF;IACnF,SAAS,IAAI,IAAI,CAAC;CACnB;AAGD;;GAEG;AACH,wBAAgB,SAAS,IAAI,GAAG,CAwC/B"}
@@ -0,0 +1,43 @@
1
+ /** The built-in zero-dependency spy the default mock factory hands out. */
2
+ /* oxlint-enable typescript/no-explicit-any */
3
+ /**
4
+ * Creates a fresh zero-dependency spy that records its calls and returns `undefined` until configured.
5
+ */
6
+ export function createSpy() {
7
+ const calls = [];
8
+ const results = [];
9
+ let implementation;
10
+ let returnValue;
11
+ const spy = Object.assign((...args) => {
12
+ calls.push(args);
13
+ try {
14
+ const value = implementation === undefined ? returnValue : implementation(...args);
15
+ results.push({ type: "return", value });
16
+ return value;
17
+ }
18
+ catch (error) {
19
+ results.push({ type: "throw", value: error });
20
+ throw error;
21
+ }
22
+ }, {
23
+ mock: { calls, results },
24
+ mockReturnValue(value) {
25
+ returnValue = value;
26
+ implementation = undefined;
27
+ return spy;
28
+ },
29
+ mockImplementation(fn) {
30
+ implementation = fn;
31
+ return spy;
32
+ },
33
+ // Cleared in place so `mock` stays one stable object across resets.
34
+ mockReset() {
35
+ calls.length = 0;
36
+ results.length = 0;
37
+ implementation = undefined;
38
+ returnValue = undefined;
39
+ },
40
+ });
41
+ return spy;
42
+ }
43
+ //# sourceMappingURL=spy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spy.js","sourceRoot":"","sources":["../../src/mocking/spy.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAsC3E,8CAA8C;AAE9C;;GAEG;AACH,MAAM,UAAU,SAAS;IACvB,MAAM,KAAK,GAAkC,EAAE,CAAC;IAChD,MAAM,OAAO,GAAqB,EAAE,CAAC;IACrC,IAAI,cAA0E,CAAC;IAC/E,IAAI,WAAoB,CAAC;IAEzB,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CACvB,CAAC,GAAG,IAA4B,EAAW,EAAE;QAC3C,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjB,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,cAAc,KAAK,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,cAAc,CAAC,GAAG,IAAI,CAAC,CAAC;YACnF,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;YACxC,OAAO,KAAK,CAAC;QACf,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;YAC9C,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC,EACD;QACE,IAAI,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE;QACxB,eAAe,CAAC,KAAc;YAC5B,WAAW,GAAG,KAAK,CAAC;YACpB,cAAc,GAAG,SAAS,CAAC;YAC3B,OAAO,GAAG,CAAC;QACb,CAAC;QACD,kBAAkB,CAAC,EAAgD;YACjE,cAAc,GAAG,EAAE,CAAC;YACpB,OAAO,GAAG,CAAC;QACb,CAAC;QACD,oEAAoE;QACpE,SAAS;YACP,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;YACjB,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;YACnB,cAAc,GAAG,SAAS,CAAC;YAC3B,WAAW,GAAG,SAAS,CAAC;QAC1B,CAAC;KACF,CACK,CAAC;IAET,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,83 @@
1
+ /** The override-recording core both test-bed builders extend. */
2
+ import type { Constructor, Container, DependencyKey, InjectOptions, MetadataReader } from "@codefast/di";
3
+ import type { BoundMock, SlottedOverride } from "#/discovery/mock-binder";
4
+ import type { DeepPartial } from "#/mocking/auto-mock";
5
+ import type { MockFactory, MockFunction } from "#/mocking/mock-factory";
6
+ import type { Spy } from "#/mocking/spy";
7
+ import type { InjectionIdentifier } from "#/types";
8
+ /**
9
+ * Options that configure a whole test-bed compile.
10
+ *
11
+ * @typeParam Backend - The spy type the mock factory produces; it flows into every `Mocked` member,
12
+ * `mocks.get`, and the `.stub` callback, so `() => vi.fn()` yields Vitest's own mock typing.
13
+ */
14
+ export interface TestBedOptions<Backend extends MockFunction = Spy> {
15
+ /** Spy factory each auto-mock property is materialized with; defaults to the built-in spy. */
16
+ readonly mockFactory?: MockFactory<Backend> | undefined;
17
+ /** Reader the dependency scan and the compile container both consult; defaults to di's reader. */
18
+ readonly metadataReader?: MetadataReader | undefined;
19
+ }
20
+ /**
21
+ * The override step: choose how one named dependency is supplied instead of a plain auto-mock.
22
+ *
23
+ * @typeParam Dependency - The dependency's value type.
24
+ * @typeParam Owner - The builder the chain returns to.
25
+ * @typeParam Backend - The spy type the bed's mock factory produces.
26
+ */
27
+ export interface MockOverrideBuilder<Dependency, Owner, Backend extends MockFunction = Spy> {
28
+ /**
29
+ * Supplies a fixed value for this dependency.
30
+ *
31
+ * @remarks The value is bound as-is and sealed: it has no mock surface, so `mocks.get` refuses
32
+ * it rather than hand it back mistyped — the test already holds the reference it passed in.
33
+ */
34
+ using(value: Dependency): Owner;
35
+ /**
36
+ * Supplies a partial stub, built from the active spy factory; unlisted members stay auto-mocked.
37
+ *
38
+ * @remarks The callback runs once per compile, so beds built from one builder never share spies.
39
+ */
40
+ stub(setup: (fn: MockFactory<Backend>) => DeepPartial<Dependency>): Owner;
41
+ /** Leaves the dependency unbound: an `optional()` slot resolves `undefined`, an `injectAll()` slot `[]`. */
42
+ absent(): Owner;
43
+ /** Supplies every element of an unconstrained `injectAll()` slot, in order. Sealed like `.using`. */
44
+ usingAll(values: ReadonlyArray<Dependency>): Owner;
45
+ }
46
+ /**
47
+ * What a builder's prepare step hands the compile template: the container and the bound mock entries.
48
+ */
49
+ export interface PreparedBed {
50
+ readonly container: Container;
51
+ readonly mocks: ReadonlyMap<DependencyKey, ReadonlyArray<BoundMock>>;
52
+ }
53
+ /**
54
+ * The shared builder core: records overrides, resolves the reader and mock factory, and owns the
55
+ * compile template that disposes the container when a build fails.
56
+ *
57
+ * @remarks Fields are `protected` rather than `#` so the two concrete builders stay thin; nothing
58
+ * outside `test-bed/` extends this class.
59
+ *
60
+ * @typeParam Class - The class under test.
61
+ * @typeParam Backend - The spy type the bed's mock factory produces.
62
+ */
63
+ export declare abstract class BedBuilder<Class, Backend extends MockFunction = Spy> {
64
+ #private;
65
+ protected readonly target: Constructor<Class>;
66
+ protected readonly reader: MetadataReader;
67
+ protected readonly mockFactory: MockFactory<Backend>;
68
+ protected readonly overrides: ReadonlyMap<DependencyKey, ReadonlyArray<SlottedOverride>>;
69
+ constructor(target: Constructor<Class>, options?: TestBedOptions<Backend>);
70
+ /**
71
+ * Replaces the auto-mock for one dependency with a hand-written stub or a concrete value.
72
+ *
73
+ * @remarks Pass `options` (a name or tags) to target one slot of a token bound several ways;
74
+ * without them the override covers every slot of the token that has no more specific override.
75
+ * Registering the same target twice replaces the earlier override wholesale.
76
+ */
77
+ mock<Dependency>(identifier: InjectionIdentifier<Dependency>, options?: InjectOptions): MockOverrideBuilder<Dependency, this, Backend>;
78
+ /** Resolves the unit inside a guard that disposes the container when any compile step throws. */
79
+ protected compileWith<Prepared extends PreparedBed, Bed>(prepare: () => Prepared, build: (unit: Class, prepared: Prepared) => Bed): Bed;
80
+ /** The async twin of {@link compileWith}, for units whose activation is asynchronous. */
81
+ protected compileWithAsync<Prepared extends PreparedBed, Bed>(prepare: () => Prepared, build: (unit: Class, prepared: Prepared) => Bed): Promise<Bed>;
82
+ }
83
+ //# sourceMappingURL=bed-builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bed-builder.d.ts","sourceRoot":"","sources":["../../src/test-bed/bed-builder.ts"],"names":[],"mappings":"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"}