@noego/testing 0.2.0 → 0.4.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/dist/index.cjs +87 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +48 -2
- package/dist/index.d.ts +48 -2
- package/dist/index.js +85 -6
- package/dist/index.js.map +1 -1
- package/package.json +4 -5
package/dist/index.cjs
CHANGED
|
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
AmbiguousNameKeyError: () => AmbiguousNameKeyError,
|
|
23
24
|
CallScriptExhaustedError: () => CallScriptExhaustedError,
|
|
24
25
|
ENV_REGISTRY: () => ENV_REGISTRY,
|
|
25
26
|
ExpectationOverflowError: () => ExpectationOverflowError,
|
|
@@ -29,6 +30,7 @@ __export(index_exports, {
|
|
|
29
30
|
NonObjectMethodTargetError: () => NonObjectMethodTargetError,
|
|
30
31
|
TestIocBuilder: () => TestIocBuilder,
|
|
31
32
|
TestingError: () => TestingError,
|
|
33
|
+
TokenIdentitySplitError: () => TokenIdentitySplitError,
|
|
32
34
|
UnknownTokenKeyError: () => UnknownTokenKeyError,
|
|
33
35
|
UnwatchedInspectionError: () => UnwatchedInspectionError,
|
|
34
36
|
VerificationError: () => VerificationError,
|
|
@@ -107,6 +109,22 @@ var UnknownTokenKeyError = class extends TestingError {
|
|
|
107
109
|
};
|
|
108
110
|
var InvalidDescriptorError = class extends TestingError {
|
|
109
111
|
};
|
|
112
|
+
var AmbiguousNameKeyError = class extends TestingError {
|
|
113
|
+
constructor(name, space) {
|
|
114
|
+
super(
|
|
115
|
+
`.${space}() key "${name}" is ambiguous: multiple distinct tokens share that display name. Use the canonical tuple form with the exact token: .${space}([[TheToken, ...]]).`
|
|
116
|
+
);
|
|
117
|
+
this.name = "AmbiguousNameKeyError";
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
var TokenIdentitySplitError = class extends TestingError {
|
|
121
|
+
constructor(names) {
|
|
122
|
+
super(
|
|
123
|
+
`token identity split: .methods() entr${names.length === 1 ? "y" : "ies"} for ${names.map((name) => `"${name}"`).join(", ")} never matched a constructed instance, but a DIFFERENT class with the same name was constructed. Two module registries have loaded the same class file (test-file import vs harness graph import) \u2014 the configured behavior did not apply. Route the harness's imports through your registry (e.g. testApp .importer((p) => import(p))) or pass the exact token the graph uses.`
|
|
124
|
+
);
|
|
125
|
+
this.name = "TokenIdentitySplitError";
|
|
126
|
+
}
|
|
127
|
+
};
|
|
110
128
|
|
|
111
129
|
// src/descriptors.ts
|
|
112
130
|
var DESCRIPTOR = /* @__PURE__ */ Symbol.for("noego:testing:descriptor");
|
|
@@ -227,6 +245,10 @@ var WatchRegistry = class {
|
|
|
227
245
|
states = /* @__PURE__ */ new Map();
|
|
228
246
|
nameIndex = /* @__PURE__ */ new Map();
|
|
229
247
|
entries = [];
|
|
248
|
+
/** Class-token entries that identity-matched at least one construction. */
|
|
249
|
+
matchedEntryKeys = /* @__PURE__ */ new Set();
|
|
250
|
+
/** Constructed class tokens that matched NO entry, by display name. */
|
|
251
|
+
unmatchedConstructedByName = /* @__PURE__ */ new Map();
|
|
230
252
|
addEntry(entry) {
|
|
231
253
|
this.entries.push(entry);
|
|
232
254
|
const byMethod = /* @__PURE__ */ new Map();
|
|
@@ -246,9 +268,15 @@ var WatchRegistry = class {
|
|
|
246
268
|
/** Entries applying to a resolving token (exact identity or name match). */
|
|
247
269
|
matchEntries(token) {
|
|
248
270
|
const name = typeof token === "function" ? token.name : typeof token === "string" ? token : void 0;
|
|
249
|
-
|
|
271
|
+
const matched = this.entries.filter(
|
|
250
272
|
(entry) => entry.key === token || entry.byName && name !== void 0 && entry.key === name
|
|
251
273
|
);
|
|
274
|
+
if (matched.length) {
|
|
275
|
+
for (const entry of matched) this.matchedEntryKeys.add(entry.key);
|
|
276
|
+
} else if (typeof token === "function" && name) {
|
|
277
|
+
this.unmatchedConstructedByName.set(name, token);
|
|
278
|
+
}
|
|
279
|
+
return matched;
|
|
252
280
|
}
|
|
253
281
|
state(entryKey, method) {
|
|
254
282
|
return this.states.get(entryKey)?.get(method);
|
|
@@ -264,8 +292,34 @@ var WatchRegistry = class {
|
|
|
264
292
|
}
|
|
265
293
|
throw new UnwatchedInspectionError(label, method);
|
|
266
294
|
}
|
|
295
|
+
/**
|
|
296
|
+
* Class-token entries that never identity-matched a construction while a
|
|
297
|
+
* DIFFERENT class with the same display name did construct. This is the
|
|
298
|
+
* split-module-registry signature (a test-file class object vs the graph's
|
|
299
|
+
* own load of the same file) — or two genuinely distinct same-named tokens
|
|
300
|
+
* where the configured one never resolved. Either way the configured
|
|
301
|
+
* behavior silently did not apply, which must be loud.
|
|
302
|
+
*/
|
|
303
|
+
identitySplits() {
|
|
304
|
+
const splits = [];
|
|
305
|
+
for (const entry of this.entries) {
|
|
306
|
+
if (entry.byName || typeof entry.key !== "function") continue;
|
|
307
|
+
if (this.matchedEntryKeys.has(entry.key)) continue;
|
|
308
|
+
const name = entry.key.name;
|
|
309
|
+
if (!name) continue;
|
|
310
|
+
const constructed = this.unmatchedConstructedByName.get(name);
|
|
311
|
+
if (constructed !== void 0 && constructed !== entry.key) {
|
|
312
|
+
splits.push({ name, entryToken: entry.key });
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return splits;
|
|
316
|
+
}
|
|
267
317
|
/** Repeatable snapshot check of all exact expectations. */
|
|
268
318
|
verify() {
|
|
319
|
+
const splits = this.identitySplits();
|
|
320
|
+
if (splits.length) {
|
|
321
|
+
throw new TokenIdentitySplitError(splits.map((split) => split.name));
|
|
322
|
+
}
|
|
269
323
|
const failures = [];
|
|
270
324
|
for (const byMethod of this.states.values()) {
|
|
271
325
|
for (const state of byMethod.values()) {
|
|
@@ -414,7 +468,16 @@ function buildMethodWrapper(target, method, descriptor, state) {
|
|
|
414
468
|
// src/builder.ts
|
|
415
469
|
var COMPONENT_OPTIONS_KEY = /* @__PURE__ */ Symbol.for("ioc:component:options");
|
|
416
470
|
function* configEntries(config) {
|
|
417
|
-
if (config
|
|
471
|
+
if (Array.isArray(config)) {
|
|
472
|
+
for (const entry of config) {
|
|
473
|
+
if (!Array.isArray(entry) || entry.length !== 2) {
|
|
474
|
+
throw new InvalidDescriptorError(
|
|
475
|
+
"canonical composition entries are [token, value] tuples, e.g. .classes([[Token, Impl]])"
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
yield [entry[0], false, entry[1]];
|
|
479
|
+
}
|
|
480
|
+
} else if (config instanceof Map) {
|
|
418
481
|
for (const [key, value] of config) yield [key, false, value];
|
|
419
482
|
} else {
|
|
420
483
|
for (const key of [...Object.getOwnPropertyNames(config), ...Object.getOwnPropertySymbols(config)]) {
|
|
@@ -517,16 +580,24 @@ var TestIocBuilder = class _TestIocBuilder {
|
|
|
517
580
|
throw new MissingIocSeamError();
|
|
518
581
|
}
|
|
519
582
|
const knownByName = /* @__PURE__ */ new Map();
|
|
583
|
+
const ambiguousNames = /* @__PURE__ */ new Set();
|
|
520
584
|
const knownLifetimes = /* @__PURE__ */ new Map();
|
|
521
585
|
const note = (token, loadAs) => {
|
|
522
|
-
|
|
523
|
-
|
|
586
|
+
const name = typeof token === "function" && token.name ? token.name : typeof token === "string" ? token : null;
|
|
587
|
+
if (name !== null) {
|
|
588
|
+
const existing = knownByName.get(name);
|
|
589
|
+
if (existing !== void 0 && existing !== token) ambiguousNames.add(name);
|
|
590
|
+
knownByName.set(name, token);
|
|
591
|
+
}
|
|
524
592
|
if (loadAs !== void 0) knownLifetimes.set(token, loadAs);
|
|
525
593
|
};
|
|
526
594
|
const effective = /* @__PURE__ */ new Map();
|
|
527
595
|
const methodWrites = [];
|
|
528
596
|
const resolveKey = (key, byName, space) => {
|
|
529
597
|
if (!byName) return key;
|
|
598
|
+
if (ambiguousNames.has(key)) {
|
|
599
|
+
throw new AmbiguousNameKeyError(key, space);
|
|
600
|
+
}
|
|
530
601
|
const known = knownByName.get(key);
|
|
531
602
|
if (known !== void 0) return known;
|
|
532
603
|
if (space === "values" || space === "functions") return key;
|
|
@@ -571,8 +642,13 @@ var TestIocBuilder = class _TestIocBuilder {
|
|
|
571
642
|
space: "class",
|
|
572
643
|
token,
|
|
573
644
|
implementation: write.implementation,
|
|
574
|
-
//
|
|
575
|
-
|
|
645
|
+
// Lifetime priority: the replacement's own @Component scope, then
|
|
646
|
+
// the lifetime the composition already knows for the token, then
|
|
647
|
+
// the TOKEN class's declared @Component scope — a plain stub
|
|
648
|
+
// class replacing a Singleton-scoped production service must not
|
|
649
|
+
// silently degrade to Transient (captive-lifetime validation
|
|
650
|
+
// would reject the production dependents).
|
|
651
|
+
loadAs: componentScope(write.implementation) ?? knownLifetimes.get(token) ?? componentScope(token)
|
|
576
652
|
});
|
|
577
653
|
break;
|
|
578
654
|
}
|
|
@@ -597,6 +673,9 @@ var TestIocBuilder = class _TestIocBuilder {
|
|
|
597
673
|
break;
|
|
598
674
|
}
|
|
599
675
|
case "methods": {
|
|
676
|
+
if (write.byName && ambiguousNames.has(write.key)) {
|
|
677
|
+
throw new AmbiguousNameKeyError(write.key, "methods");
|
|
678
|
+
}
|
|
600
679
|
const token = write.byName && knownByName.has(write.key) ? knownByName.get(write.key) : write.key;
|
|
601
680
|
methodWrites.push({
|
|
602
681
|
key: token,
|
|
@@ -672,6 +751,7 @@ function testIoc(...inputs) {
|
|
|
672
751
|
}
|
|
673
752
|
// Annotate the CommonJS export names for ESM import in node:
|
|
674
753
|
0 && (module.exports = {
|
|
754
|
+
AmbiguousNameKeyError,
|
|
675
755
|
CallScriptExhaustedError,
|
|
676
756
|
ENV_REGISTRY,
|
|
677
757
|
ExpectationOverflowError,
|
|
@@ -681,6 +761,7 @@ function testIoc(...inputs) {
|
|
|
681
761
|
NonObjectMethodTargetError,
|
|
682
762
|
TestIocBuilder,
|
|
683
763
|
TestingError,
|
|
764
|
+
TokenIdentitySplitError,
|
|
684
765
|
UnknownTokenKeyError,
|
|
685
766
|
UnwatchedInspectionError,
|
|
686
767
|
VerificationError,
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/builder.ts","../src/errors.ts","../src/descriptors.ts","../src/method_state.ts"],"sourcesContent":["/**\n * @noego/testing — the canonical shared owner of real-IoC test composition\n * (`testIoc`) and the lowercase `test.*` method behavior/watch/expectation\n * language, built on @noego/ioc's production seams.\n *\n * The former generic runtime-mirror doubles (ManualClock, ScriptedFetchClient,\n * memory stores, recording sinks, contract suites, LeakDetector, …) are\n * retired; pin 0.1.x if you still need them, and see the NoEgo runtime/testing\n * deprecation plan for per-export dispositions.\n */\n\nexport { testIoc, TestIocBuilder } from './builder';\nexport type { TestEnvironment, ConfigMap, MethodsConfig, UseInput } from './builder';\n\nexport { test, ENV_REGISTRY } from './descriptors';\nexport type {\n BehaviorDescriptor,\n CallsDescriptor,\n ExpectationDescriptor,\n MethodDescriptor,\n MethodInspection,\n OriginalDescriptor,\n RawMethodWrapper,\n RecordedCall,\n ReturnsDescriptor,\n ThrowsDescriptor,\n WatchDescriptor,\n} from './descriptors';\n\nexport {\n TestingError,\n MissingIocSeamError,\n UnwatchedInspectionError,\n CallScriptExhaustedError,\n ExpectationOverflowError,\n VerificationError,\n MethodNotCallableError,\n NonObjectMethodTargetError,\n UnknownTokenKeyError,\n InvalidDescriptorError,\n} from './errors';\n","/**\n * `testIoc` — the canonical shared real-IoC test composition builder.\n *\n * Persistent immutable: every fluent call returns a new derived builder\n * sharing the ordered write log structurally. Non-conflicting writes are\n * order-insensitive; the last write to the same effective identity wins on\n * that derived branch. `.build()` is non-consuming and creates fresh runtime,\n * watch, and expectation state (spec 15, PBR-01..15).\n */\n\nimport {\n createContainer,\n flattenModule,\n LoadAs,\n SCOPED_CONTAINER,\n type ApplicationModule,\n type IContainer,\n} from '@noego/ioc';\n\nimport { ENV_REGISTRY, isDescriptor, type MethodDescriptor } from './descriptors';\nimport { createMethodDecorator, WatchRegistry, tokenLabel } from './method_state';\nimport { InvalidDescriptorError, MissingIocSeamError, UnknownTokenKeyError } from './errors';\n\nconst COMPONENT_OPTIONS_KEY = Symbol.for('ioc:component:options');\n\ntype Token = unknown;\ntype ClassLike = new (...args: any[]) => any;\n\n/** Config maps accept plain objects (string keys) or Maps (exact tokens). */\nexport type ConfigMap<V> = Record<string, V> | ReadonlyMap<Token, V>;\n\nexport type MethodsConfig = ConfigMap<Record<string, MethodDescriptor> | ReadonlyMap<string, MethodDescriptor>>;\n\nexport type UseInput = ApplicationModule | TestIocBuilder;\n\ntype Write =\n | { op: 'use'; module: ApplicationModule }\n | { op: 'classes'; key: Token; byName: boolean; implementation: ClassLike }\n | { op: 'functions'; key: Token; byName: boolean; factory: (...args: any[]) => any }\n | { op: 'values'; key: Token; byName: boolean; value: unknown }\n | { op: 'methods'; key: Token; byName: boolean; methods: ReadonlyMap<string, MethodDescriptor> };\n\nexport interface TestEnvironment {\n readonly root: IContainer;\n get<T>(token: unknown, params?: any[]): Promise<T> | T;\n instance<T>(cls: new (...args: any[]) => T, params?: any[]): Promise<T> | T;\n extend(): IContainer;\n verify(): Promise<void>;\n dispose(): Promise<void>;\n readonly [ENV_REGISTRY]: WatchRegistry;\n}\n\nfunction* configEntries<V>(config: ConfigMap<V>): Iterable<[Token, boolean, V]> {\n if (config instanceof Map) {\n for (const [key, value] of config) yield [key, false, value];\n } else {\n for (const key of [...Object.getOwnPropertyNames(config), ...Object.getOwnPropertySymbols(config)]) {\n yield [key, typeof key === 'string', (config as any)[key]];\n }\n }\n}\n\nfunction componentScope(cls: any): LoadAs | undefined {\n const options =\n typeof Reflect !== 'undefined' && (Reflect as any).getMetadata\n ? (Reflect as any).getMetadata(COMPONENT_OPTIONS_KEY, cls)\n : undefined;\n return options?.scope;\n}\n\nfunction lifetimeToLoadAs(lifetime: string): LoadAs {\n if (lifetime === 'singleton') return LoadAs.Singleton;\n if (lifetime === 'scoped') return LoadAs.Scoped;\n return LoadAs.Transient;\n}\n\nexport class TestIocBuilder {\n private constructor(private readonly log: readonly Write[]) {\n Object.freeze(this);\n }\n\n /** @internal */\n static create(inputs: readonly UseInput[]): TestIocBuilder {\n return new TestIocBuilder([]).useAll(inputs);\n }\n\n /** @internal — read by .use(builderPreset) */\n get writes(): readonly Write[] {\n return this.log;\n }\n\n private derive(writes: Write[]): TestIocBuilder {\n return new TestIocBuilder([...this.log, ...writes]);\n }\n\n private useAll(inputs: readonly UseInput[]): TestIocBuilder {\n let builder: TestIocBuilder = this;\n for (const input of inputs) builder = builder.use(input);\n return builder;\n }\n\n /** Apply a reusable composition preset: an ApplicationModule or a builder. */\n use(preset: UseInput): TestIocBuilder {\n if (preset instanceof TestIocBuilder) {\n return this.derive([...preset.writes]);\n }\n return this.derive([{ op: 'use', module: preset }]);\n }\n\n /** Replace the implementation for IoC class tokens in the built environment. */\n classes(config: ConfigMap<ClassLike>): TestIocBuilder {\n const writes: Write[] = [];\n for (const [key, byName, implementation] of configEntries(config)) {\n if (typeof implementation !== 'function' || !implementation.prototype) {\n throw new InvalidDescriptorError(\n `.classes() value for \"${tokenLabel(key)}\" must be a class constructor.`,\n );\n }\n writes.push({ op: 'classes', key, byName, implementation });\n }\n return this.derive(writes);\n }\n\n /** Replace IoC factory/provider registrations. */\n functions(config: ConfigMap<(...args: any[]) => any>): TestIocBuilder {\n const writes: Write[] = [];\n for (const [key, byName, factory] of configEntries(config)) {\n if (typeof factory !== 'function') {\n throw new InvalidDescriptorError(`.functions() value for \"${tokenLabel(key)}\" must be a function.`);\n }\n writes.push({ op: 'functions', key, byName, factory });\n }\n return this.derive(writes);\n }\n\n /** Provide/replace IoC value registrations. */\n values(config: ConfigMap<unknown>): TestIocBuilder {\n const writes: Write[] = [];\n for (const [key, byName, value] of configEntries(config)) {\n writes.push({ op: 'values', key, byName, value });\n }\n return this.derive(writes);\n }\n\n /** Install method behavior/observation descriptors on IoC-managed instances. */\n methods(config: MethodsConfig): TestIocBuilder {\n const writes: Write[] = [];\n for (const [key, byName, methodsInput] of configEntries(config)) {\n const methods = new Map<string, MethodDescriptor>();\n const entries =\n methodsInput instanceof Map\n ? methodsInput.entries()\n : Object.entries(methodsInput as Record<string, MethodDescriptor>);\n for (const [name, descriptor] of entries) {\n if (typeof descriptor !== 'function' && !isDescriptor(descriptor)) {\n throw new InvalidDescriptorError(\n `.methods() entry ${tokenLabel(key)}.${name} must be a test.* descriptor or a raw wrapper function.`,\n );\n }\n methods.set(name, descriptor);\n }\n writes.push({ op: 'methods', key, byName, methods });\n }\n return this.derive(writes);\n }\n\n /** Materialize a fresh, isolated real-IoC environment. Non-consuming. */\n async build(): Promise<TestEnvironment> {\n const root = createContainer();\n if (typeof (root as any).setInstanceDecorator !== 'function') {\n throw new MissingIocSeamError();\n }\n\n // ---- Materialize the ordered log: last write wins per effective identity\n // Known tokens (for resolving string keys of classes/functions/values)\n const knownByName = new Map<string, Token>();\n const knownLifetimes = new Map<Token, LoadAs>();\n const note = (token: Token, loadAs: LoadAs | undefined) => {\n if (typeof token === 'function' && (token as any).name) knownByName.set((token as any).name, token);\n else if (typeof token === 'string') knownByName.set(token, token);\n if (loadAs !== undefined) knownLifetimes.set(token, loadAs);\n };\n\n type Effective =\n | { space: 'class'; token: Token; implementation: ClassLike; loadAs?: LoadAs }\n | { space: 'factory'; token: Token; factory: (...args: any[]) => any; loadAs?: LoadAs; deps?: Token[] }\n | { space: 'value'; token: Token; value: unknown };\n\n // ordered map: identity → latest effective write (Map preserves first-write\n // position which is fine — later writes replace content, LWW)\n const effective = new Map<Token, Effective>();\n const methodWrites: { key: Token; byName: boolean; methods: Map<string, MethodDescriptor> }[] = [];\n\n const resolveKey = (key: Token, byName: boolean, space: string): Token => {\n if (!byName) return key;\n const known = knownByName.get(key as string);\n if (known !== undefined) return known;\n if (space === 'values' || space === 'functions') return key; // string tokens are first-class\n throw new UnknownTokenKeyError(key as string, space, [...knownByName.keys()]);\n };\n\n for (const write of this.log) {\n switch (write.op) {\n case 'use': {\n for (const reg of flattenModule(write.module)) {\n const loadAs = lifetimeToLoadAs(reg.lifetime);\n note(reg.token, loadAs);\n if (reg.kind === 'class') {\n note(reg.implementation as Token, loadAs);\n effective.set(reg.token, {\n space: 'class',\n token: reg.token,\n implementation: reg.implementation as ClassLike,\n loadAs,\n });\n knownLifetimes.set(reg.token, loadAs);\n knownLifetimes.set(reg.implementation as Token, loadAs);\n } else if (reg.kind === 'factory') {\n effective.set(reg.token, {\n space: 'factory',\n token: reg.token,\n factory: reg.implementation as (...args: any[]) => any,\n loadAs,\n deps: [...reg.dependencies],\n });\n } else {\n effective.set(reg.token, { space: 'value', token: reg.token, value: reg.implementation });\n knownLifetimes.set(reg.token, LoadAs.Singleton);\n }\n }\n break;\n }\n case 'classes': {\n const token = resolveKey(write.key, write.byName, 'classes');\n note(write.implementation, undefined);\n if (!write.byName) note(token, undefined);\n effective.set(token, {\n space: 'class',\n token,\n implementation: write.implementation,\n // preserve configured lifetime unless the replacement declares its own scope\n loadAs: componentScope(write.implementation) ?? knownLifetimes.get(token),\n });\n break;\n }\n case 'functions': {\n const token = resolveKey(write.key, write.byName, 'functions');\n const prior = effective.get(token);\n effective.set(token, {\n space: 'factory',\n token,\n factory: write.factory,\n // preserve configured lifetime unless the scenario overrides it\n loadAs: prior && prior.space === 'factory' ? prior.loadAs : knownLifetimes.get(token),\n deps: prior && prior.space === 'factory' ? prior.deps : undefined,\n });\n note(token, undefined);\n break;\n }\n case 'values': {\n const token = resolveKey(write.key, write.byName, 'values');\n effective.set(token, { space: 'value', token, value: write.value });\n note(token, LoadAs.Singleton);\n break;\n }\n case 'methods': {\n const token = write.byName && knownByName.has(write.key as string)\n ? knownByName.get(write.key as string)!\n : write.key;\n methodWrites.push({\n key: token,\n byName: write.byName && !knownByName.has(write.key as string),\n methods: new Map(write.methods),\n });\n break;\n }\n }\n }\n\n // ---- Apply effective registrations to the fresh root\n for (const entry of effective.values()) {\n if (entry.space === 'class') {\n this.registerClassBinding(root, entry.token, entry.implementation, entry.loadAs);\n } else if (entry.space === 'factory') {\n root.registerFunction(entry.token, entry.factory, {\n loadAs: entry.loadAs,\n param: entry.deps,\n } as any);\n } else {\n const value = entry.value;\n root.registerFunction(entry.token, () => value, { loadAs: LoadAs.Singleton } as any);\n }\n }\n\n // ---- Fresh watch/expectation state + the ioc decoration seam\n const registry = new WatchRegistry();\n // merge method writes: LWW per (entry key, method), deep-merge per token\n const mergedMethods = new Map<Token, { byName: boolean; methods: Map<string, MethodDescriptor> }>();\n for (const write of methodWrites) {\n const existing = mergedMethods.get(write.key);\n if (existing) {\n for (const [name, descriptor] of write.methods) existing.methods.set(name, descriptor);\n } else {\n mergedMethods.set(write.key, { byName: write.byName, methods: new Map(write.methods) });\n }\n }\n for (const [key, { byName, methods }] of mergedMethods) {\n registry.addEntry({ key, byName, methods });\n }\n (root as any).setInstanceDecorator(createMethodDecorator(registry));\n\n const env: TestEnvironment = {\n root: root as IContainer,\n get: (token, params) => root.get(token as any, params),\n instance: (cls, params) => root.instance(cls, params),\n extend: () => root.extend() as IContainer,\n verify: async () => registry.verify(),\n dispose: () => root.dispose(),\n [ENV_REGISTRY]: registry,\n };\n return env;\n }\n\n /**\n * Register a class binding. When token === implementation this is a plain\n * class registration. Otherwise an alias factory resolves the implementation\n * through real IoC, mirroring the implementation's effective lifetime so\n * lifetime validation (captive-lifetime checks) stays honest.\n */\n private registerClassBinding(\n root: IContainer,\n token: Token,\n implementation: ClassLike,\n configuredLoadAs?: LoadAs,\n ): void {\n const loadAs = configuredLoadAs ?? componentScope(implementation) ?? LoadAs.Transient;\n root.registerClass(implementation as any, configuredLoadAs !== undefined ? ({ loadAs } as any) : undefined);\n if (token === implementation) return;\n\n if (loadAs === LoadAs.Singleton) {\n root.registerFunction(token, () => root.get(implementation as any), {\n loadAs: LoadAs.Singleton,\n } as any);\n } else {\n root.registerFunction(token, (scope: IContainer) => scope.get(implementation as any), {\n loadAs,\n param: [SCOPED_CONTAINER],\n } as any);\n }\n }\n}\n\n/** Create a persistent immutable real-IoC test composition builder. */\nexport function testIoc(...inputs: UseInput[]): TestIocBuilder {\n return TestIocBuilder.create(inputs);\n}\n","/**\n * Diagnostics are first-class: every error names the real token/method and\n * what was expected vs what happened, never only internal wrapper machinery.\n */\n\nexport class TestingError extends Error {\n constructor(message: string) {\n super(message);\n this.name = new.target.name;\n }\n}\n\n/** Thrown at build() when the installed @noego/ioc lacks the decoration seam. */\nexport class MissingIocSeamError extends TestingError {\n constructor() {\n super(\n '@noego/testing requires an @noego/ioc version that provides ' +\n 'Container.setInstanceDecorator (>= 0.5.x with the instance-decoration seam). ' +\n 'Upgrade @noego/ioc.',\n );\n }\n}\n\n/** test.inspect() on a method that is not watched in this environment. */\nexport class UnwatchedInspectionError extends TestingError {\n constructor(token: string, method: string) {\n super(\n `Method \"${method}\" on ${token} is not watched in this environment. ` +\n 'Only watched methods are inspectable — install test.watch() or any ' +\n 'test.* behavior/expectation descriptor for it.',\n );\n }\n}\n\n/** A call arrived after a test.calls([...]) script was fully consumed. */\nexport class CallScriptExhaustedError extends TestingError {\n constructor(token: string, method: string, scriptLength: number, callIndex: number) {\n super(\n `Call #${callIndex} to ${token}.${method} exceeds its test.calls() script ` +\n `of ${scriptLength} ${scriptLength === 1 ? 'entry' : 'entries'}.`,\n );\n }\n}\n\n/** An exact expectation (once/times/never) was exceeded at call time. */\nexport class ExpectationOverflowError extends TestingError {\n constructor(token: string, method: string, expected: number, attempted: number) {\n super(\n expected === 0\n ? `${token}.${method} was expected never to be called, but it was invoked.`\n : `${token}.${method} was expected exactly ${expected} ` +\n `${expected === 1 ? 'call' : 'calls'}, but call #${attempted} arrived.`,\n );\n }\n}\n\n/** Aggregated under-count failures reported by env.verify(). */\nexport class VerificationError extends TestingError {\n constructor(failures: readonly { token: string; method: string; expected: number; actual: number }[]) {\n super(\n 'Exact method expectations were not satisfied:\\n' +\n failures\n .map(\n (f) =>\n ` - ${f.token}.${f.method}: expected exactly ${f.expected} ` +\n `${f.expected === 1 ? 'call' : 'calls'}, observed ${f.actual}`,\n )\n .join('\\n'),\n );\n }\n}\n\n/** .methods configured for a token whose resolved value has no such callable method. */\nexport class MethodNotCallableError extends TestingError {\n constructor(token: string, method: string) {\n super(\n `Cannot install a test.* descriptor on ${token}.${method}: the resolved ` +\n 'instance has no callable method with that name.',\n );\n }\n}\n\n/** .methods configured for a token that resolved to a non-object value. */\nexport class NonObjectMethodTargetError extends TestingError {\n constructor(token: string) {\n super(\n `.methods() is configured for ${token}, but that token resolved to a ` +\n 'non-object value. Method descriptors apply only to IoC-managed instances.',\n );\n }\n}\n\n/** A string configuration key could not be resolved to a known IoC token. */\nexport class UnknownTokenKeyError extends TestingError {\n constructor(key: string, space: string, known: readonly string[]) {\n super(\n `Unknown ${space} key \"${key}\" — it does not match any token known to this ` +\n 'builder. Pass the class/token itself via a Map, or include the ' +\n 'registration through .use(...). Known tokens: ' +\n (known.length ? known.join(', ') : '(none)'),\n );\n }\n}\n\n/** Invalid descriptor construction (e.g. test.times(-1)). */\nexport class InvalidDescriptorError extends TestingError {}\n","/**\n * Lowercase `test.*` descriptors — immutable frozen values.\n *\n * Descriptors carry NO mutable state (no cursors, no counters, no histories);\n * all mutable invocation state lives in the built environment, so one\n * descriptor value is safe to share across builders and repeated builds.\n */\n\nimport { InvalidDescriptorError } from './errors';\n\nexport const DESCRIPTOR = Symbol.for('noego:testing:descriptor');\n\n/** A raw custom method wrapper: (original) => replacement. NOT auto-watched. */\nexport type RawMethodWrapper = (\n original: (...args: any[]) => any,\n) => (...args: any[]) => any;\n\nexport interface ReturnsDescriptor {\n readonly [DESCRIPTOR]: true;\n readonly kind: 'returns';\n readonly value: unknown;\n}\n\nexport interface ThrowsDescriptor {\n readonly [DESCRIPTOR]: true;\n readonly kind: 'throws';\n readonly error: unknown;\n}\n\nexport interface OriginalDescriptor {\n readonly [DESCRIPTOR]: true;\n readonly kind: 'original';\n}\n\nexport interface CallsDescriptor {\n readonly [DESCRIPTOR]: true;\n readonly kind: 'calls';\n readonly script: readonly BehaviorDescriptor[];\n}\n\nexport type BehaviorDescriptor =\n | ReturnsDescriptor\n | ThrowsDescriptor\n | OriginalDescriptor\n | CallsDescriptor;\n\nexport interface WatchDescriptor {\n readonly [DESCRIPTOR]: true;\n readonly kind: 'watch';\n readonly wrapper?: RawMethodWrapper;\n}\n\nexport interface ExpectationDescriptor {\n readonly [DESCRIPTOR]: true;\n readonly kind: 'expect';\n /** Exact required call count; 0 for never(). */\n readonly expected: number;\n /** Behavior used for allowed calls; undefined = original effective behavior. */\n readonly behavior?: BehaviorDescriptor;\n}\n\n/** Everything installable through .methods({...}). */\nexport type MethodDescriptor =\n | BehaviorDescriptor\n | WatchDescriptor\n | ExpectationDescriptor\n | RawMethodWrapper;\n\nexport function isDescriptor(value: unknown): value is Exclude<MethodDescriptor, RawMethodWrapper> {\n return typeof value === 'object' && value !== null && (value as any)[DESCRIPTOR] === true;\n}\n\nfunction frozen<T extends object>(value: T): T {\n return Object.freeze(value);\n}\n\nfunction assertBehavior(value: unknown, where: string): asserts value is BehaviorDescriptor {\n if (!isDescriptor(value) || !['returns', 'throws', 'original', 'calls'].includes((value as any).kind)) {\n throw new InvalidDescriptorError(\n `${where} requires a behavior descriptor (test.returns/throws/original/calls).`,\n );\n }\n}\n\nexport const test = {\n /** Return the supplied value when the method is called. Auto-watches. */\n returns(value: unknown): ReturnsDescriptor {\n return frozen({ [DESCRIPTOR]: true as const, kind: 'returns' as const, value });\n },\n\n /** Throw/reject with the supplied error. Auto-watches. */\n throws(error: unknown): ThrowsDescriptor {\n return frozen({ [DESCRIPTOR]: true as const, kind: 'throws' as const, error });\n },\n\n /** Invoke the original effective method. Auto-watches. */\n original(): OriginalDescriptor {\n return frozen({ [DESCRIPTOR]: true as const, kind: 'original' as const });\n },\n\n /**\n * Per-invocation behavior script: call 1 uses entry 1, and so on. A call\n * after exhaustion fails immediately. Unused entries do not fail verification.\n */\n calls(script: readonly BehaviorDescriptor[]): CallsDescriptor {\n if (!Array.isArray(script)) {\n throw new InvalidDescriptorError('test.calls() requires an array of behavior descriptors.');\n }\n script.forEach((entry, i) => assertBehavior(entry, `test.calls() entry #${i + 1}`));\n return frozen({ [DESCRIPTOR]: true as const, kind: 'calls' as const, script: Object.freeze([...script]) });\n },\n\n /**\n * Keep original behavior and record calls. With a raw wrapper argument, the\n * wrapper's behavior runs and is recorded.\n */\n watch(wrapper?: RawMethodWrapper): WatchDescriptor {\n if (wrapper !== undefined && typeof wrapper !== 'function') {\n throw new InvalidDescriptorError('test.watch() accepts only a raw wrapper function.');\n }\n return frozen({ [DESCRIPTOR]: true as const, kind: 'watch' as const, wrapper });\n },\n\n /** Require exactly one call; with no behavior, the original runs. */\n once(behavior?: BehaviorDescriptor): ExpectationDescriptor {\n if (behavior !== undefined) assertBehavior(behavior, 'test.once()');\n return frozen({ [DESCRIPTOR]: true as const, kind: 'expect' as const, expected: 1, behavior });\n },\n\n /** Require exactly `count` calls; with no behavior, the original runs. */\n times(count: number, behavior?: BehaviorDescriptor): ExpectationDescriptor {\n if (!Number.isInteger(count) || count < 0) {\n throw new InvalidDescriptorError(\n `test.times() requires a non-negative integer count, received ${String(count)}.`,\n );\n }\n if (behavior !== undefined) assertBehavior(behavior, 'test.times()');\n return frozen({ [DESCRIPTOR]: true as const, kind: 'expect' as const, expected: count, behavior });\n },\n\n /** Require zero calls; the first invocation fails and skips the original. */\n never(): ExpectationDescriptor {\n return frozen({ [DESCRIPTOR]: true as const, kind: 'expect' as const, expected: 0 });\n },\n\n /** Read the recorded history for a watched method in one environment. */\n inspect(environment: unknown, token: unknown, method: string): MethodInspection {\n const registry = (environment as any)?.[ENV_REGISTRY];\n if (!registry) {\n throw new InvalidDescriptorError(\n 'test.inspect() requires a built @noego/testing environment as its first argument.',\n );\n }\n return registry.inspect(token, method);\n },\n};\n\nObject.freeze(test);\n\n/** Symbol under which a built environment exposes its watch registry. */\nexport const ENV_REGISTRY = Symbol.for('noego:testing:env-registry');\n\nexport interface RecordedCall {\n /** 1-based invocation index in this environment. */\n readonly index: number;\n readonly args: readonly unknown[];\n /** Present once the call returned (resolved value for async methods). */\n readonly result?: unknown;\n /** Present once the call threw/rejected. */\n readonly error?: unknown;\n /** True while an async outcome is still pending. */\n readonly pending: boolean;\n readonly timestamp: number;\n}\n\nexport interface MethodInspection {\n readonly count: number;\n readonly calls: readonly RecordedCall[];\n}\n","/**\n * Environment-owned method behavior/observation runtime.\n *\n * All mutable state (histories, expectation counters, calls-script cursors)\n * lives here, created fresh at every build(). Descriptors stay immutable.\n */\n\nimport {\n type BehaviorDescriptor,\n type ExpectationDescriptor,\n type MethodDescriptor,\n type MethodInspection,\n type RawMethodWrapper,\n type RecordedCall,\n isDescriptor,\n} from './descriptors';\nimport {\n CallScriptExhaustedError,\n ExpectationOverflowError,\n MethodNotCallableError,\n NonObjectMethodTargetError,\n UnwatchedInspectionError,\n VerificationError,\n} from './errors';\n\nconst CONTEXT_WRAPPED = Symbol.for('ioc:context-wrapped');\nconst CONTEXT_OWNER = Symbol.for('ioc:context-owner');\n\nexport function tokenLabel(token: unknown): string {\n if (typeof token === 'function') return (token as { name?: string }).name || '[anonymous class]';\n if (typeof token === 'symbol') return String(token);\n return String(token);\n}\n\ninterface MutableCall {\n index: number;\n args: readonly unknown[];\n result?: unknown;\n error?: unknown;\n pending: boolean;\n timestamp: number;\n}\n\nexport class MethodState {\n readonly calls: MutableCall[] = [];\n /** Actual invocation count (includes the call currently executing). */\n actual = 0;\n /** Cursor into a test.calls() script. */\n scriptCursor = 0;\n\n constructor(\n readonly tokenName: string,\n readonly method: string,\n readonly descriptor: Exclude<MethodDescriptor, RawMethodWrapper>,\n ) {}\n\n get expectation(): ExpectationDescriptor | undefined {\n return this.descriptor.kind === 'expect' ? this.descriptor : undefined;\n }\n\n inspection(): MethodInspection {\n return {\n count: this.calls.length,\n calls: this.calls.map((c) => ({ ...c, args: [...c.args] }) as RecordedCall),\n };\n }\n}\n\n/** One method-config entry: how it was keyed, and its per-method descriptors. */\nexport interface MethodConfigEntry {\n /** Exact token (class/symbol/string) or a name string matched lazily. */\n readonly key: unknown;\n readonly byName: boolean;\n readonly methods: ReadonlyMap<string, MethodDescriptor>;\n}\n\nexport class WatchRegistry {\n /** entry-identity → method → state. Entries share states across instances. */\n private readonly states = new Map<unknown, Map<string, MethodState>>();\n private readonly nameIndex = new Map<string, unknown>();\n private readonly entries: MethodConfigEntry[] = [];\n\n addEntry(entry: MethodConfigEntry): void {\n this.entries.push(entry);\n const byMethod = new Map<string, MethodState>();\n for (const [method, descriptor] of entry.methods) {\n if (typeof descriptor === 'function') continue; // raw wrapper: unwatched, no state\n byMethod.set(method, new MethodState(this.entryLabel(entry), method, descriptor));\n }\n this.states.set(entry.key, byMethod);\n if (entry.byName) this.nameIndex.set(entry.key as string, entry.key);\n else if (typeof entry.key === 'function' && (entry.key as any).name) {\n this.nameIndex.set((entry.key as any).name, entry.key);\n }\n }\n\n private entryLabel(entry: MethodConfigEntry): string {\n return entry.byName ? String(entry.key) : tokenLabel(entry.key);\n }\n\n /** Entries applying to a resolving token (exact identity or name match). */\n matchEntries(token: unknown): MethodConfigEntry[] {\n const name = typeof token === 'function' ? (token as any).name : typeof token === 'string' ? token : undefined;\n return this.entries.filter(\n (entry) => entry.key === token || (entry.byName && name !== undefined && entry.key === name),\n );\n }\n\n state(entryKey: unknown, method: string): MethodState | undefined {\n return this.states.get(entryKey)?.get(method);\n }\n\n inspect(token: unknown, method: string): MethodInspection {\n const label = tokenLabel(token);\n const keys: unknown[] = [token];\n const name = typeof token === 'function' ? (token as any).name : typeof token === 'string' ? token : undefined;\n if (name !== undefined && this.nameIndex.has(name)) keys.push(this.nameIndex.get(name));\n for (const key of keys) {\n const state = this.states.get(key)?.get(method);\n if (state) return state.inspection();\n }\n throw new UnwatchedInspectionError(label, method);\n }\n\n /** Repeatable snapshot check of all exact expectations. */\n verify(): void {\n const failures: { token: string; method: string; expected: number; actual: number }[] = [];\n for (const byMethod of this.states.values()) {\n for (const state of byMethod.values()) {\n const expectation = state.expectation;\n if (!expectation) continue;\n if (state.actual !== expectation.expected) {\n failures.push({\n token: state.tokenName,\n method: state.method,\n expected: expectation.expected,\n actual: state.actual,\n });\n }\n }\n }\n if (failures.length) throw new VerificationError(failures);\n }\n}\n\nfunction isPromiseLike(value: unknown): value is Promise<unknown> {\n return !!value && typeof (value as any).then === 'function';\n}\n\nfunction recordOutcome(call: MutableCall, outcome: unknown, threw: boolean): unknown {\n if (!threw && isPromiseLike(outcome)) {\n call.pending = true;\n outcome.then(\n (value) => {\n call.result = value;\n call.pending = false;\n },\n (error) => {\n call.error = error;\n call.pending = false;\n },\n );\n return outcome;\n }\n call.pending = false;\n if (threw) call.error = outcome;\n else call.result = outcome;\n return outcome;\n}\n\n/**\n * Build the ioc instance decorator for one environment.\n *\n * Identity-stable (WeakMap), preserves sync/async call shape and `this`\n * binding, forwards the ioc context symbols by delegating to the underlying\n * (context-wrapped) target for everything unconfigured.\n */\nexport function createMethodDecorator(registry: WatchRegistry): (instance: any, token: any) => any {\n const wrappers = new WeakMap<object, any>();\n\n return function decorate(instance: any, token: any): any {\n const entries = registry.matchEntries(token);\n if (entries.length === 0) return instance;\n\n if (instance === null || (typeof instance !== 'object' && typeof instance !== 'function')) {\n throw new NonObjectMethodTargetError(tokenLabel(token));\n }\n\n if (wrappers.has(instance)) return wrappers.get(instance);\n\n // Effective per-method config: entries merge in write order, LWW per method\n const effective = new Map<string, { entryKey: unknown; descriptor: MethodDescriptor }>();\n for (const entry of entries) {\n for (const [method, descriptor] of entry.methods) {\n effective.set(method, { entryKey: entry.key, descriptor });\n }\n }\n\n // Validate configured methods exist and are callable\n for (const method of effective.keys()) {\n if (typeof instance[method] !== 'function') {\n throw new MethodNotCallableError(tokenLabel(token), method);\n }\n }\n\n const methodCache = new Map<string, (...args: any[]) => any>();\n\n const proxy = new Proxy(instance, {\n get(target, prop, receiver) {\n if (typeof prop === 'string' && effective.has(prop)) {\n let wrapped = methodCache.get(prop);\n if (!wrapped) {\n const { entryKey, descriptor } = effective.get(prop)!;\n wrapped = buildMethodWrapper(target, prop, descriptor, registry.state(entryKey, prop));\n methodCache.set(prop, wrapped);\n }\n return wrapped;\n }\n // Everything else (including ioc context symbols) delegates to the\n // underlying — usually context-wrapped — target.\n if (prop === CONTEXT_WRAPPED || prop === CONTEXT_OWNER) return (target as any)[prop];\n return Reflect.get(target, prop, receiver);\n },\n });\n\n wrappers.set(instance, proxy);\n return proxy;\n };\n}\n\nfunction buildMethodWrapper(\n target: any,\n method: string,\n descriptor: MethodDescriptor,\n state: MethodState | undefined,\n): (...args: any[]) => any {\n // The original effective method, read through the underlying target so the\n // ioc context proxy still owns context entry for real invocations.\n const callOriginal = (self: any, args: any[]) => {\n const fn = target[method];\n return fn.apply(self === undefined ? target : self, args);\n };\n\n // Raw custom wrapper: installed as-is, NOT watched, no recording.\n if (typeof descriptor === 'function') {\n const replacement = descriptor((...args: any[]) => callOriginal(target, args));\n return function (this: any, ...args: any[]) {\n return replacement.apply(this, args);\n };\n }\n\n if (!isDescriptor(descriptor)) {\n // Should be unreachable: builder validates descriptors on write.\n throw new TypeError(`Invalid method descriptor for ${method}`);\n }\n\n return function (this: any, ...args: any[]) {\n if (!state) throw new TypeError(`Missing method state for ${method}`);\n\n const expectation = state.expectation;\n const attempted = state.actual + 1;\n\n // Overflow fails immediately and never runs behavior/original\n if (expectation && attempted > expectation.expected) {\n state.actual = attempted;\n throw new ExpectationOverflowError(state.tokenName, method, expectation.expected, attempted);\n }\n\n state.actual = attempted;\n const call: MutableCall = {\n index: attempted,\n args: [...args],\n pending: false,\n timestamp: Date.now(),\n };\n state.calls.push(call);\n\n // Effective behavior for this invocation\n let behavior: BehaviorDescriptor | 'original-effective' | RawMethodWrapper;\n const base =\n descriptor.kind === 'expect'\n ? (descriptor.behavior ?? 'original-effective')\n : descriptor.kind === 'watch'\n ? (descriptor.wrapper ?? 'original-effective')\n : descriptor;\n\n if (base !== 'original-effective' && typeof base !== 'function' && base !== undefined && base.kind === 'calls') {\n if (state.scriptCursor >= base.script.length) {\n throw new CallScriptExhaustedError(state.tokenName, method, base.script.length, attempted);\n }\n behavior = base.script[state.scriptCursor]!;\n state.scriptCursor += 1;\n } else {\n behavior = base;\n }\n\n try {\n let outcome: unknown;\n if (behavior === 'original-effective') {\n outcome = callOriginal(this, args);\n } else if (typeof behavior === 'function') {\n // test.watch(rawWrapper): wrapper behavior runs and is recorded\n outcome = behavior((...inner: any[]) => callOriginal(this, inner)).apply(this, args);\n } else if (behavior.kind === 'returns') {\n outcome = behavior.value;\n } else if (behavior.kind === 'throws') {\n throw behavior.error;\n } else if (behavior.kind === 'original') {\n outcome = callOriginal(this, args);\n } else {\n throw new TypeError(`Nested test.calls() scripts are not supported (${method}).`);\n }\n return recordOutcome(call, outcome, false);\n } catch (error) {\n recordOutcome(call, error, true);\n throw error;\n }\n };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/builder.ts","../src/errors.ts","../src/descriptors.ts","../src/method_state.ts"],"sourcesContent":["/**\n * @noego/testing — the canonical shared owner of real-IoC test composition\n * (`testIoc`) and the lowercase `test.*` method behavior/watch/expectation\n * language, built on @noego/ioc's production seams.\n *\n * The former generic runtime-mirror doubles (ManualClock, ScriptedFetchClient,\n * memory stores, recording sinks, contract suites, LeakDetector, …) are\n * retired; pin 0.1.x if you still need them, and see the NoEgo runtime/testing\n * deprecation plan for per-export dispositions.\n */\n\nexport { testIoc, TestIocBuilder } from './builder';\nexport type { TestEnvironment, ConfigMap, MethodsConfig, UseInput } from './builder';\n\nexport { test, ENV_REGISTRY } from './descriptors';\nexport type {\n BehaviorDescriptor,\n CallsDescriptor,\n ExpectationDescriptor,\n MethodDescriptor,\n MethodInspection,\n OriginalDescriptor,\n RawMethodWrapper,\n RecordedCall,\n ReturnsDescriptor,\n ThrowsDescriptor,\n WatchDescriptor,\n} from './descriptors';\n\nexport {\n TestingError,\n MissingIocSeamError,\n UnwatchedInspectionError,\n CallScriptExhaustedError,\n ExpectationOverflowError,\n VerificationError,\n MethodNotCallableError,\n NonObjectMethodTargetError,\n UnknownTokenKeyError,\n AmbiguousNameKeyError,\n TokenIdentitySplitError,\n InvalidDescriptorError,\n} from './errors';\n","/**\n * `testIoc` — the canonical shared real-IoC test composition builder.\n *\n * Persistent immutable: every fluent call returns a new derived builder\n * sharing the ordered write log structurally. Non-conflicting writes are\n * order-insensitive; the last write to the same effective identity wins on\n * that derived branch. `.build()` is non-consuming and creates fresh runtime,\n * watch, and expectation state (spec 15, PBR-01..15).\n */\n\nimport {\n createContainer,\n flattenModule,\n LoadAs,\n SCOPED_CONTAINER,\n type ApplicationModule,\n type IContainer,\n} from '@noego/ioc';\n\nimport { ENV_REGISTRY, isDescriptor, type MethodDescriptor } from './descriptors';\nimport { createMethodDecorator, WatchRegistry, tokenLabel } from './method_state';\nimport { InvalidDescriptorError, MissingIocSeamError, UnknownTokenKeyError, AmbiguousNameKeyError,\n} from './errors';\n\nconst COMPONENT_OPTIONS_KEY = Symbol.for('ioc:component:options');\n\ntype Token = unknown;\ntype ClassLike = new (...args: any[]) => any;\n\n/** Config maps accept plain objects (string keys) or Maps (exact tokens). */\n/**\n * Composition input (spec 04 §2). The CANONICAL entry form is an array of\n * tuples — token-first, exact identity:\n *\n * .classes([[ProjectRepository, MemoryProjectRepository]])\n *\n * A `ReadonlyMap<Token, V>` is equivalent (token-keyed). A name-keyed\n * `Record<string, V>` remains accepted as COMPATIBILITY input: string keys\n * resolve against known token display names, fail on unknown names, and\n * fail on ambiguous names (two tokens sharing one display name never\n * collapse into one entry — tokens are identities, names are labels).\n */\nexport type ConfigMap<V> =\n | ReadonlyArray<readonly [Token, V]>\n | ReadonlyMap<Token, V>\n | Record<string, V>;\n\nexport type MethodsConfig = ConfigMap<Record<string, MethodDescriptor> | ReadonlyMap<string, MethodDescriptor>>;\n\nexport type UseInput = ApplicationModule | TestIocBuilder;\n\ntype Write =\n | { op: 'use'; module: ApplicationModule }\n | { op: 'classes'; key: Token; byName: boolean; implementation: ClassLike }\n | { op: 'functions'; key: Token; byName: boolean; factory: (...args: any[]) => any }\n | { op: 'values'; key: Token; byName: boolean; value: unknown }\n | { op: 'methods'; key: Token; byName: boolean; methods: ReadonlyMap<string, MethodDescriptor> };\n\nexport interface TestEnvironment {\n readonly root: IContainer;\n get<T>(token: unknown, params?: any[]): Promise<T> | T;\n instance<T>(cls: new (...args: any[]) => T, params?: any[]): Promise<T> | T;\n extend(): IContainer;\n verify(): Promise<void>;\n dispose(): Promise<void>;\n readonly [ENV_REGISTRY]: WatchRegistry;\n}\n\nfunction* configEntries<V>(config: ConfigMap<V>): Iterable<[Token, boolean, V]> {\n if (Array.isArray(config)) {\n // Canonical tuple form: exact token identity, never name resolution.\n for (const entry of config as ReadonlyArray<readonly [Token, V]>) {\n if (!Array.isArray(entry) || entry.length !== 2) {\n throw new InvalidDescriptorError(\n 'canonical composition entries are [token, value] tuples, e.g. .classes([[Token, Impl]])',\n );\n }\n yield [entry[0], false, entry[1]];\n }\n } else if (config instanceof Map) {\n for (const [key, value] of config) yield [key, false, value];\n } else {\n for (const key of [...Object.getOwnPropertyNames(config), ...Object.getOwnPropertySymbols(config)]) {\n yield [key, typeof key === 'string', (config as any)[key]];\n }\n }\n}\n\nfunction componentScope(cls: any): LoadAs | undefined {\n const options =\n typeof Reflect !== 'undefined' && (Reflect as any).getMetadata\n ? (Reflect as any).getMetadata(COMPONENT_OPTIONS_KEY, cls)\n : undefined;\n return options?.scope;\n}\n\nfunction lifetimeToLoadAs(lifetime: string): LoadAs {\n if (lifetime === 'singleton') return LoadAs.Singleton;\n if (lifetime === 'scoped') return LoadAs.Scoped;\n return LoadAs.Transient;\n}\n\nexport class TestIocBuilder {\n private constructor(private readonly log: readonly Write[]) {\n Object.freeze(this);\n }\n\n /** @internal */\n static create(inputs: readonly UseInput[]): TestIocBuilder {\n return new TestIocBuilder([]).useAll(inputs);\n }\n\n /** @internal — read by .use(builderPreset) */\n get writes(): readonly Write[] {\n return this.log;\n }\n\n private derive(writes: Write[]): TestIocBuilder {\n return new TestIocBuilder([...this.log, ...writes]);\n }\n\n private useAll(inputs: readonly UseInput[]): TestIocBuilder {\n let builder: TestIocBuilder = this;\n for (const input of inputs) builder = builder.use(input);\n return builder;\n }\n\n /** Apply a reusable composition preset: an ApplicationModule or a builder. */\n use(preset: UseInput): TestIocBuilder {\n if (preset instanceof TestIocBuilder) {\n return this.derive([...preset.writes]);\n }\n return this.derive([{ op: 'use', module: preset }]);\n }\n\n /** Replace the implementation for IoC class tokens in the built environment. */\n classes(config: ConfigMap<ClassLike>): TestIocBuilder {\n const writes: Write[] = [];\n for (const [key, byName, implementation] of configEntries(config)) {\n if (typeof implementation !== 'function' || !implementation.prototype) {\n throw new InvalidDescriptorError(\n `.classes() value for \"${tokenLabel(key)}\" must be a class constructor.`,\n );\n }\n writes.push({ op: 'classes', key, byName, implementation });\n }\n return this.derive(writes);\n }\n\n /** Replace IoC factory/provider registrations. */\n functions(config: ConfigMap<(...args: any[]) => any>): TestIocBuilder {\n const writes: Write[] = [];\n for (const [key, byName, factory] of configEntries(config)) {\n if (typeof factory !== 'function') {\n throw new InvalidDescriptorError(`.functions() value for \"${tokenLabel(key)}\" must be a function.`);\n }\n writes.push({ op: 'functions', key, byName, factory });\n }\n return this.derive(writes);\n }\n\n /** Provide/replace IoC value registrations. */\n values(config: ConfigMap<unknown>): TestIocBuilder {\n const writes: Write[] = [];\n for (const [key, byName, value] of configEntries(config)) {\n writes.push({ op: 'values', key, byName, value });\n }\n return this.derive(writes);\n }\n\n /** Install method behavior/observation descriptors on IoC-managed instances. */\n methods(config: MethodsConfig): TestIocBuilder {\n const writes: Write[] = [];\n for (const [key, byName, methodsInput] of configEntries(config)) {\n const methods = new Map<string, MethodDescriptor>();\n const entries =\n methodsInput instanceof Map\n ? methodsInput.entries()\n : Object.entries(methodsInput as Record<string, MethodDescriptor>);\n for (const [name, descriptor] of entries) {\n if (typeof descriptor !== 'function' && !isDescriptor(descriptor)) {\n throw new InvalidDescriptorError(\n `.methods() entry ${tokenLabel(key)}.${name} must be a test.* descriptor or a raw wrapper function.`,\n );\n }\n methods.set(name, descriptor);\n }\n writes.push({ op: 'methods', key, byName, methods });\n }\n return this.derive(writes);\n }\n\n /** Materialize a fresh, isolated real-IoC environment. Non-consuming. */\n async build(): Promise<TestEnvironment> {\n const root = createContainer();\n if (typeof (root as any).setInstanceDecorator !== 'function') {\n throw new MissingIocSeamError();\n }\n\n // ---- Materialize the ordered log: last write wins per effective identity\n // Known tokens (for resolving string keys of classes/functions/values)\n const knownByName = new Map<string, Token>();\n /** Display names claimed by more than one distinct token (spec 04 §5/§17). */\n const ambiguousNames = new Set<string>();\n const knownLifetimes = new Map<Token, LoadAs>();\n const note = (token: Token, loadAs: LoadAs | undefined) => {\n const name =\n typeof token === 'function' && (token as any).name\n ? (token as any).name as string\n : typeof token === 'string'\n ? token\n : null;\n if (name !== null) {\n const existing = knownByName.get(name);\n if (existing !== undefined && existing !== token) ambiguousNames.add(name);\n knownByName.set(name, token);\n }\n if (loadAs !== undefined) knownLifetimes.set(token, loadAs);\n };\n\n type Effective =\n | { space: 'class'; token: Token; implementation: ClassLike; loadAs?: LoadAs }\n | { space: 'factory'; token: Token; factory: (...args: any[]) => any; loadAs?: LoadAs; deps?: Token[] }\n | { space: 'value'; token: Token; value: unknown };\n\n // ordered map: identity → latest effective write (Map preserves first-write\n // position which is fine — later writes replace content, LWW)\n const effective = new Map<Token, Effective>();\n const methodWrites: { key: Token; byName: boolean; methods: Map<string, MethodDescriptor> }[] = [];\n\n const resolveKey = (key: Token, byName: boolean, space: string): Token => {\n if (!byName) return key;\n if (ambiguousNames.has(key as string)) {\n throw new AmbiguousNameKeyError(key as string, space);\n }\n const known = knownByName.get(key as string);\n if (known !== undefined) return known;\n if (space === 'values' || space === 'functions') return key; // string tokens are first-class\n throw new UnknownTokenKeyError(key as string, space, [...knownByName.keys()]);\n };\n\n for (const write of this.log) {\n switch (write.op) {\n case 'use': {\n for (const reg of flattenModule(write.module)) {\n const loadAs = lifetimeToLoadAs(reg.lifetime);\n note(reg.token, loadAs);\n if (reg.kind === 'class') {\n note(reg.implementation as Token, loadAs);\n effective.set(reg.token, {\n space: 'class',\n token: reg.token,\n implementation: reg.implementation as ClassLike,\n loadAs,\n });\n knownLifetimes.set(reg.token, loadAs);\n knownLifetimes.set(reg.implementation as Token, loadAs);\n } else if (reg.kind === 'factory') {\n effective.set(reg.token, {\n space: 'factory',\n token: reg.token,\n factory: reg.implementation as (...args: any[]) => any,\n loadAs,\n deps: [...reg.dependencies],\n });\n } else {\n effective.set(reg.token, { space: 'value', token: reg.token, value: reg.implementation });\n knownLifetimes.set(reg.token, LoadAs.Singleton);\n }\n }\n break;\n }\n case 'classes': {\n const token = resolveKey(write.key, write.byName, 'classes');\n note(write.implementation, undefined);\n if (!write.byName) note(token, undefined);\n effective.set(token, {\n space: 'class',\n token,\n implementation: write.implementation,\n // Lifetime priority: the replacement's own @Component scope, then\n // the lifetime the composition already knows for the token, then\n // the TOKEN class's declared @Component scope — a plain stub\n // class replacing a Singleton-scoped production service must not\n // silently degrade to Transient (captive-lifetime validation\n // would reject the production dependents).\n loadAs:\n componentScope(write.implementation)\n ?? knownLifetimes.get(token)\n ?? componentScope(token),\n });\n break;\n }\n case 'functions': {\n const token = resolveKey(write.key, write.byName, 'functions');\n const prior = effective.get(token);\n effective.set(token, {\n space: 'factory',\n token,\n factory: write.factory,\n // preserve configured lifetime unless the scenario overrides it\n loadAs: prior && prior.space === 'factory' ? prior.loadAs : knownLifetimes.get(token),\n deps: prior && prior.space === 'factory' ? prior.deps : undefined,\n });\n note(token, undefined);\n break;\n }\n case 'values': {\n const token = resolveKey(write.key, write.byName, 'values');\n effective.set(token, { space: 'value', token, value: write.value });\n note(token, LoadAs.Singleton);\n break;\n }\n case 'methods': {\n if (write.byName && ambiguousNames.has(write.key as string)) {\n throw new AmbiguousNameKeyError(write.key as string, 'methods');\n }\n const token = write.byName && knownByName.has(write.key as string)\n ? knownByName.get(write.key as string)!\n : write.key;\n methodWrites.push({\n key: token,\n byName: write.byName && !knownByName.has(write.key as string),\n methods: new Map(write.methods),\n });\n break;\n }\n }\n }\n\n // ---- Apply effective registrations to the fresh root\n for (const entry of effective.values()) {\n if (entry.space === 'class') {\n this.registerClassBinding(root, entry.token, entry.implementation, entry.loadAs);\n } else if (entry.space === 'factory') {\n root.registerFunction(entry.token, entry.factory, {\n loadAs: entry.loadAs,\n param: entry.deps,\n } as any);\n } else {\n const value = entry.value;\n root.registerFunction(entry.token, () => value, { loadAs: LoadAs.Singleton } as any);\n }\n }\n\n // ---- Fresh watch/expectation state + the ioc decoration seam\n const registry = new WatchRegistry();\n // merge method writes: LWW per (entry key, method), deep-merge per token\n const mergedMethods = new Map<Token, { byName: boolean; methods: Map<string, MethodDescriptor> }>();\n for (const write of methodWrites) {\n const existing = mergedMethods.get(write.key);\n if (existing) {\n for (const [name, descriptor] of write.methods) existing.methods.set(name, descriptor);\n } else {\n mergedMethods.set(write.key, { byName: write.byName, methods: new Map(write.methods) });\n }\n }\n for (const [key, { byName, methods }] of mergedMethods) {\n registry.addEntry({ key, byName, methods });\n }\n (root as any).setInstanceDecorator(createMethodDecorator(registry));\n\n const env: TestEnvironment = {\n root: root as IContainer,\n get: (token, params) => root.get(token as any, params),\n instance: (cls, params) => root.instance(cls, params),\n extend: () => root.extend() as IContainer,\n verify: async () => registry.verify(),\n dispose: () => root.dispose(),\n [ENV_REGISTRY]: registry,\n };\n return env;\n }\n\n /**\n * Register a class binding. When token === implementation this is a plain\n * class registration. Otherwise an alias factory resolves the implementation\n * through real IoC, mirroring the implementation's effective lifetime so\n * lifetime validation (captive-lifetime checks) stays honest.\n */\n private registerClassBinding(\n root: IContainer,\n token: Token,\n implementation: ClassLike,\n configuredLoadAs?: LoadAs,\n ): void {\n const loadAs = configuredLoadAs ?? componentScope(implementation) ?? LoadAs.Transient;\n root.registerClass(implementation as any, configuredLoadAs !== undefined ? ({ loadAs } as any) : undefined);\n if (token === implementation) return;\n\n if (loadAs === LoadAs.Singleton) {\n root.registerFunction(token, () => root.get(implementation as any), {\n loadAs: LoadAs.Singleton,\n } as any);\n } else {\n root.registerFunction(token, (scope: IContainer) => scope.get(implementation as any), {\n loadAs,\n param: [SCOPED_CONTAINER],\n } as any);\n }\n }\n}\n\n/** Create a persistent immutable real-IoC test composition builder. */\nexport function testIoc(...inputs: UseInput[]): TestIocBuilder {\n return TestIocBuilder.create(inputs);\n}\n","/**\n * Diagnostics are first-class: every error names the real token/method and\n * what was expected vs what happened, never only internal wrapper machinery.\n */\n\nexport class TestingError extends Error {\n constructor(message: string) {\n super(message);\n this.name = new.target.name;\n }\n}\n\n/** Thrown at build() when the installed @noego/ioc lacks the decoration seam. */\nexport class MissingIocSeamError extends TestingError {\n constructor() {\n super(\n '@noego/testing requires an @noego/ioc version that provides ' +\n 'Container.setInstanceDecorator (>= 0.5.x with the instance-decoration seam). ' +\n 'Upgrade @noego/ioc.',\n );\n }\n}\n\n/** test.inspect() on a method that is not watched in this environment. */\nexport class UnwatchedInspectionError extends TestingError {\n constructor(token: string, method: string) {\n super(\n `Method \"${method}\" on ${token} is not watched in this environment. ` +\n 'Only watched methods are inspectable — install test.watch() or any ' +\n 'test.* behavior/expectation descriptor for it.',\n );\n }\n}\n\n/** A call arrived after a test.calls([...]) script was fully consumed. */\nexport class CallScriptExhaustedError extends TestingError {\n constructor(token: string, method: string, scriptLength: number, callIndex: number) {\n super(\n `Call #${callIndex} to ${token}.${method} exceeds its test.calls() script ` +\n `of ${scriptLength} ${scriptLength === 1 ? 'entry' : 'entries'}.`,\n );\n }\n}\n\n/** An exact expectation (once/times/never) was exceeded at call time. */\nexport class ExpectationOverflowError extends TestingError {\n constructor(token: string, method: string, expected: number, attempted: number) {\n super(\n expected === 0\n ? `${token}.${method} was expected never to be called, but it was invoked.`\n : `${token}.${method} was expected exactly ${expected} ` +\n `${expected === 1 ? 'call' : 'calls'}, but call #${attempted} arrived.`,\n );\n }\n}\n\n/** Aggregated under-count failures reported by env.verify(). */\nexport class VerificationError extends TestingError {\n constructor(failures: readonly { token: string; method: string; expected: number; actual: number }[]) {\n super(\n 'Exact method expectations were not satisfied:\\n' +\n failures\n .map(\n (f) =>\n ` - ${f.token}.${f.method}: expected exactly ${f.expected} ` +\n `${f.expected === 1 ? 'call' : 'calls'}, observed ${f.actual}`,\n )\n .join('\\n'),\n );\n }\n}\n\n/** .methods configured for a token whose resolved value has no such callable method. */\nexport class MethodNotCallableError extends TestingError {\n constructor(token: string, method: string) {\n super(\n `Cannot install a test.* descriptor on ${token}.${method}: the resolved ` +\n 'instance has no callable method with that name.',\n );\n }\n}\n\n/** .methods configured for a token that resolved to a non-object value. */\nexport class NonObjectMethodTargetError extends TestingError {\n constructor(token: string) {\n super(\n `.methods() is configured for ${token}, but that token resolved to a ` +\n 'non-object value. Method descriptors apply only to IoC-managed instances.',\n );\n }\n}\n\n/** A string configuration key could not be resolved to a known IoC token. */\nexport class UnknownTokenKeyError extends TestingError {\n constructor(key: string, space: string, known: readonly string[]) {\n super(\n `Unknown ${space} key \"${key}\" — it does not match any token known to this ` +\n 'builder. Pass the class/token itself via a Map, or include the ' +\n 'registration through .use(...). Known tokens: ' +\n (known.length ? known.join(', ') : '(none)'),\n );\n }\n}\n\n/** Invalid descriptor construction (e.g. test.times(-1)). */\nexport class InvalidDescriptorError extends TestingError {}\n\n/**\n * A name-keyed compatibility entry referred to a display name claimed by\n * two or more distinct tokens. Tokens are identities; names are labels —\n * pass the exact token in canonical tuple form instead (spec 04 §5).\n */\nexport class AmbiguousNameKeyError extends TestingError {\n constructor(name: string, space: string) {\n super(\n `.${space}() key \"${name}\" is ambiguous: multiple distinct tokens share that display name. ` +\n `Use the canonical tuple form with the exact token: .${space}([[TheToken, ...]]).`,\n );\n this.name = 'AmbiguousNameKeyError';\n }\n}\n\n\n/**\n * A .methods() class token never matched any constructed instance while a\n * DIFFERENT class with the same display name did construct — the configured\n * behavior silently did not apply. Usual cause: two module registries loaded\n * the same source file (e.g. a vitest test file's import vs a framework\n * harness's native import of the production graph); fix by routing the\n * harness's imports through the caller's registry (testApp:\n * `.importer((p) => import(p))`) or by passing the token the graph actually\n * uses.\n */\nexport class TokenIdentitySplitError extends TestingError {\n constructor(names: readonly string[]) {\n super(\n `token identity split: .methods() entr${names.length === 1 ? 'y' : 'ies'} for ` +\n `${names.map((name) => `\"${name}\"`).join(', ')} never matched a constructed instance, ` +\n `but a DIFFERENT class with the same name was constructed. Two module registries ` +\n `have loaded the same class file (test-file import vs harness graph import) — the ` +\n `configured behavior did not apply. Route the harness's imports through your ` +\n `registry (e.g. testApp .importer((p) => import(p))) or pass the exact token the graph uses.`,\n );\n this.name = 'TokenIdentitySplitError';\n }\n}\n","/**\n * Lowercase `test.*` descriptors — immutable frozen values.\n *\n * Descriptors carry NO mutable state (no cursors, no counters, no histories);\n * all mutable invocation state lives in the built environment, so one\n * descriptor value is safe to share across builders and repeated builds.\n */\n\nimport { InvalidDescriptorError } from './errors';\n\nexport const DESCRIPTOR = Symbol.for('noego:testing:descriptor');\n\n/** A raw custom method wrapper: (original) => replacement. NOT auto-watched. */\nexport type RawMethodWrapper = (\n original: (...args: any[]) => any,\n) => (...args: any[]) => any;\n\nexport interface ReturnsDescriptor {\n readonly [DESCRIPTOR]: true;\n readonly kind: 'returns';\n readonly value: unknown;\n}\n\nexport interface ThrowsDescriptor {\n readonly [DESCRIPTOR]: true;\n readonly kind: 'throws';\n readonly error: unknown;\n}\n\nexport interface OriginalDescriptor {\n readonly [DESCRIPTOR]: true;\n readonly kind: 'original';\n}\n\nexport interface CallsDescriptor {\n readonly [DESCRIPTOR]: true;\n readonly kind: 'calls';\n readonly script: readonly BehaviorDescriptor[];\n}\n\nexport type BehaviorDescriptor =\n | ReturnsDescriptor\n | ThrowsDescriptor\n | OriginalDescriptor\n | CallsDescriptor;\n\nexport interface WatchDescriptor {\n readonly [DESCRIPTOR]: true;\n readonly kind: 'watch';\n readonly wrapper?: RawMethodWrapper;\n}\n\nexport interface ExpectationDescriptor {\n readonly [DESCRIPTOR]: true;\n readonly kind: 'expect';\n /** Exact required call count; 0 for never(). */\n readonly expected: number;\n /** Behavior used for allowed calls; undefined = original effective behavior. */\n readonly behavior?: BehaviorDescriptor;\n}\n\n/** Everything installable through .methods({...}). */\nexport type MethodDescriptor =\n | BehaviorDescriptor\n | WatchDescriptor\n | ExpectationDescriptor\n | RawMethodWrapper;\n\nexport function isDescriptor(value: unknown): value is Exclude<MethodDescriptor, RawMethodWrapper> {\n return typeof value === 'object' && value !== null && (value as any)[DESCRIPTOR] === true;\n}\n\nfunction frozen<T extends object>(value: T): T {\n return Object.freeze(value);\n}\n\nfunction assertBehavior(value: unknown, where: string): asserts value is BehaviorDescriptor {\n if (!isDescriptor(value) || !['returns', 'throws', 'original', 'calls'].includes((value as any).kind)) {\n throw new InvalidDescriptorError(\n `${where} requires a behavior descriptor (test.returns/throws/original/calls).`,\n );\n }\n}\n\nexport const test = {\n /** Return the supplied value when the method is called. Auto-watches. */\n returns(value: unknown): ReturnsDescriptor {\n return frozen({ [DESCRIPTOR]: true as const, kind: 'returns' as const, value });\n },\n\n /** Throw/reject with the supplied error. Auto-watches. */\n throws(error: unknown): ThrowsDescriptor {\n return frozen({ [DESCRIPTOR]: true as const, kind: 'throws' as const, error });\n },\n\n /** Invoke the original effective method. Auto-watches. */\n original(): OriginalDescriptor {\n return frozen({ [DESCRIPTOR]: true as const, kind: 'original' as const });\n },\n\n /**\n * Per-invocation behavior script: call 1 uses entry 1, and so on. A call\n * after exhaustion fails immediately. Unused entries do not fail verification.\n */\n calls(script: readonly BehaviorDescriptor[]): CallsDescriptor {\n if (!Array.isArray(script)) {\n throw new InvalidDescriptorError('test.calls() requires an array of behavior descriptors.');\n }\n script.forEach((entry, i) => assertBehavior(entry, `test.calls() entry #${i + 1}`));\n return frozen({ [DESCRIPTOR]: true as const, kind: 'calls' as const, script: Object.freeze([...script]) });\n },\n\n /**\n * Keep original behavior and record calls. With a raw wrapper argument, the\n * wrapper's behavior runs and is recorded.\n */\n watch(wrapper?: RawMethodWrapper): WatchDescriptor {\n if (wrapper !== undefined && typeof wrapper !== 'function') {\n throw new InvalidDescriptorError('test.watch() accepts only a raw wrapper function.');\n }\n return frozen({ [DESCRIPTOR]: true as const, kind: 'watch' as const, wrapper });\n },\n\n /** Require exactly one call; with no behavior, the original runs. */\n once(behavior?: BehaviorDescriptor): ExpectationDescriptor {\n if (behavior !== undefined) assertBehavior(behavior, 'test.once()');\n return frozen({ [DESCRIPTOR]: true as const, kind: 'expect' as const, expected: 1, behavior });\n },\n\n /** Require exactly `count` calls; with no behavior, the original runs. */\n times(count: number, behavior?: BehaviorDescriptor): ExpectationDescriptor {\n if (!Number.isInteger(count) || count < 0) {\n throw new InvalidDescriptorError(\n `test.times() requires a non-negative integer count, received ${String(count)}.`,\n );\n }\n if (behavior !== undefined) assertBehavior(behavior, 'test.times()');\n return frozen({ [DESCRIPTOR]: true as const, kind: 'expect' as const, expected: count, behavior });\n },\n\n /** Require zero calls; the first invocation fails and skips the original. */\n never(): ExpectationDescriptor {\n return frozen({ [DESCRIPTOR]: true as const, kind: 'expect' as const, expected: 0 });\n },\n\n /** Read the recorded history for a watched method in one environment. */\n inspect(environment: unknown, token: unknown, method: string): MethodInspection {\n const registry = (environment as any)?.[ENV_REGISTRY];\n if (!registry) {\n throw new InvalidDescriptorError(\n 'test.inspect() requires a built @noego/testing environment as its first argument.',\n );\n }\n return registry.inspect(token, method);\n },\n};\n\nObject.freeze(test);\n\n/** Symbol under which a built environment exposes its watch registry. */\nexport const ENV_REGISTRY = Symbol.for('noego:testing:env-registry');\n\nexport interface RecordedCall {\n /** 1-based invocation index in this environment. */\n readonly index: number;\n readonly args: readonly unknown[];\n /** Present once the call returned (resolved value for async methods). */\n readonly result?: unknown;\n /** Present once the call threw/rejected. */\n readonly error?: unknown;\n /** True while an async outcome is still pending. */\n readonly pending: boolean;\n readonly timestamp: number;\n}\n\nexport interface MethodInspection {\n readonly count: number;\n readonly calls: readonly RecordedCall[];\n}\n","/**\n * Environment-owned method behavior/observation runtime.\n *\n * All mutable state (histories, expectation counters, calls-script cursors)\n * lives here, created fresh at every build(). Descriptors stay immutable.\n */\n\nimport {\n type BehaviorDescriptor,\n type ExpectationDescriptor,\n type MethodDescriptor,\n type MethodInspection,\n type RawMethodWrapper,\n type RecordedCall,\n isDescriptor,\n} from './descriptors';\nimport {\n CallScriptExhaustedError,\n TokenIdentitySplitError,\n ExpectationOverflowError,\n MethodNotCallableError,\n NonObjectMethodTargetError,\n UnwatchedInspectionError,\n VerificationError,\n} from './errors';\n\nconst CONTEXT_WRAPPED = Symbol.for('ioc:context-wrapped');\nconst CONTEXT_OWNER = Symbol.for('ioc:context-owner');\n\nexport function tokenLabel(token: unknown): string {\n if (typeof token === 'function') return (token as { name?: string }).name || '[anonymous class]';\n if (typeof token === 'symbol') return String(token);\n return String(token);\n}\n\ninterface MutableCall {\n index: number;\n args: readonly unknown[];\n result?: unknown;\n error?: unknown;\n pending: boolean;\n timestamp: number;\n}\n\nexport class MethodState {\n readonly calls: MutableCall[] = [];\n /** Actual invocation count (includes the call currently executing). */\n actual = 0;\n /** Cursor into a test.calls() script. */\n scriptCursor = 0;\n\n constructor(\n readonly tokenName: string,\n readonly method: string,\n readonly descriptor: Exclude<MethodDescriptor, RawMethodWrapper>,\n ) {}\n\n get expectation(): ExpectationDescriptor | undefined {\n return this.descriptor.kind === 'expect' ? this.descriptor : undefined;\n }\n\n inspection(): MethodInspection {\n return {\n count: this.calls.length,\n calls: this.calls.map((c) => ({ ...c, args: [...c.args] }) as RecordedCall),\n };\n }\n}\n\n/** One method-config entry: how it was keyed, and its per-method descriptors. */\nexport interface MethodConfigEntry {\n /** Exact token (class/symbol/string) or a name string matched lazily. */\n readonly key: unknown;\n readonly byName: boolean;\n readonly methods: ReadonlyMap<string, MethodDescriptor>;\n}\n\nexport class WatchRegistry {\n /** entry-identity → method → state. Entries share states across instances. */\n private readonly states = new Map<unknown, Map<string, MethodState>>();\n private readonly nameIndex = new Map<string, unknown>();\n private readonly entries: MethodConfigEntry[] = [];\n /** Class-token entries that identity-matched at least one construction. */\n private readonly matchedEntryKeys = new Set<unknown>();\n /** Constructed class tokens that matched NO entry, by display name. */\n private readonly unmatchedConstructedByName = new Map<string, unknown>();\n\n addEntry(entry: MethodConfigEntry): void {\n this.entries.push(entry);\n const byMethod = new Map<string, MethodState>();\n for (const [method, descriptor] of entry.methods) {\n if (typeof descriptor === 'function') continue; // raw wrapper: unwatched, no state\n byMethod.set(method, new MethodState(this.entryLabel(entry), method, descriptor));\n }\n this.states.set(entry.key, byMethod);\n if (entry.byName) this.nameIndex.set(entry.key as string, entry.key);\n else if (typeof entry.key === 'function' && (entry.key as any).name) {\n this.nameIndex.set((entry.key as any).name, entry.key);\n }\n }\n\n private entryLabel(entry: MethodConfigEntry): string {\n return entry.byName ? String(entry.key) : tokenLabel(entry.key);\n }\n\n /** Entries applying to a resolving token (exact identity or name match). */\n matchEntries(token: unknown): MethodConfigEntry[] {\n const name = typeof token === 'function' ? (token as any).name : typeof token === 'string' ? token : undefined;\n const matched = this.entries.filter(\n (entry) => entry.key === token || (entry.byName && name !== undefined && entry.key === name),\n );\n // Split-registry tripwire bookkeeping (spec 04 §17 \"duplicate\n // human-readable token names without identity collision\"): remember which\n // class-token entries genuinely bound, and which constructions bound\n // nothing — verify() cross-references the two by display name.\n if (matched.length) {\n for (const entry of matched) this.matchedEntryKeys.add(entry.key);\n } else if (typeof token === 'function' && name) {\n this.unmatchedConstructedByName.set(name, token);\n }\n return matched;\n }\n\n state(entryKey: unknown, method: string): MethodState | undefined {\n return this.states.get(entryKey)?.get(method);\n }\n\n inspect(token: unknown, method: string): MethodInspection {\n const label = tokenLabel(token);\n const keys: unknown[] = [token];\n const name = typeof token === 'function' ? (token as any).name : typeof token === 'string' ? token : undefined;\n if (name !== undefined && this.nameIndex.has(name)) keys.push(this.nameIndex.get(name));\n for (const key of keys) {\n const state = this.states.get(key)?.get(method);\n if (state) return state.inspection();\n }\n throw new UnwatchedInspectionError(label, method);\n }\n\n /**\n * Class-token entries that never identity-matched a construction while a\n * DIFFERENT class with the same display name did construct. This is the\n * split-module-registry signature (a test-file class object vs the graph's\n * own load of the same file) — or two genuinely distinct same-named tokens\n * where the configured one never resolved. Either way the configured\n * behavior silently did not apply, which must be loud.\n */\n private identitySplits(): { name: string; entryToken: unknown }[] {\n const splits: { name: string; entryToken: unknown }[] = [];\n for (const entry of this.entries) {\n if (entry.byName || typeof entry.key !== 'function') continue;\n if (this.matchedEntryKeys.has(entry.key)) continue;\n const name = (entry.key as { name?: string }).name;\n if (!name) continue;\n const constructed = this.unmatchedConstructedByName.get(name);\n if (constructed !== undefined && constructed !== entry.key) {\n splits.push({ name, entryToken: entry.key });\n }\n }\n return splits;\n }\n\n /** Repeatable snapshot check of all exact expectations. */\n verify(): void {\n const splits = this.identitySplits();\n if (splits.length) {\n throw new TokenIdentitySplitError(splits.map((split) => split.name));\n }\n const failures: { token: string; method: string; expected: number; actual: number }[] = [];\n for (const byMethod of this.states.values()) {\n for (const state of byMethod.values()) {\n const expectation = state.expectation;\n if (!expectation) continue;\n if (state.actual !== expectation.expected) {\n failures.push({\n token: state.tokenName,\n method: state.method,\n expected: expectation.expected,\n actual: state.actual,\n });\n }\n }\n }\n if (failures.length) throw new VerificationError(failures);\n }\n}\n\nfunction isPromiseLike(value: unknown): value is Promise<unknown> {\n return !!value && typeof (value as any).then === 'function';\n}\n\nfunction recordOutcome(call: MutableCall, outcome: unknown, threw: boolean): unknown {\n if (!threw && isPromiseLike(outcome)) {\n call.pending = true;\n outcome.then(\n (value) => {\n call.result = value;\n call.pending = false;\n },\n (error) => {\n call.error = error;\n call.pending = false;\n },\n );\n return outcome;\n }\n call.pending = false;\n if (threw) call.error = outcome;\n else call.result = outcome;\n return outcome;\n}\n\n/**\n * Build the ioc instance decorator for one environment.\n *\n * Identity-stable (WeakMap), preserves sync/async call shape and `this`\n * binding, forwards the ioc context symbols by delegating to the underlying\n * (context-wrapped) target for everything unconfigured.\n */\nexport function createMethodDecorator(registry: WatchRegistry): (instance: any, token: any) => any {\n const wrappers = new WeakMap<object, any>();\n\n return function decorate(instance: any, token: any): any {\n const entries = registry.matchEntries(token);\n if (entries.length === 0) return instance;\n\n if (instance === null || (typeof instance !== 'object' && typeof instance !== 'function')) {\n throw new NonObjectMethodTargetError(tokenLabel(token));\n }\n\n if (wrappers.has(instance)) return wrappers.get(instance);\n\n // Effective per-method config: entries merge in write order, LWW per method\n const effective = new Map<string, { entryKey: unknown; descriptor: MethodDescriptor }>();\n for (const entry of entries) {\n for (const [method, descriptor] of entry.methods) {\n effective.set(method, { entryKey: entry.key, descriptor });\n }\n }\n\n // Validate configured methods exist and are callable\n for (const method of effective.keys()) {\n if (typeof instance[method] !== 'function') {\n throw new MethodNotCallableError(tokenLabel(token), method);\n }\n }\n\n const methodCache = new Map<string, (...args: any[]) => any>();\n\n const proxy = new Proxy(instance, {\n get(target, prop, receiver) {\n if (typeof prop === 'string' && effective.has(prop)) {\n let wrapped = methodCache.get(prop);\n if (!wrapped) {\n const { entryKey, descriptor } = effective.get(prop)!;\n wrapped = buildMethodWrapper(target, prop, descriptor, registry.state(entryKey, prop));\n methodCache.set(prop, wrapped);\n }\n return wrapped;\n }\n // Everything else (including ioc context symbols) delegates to the\n // underlying — usually context-wrapped — target.\n if (prop === CONTEXT_WRAPPED || prop === CONTEXT_OWNER) return (target as any)[prop];\n return Reflect.get(target, prop, receiver);\n },\n });\n\n wrappers.set(instance, proxy);\n return proxy;\n };\n}\n\nfunction buildMethodWrapper(\n target: any,\n method: string,\n descriptor: MethodDescriptor,\n state: MethodState | undefined,\n): (...args: any[]) => any {\n // The original effective method, read through the underlying target so the\n // ioc context proxy still owns context entry for real invocations.\n const callOriginal = (self: any, args: any[]) => {\n const fn = target[method];\n return fn.apply(self === undefined ? target : self, args);\n };\n\n // Raw custom wrapper: installed as-is, NOT watched, no recording.\n if (typeof descriptor === 'function') {\n const replacement = descriptor((...args: any[]) => callOriginal(target, args));\n return function (this: any, ...args: any[]) {\n return replacement.apply(this, args);\n };\n }\n\n if (!isDescriptor(descriptor)) {\n // Should be unreachable: builder validates descriptors on write.\n throw new TypeError(`Invalid method descriptor for ${method}`);\n }\n\n return function (this: any, ...args: any[]) {\n if (!state) throw new TypeError(`Missing method state for ${method}`);\n\n const expectation = state.expectation;\n const attempted = state.actual + 1;\n\n // Overflow fails immediately and never runs behavior/original\n if (expectation && attempted > expectation.expected) {\n state.actual = attempted;\n throw new ExpectationOverflowError(state.tokenName, method, expectation.expected, attempted);\n }\n\n state.actual = attempted;\n const call: MutableCall = {\n index: attempted,\n args: [...args],\n pending: false,\n timestamp: Date.now(),\n };\n state.calls.push(call);\n\n // Effective behavior for this invocation\n let behavior: BehaviorDescriptor | 'original-effective' | RawMethodWrapper;\n const base =\n descriptor.kind === 'expect'\n ? (descriptor.behavior ?? 'original-effective')\n : descriptor.kind === 'watch'\n ? (descriptor.wrapper ?? 'original-effective')\n : descriptor;\n\n if (base !== 'original-effective' && typeof base !== 'function' && base !== undefined && base.kind === 'calls') {\n if (state.scriptCursor >= base.script.length) {\n throw new CallScriptExhaustedError(state.tokenName, method, base.script.length, attempted);\n }\n behavior = base.script[state.scriptCursor]!;\n state.scriptCursor += 1;\n } else {\n behavior = base;\n }\n\n try {\n let outcome: unknown;\n if (behavior === 'original-effective') {\n outcome = callOriginal(this, args);\n } else if (typeof behavior === 'function') {\n // test.watch(rawWrapper): wrapper behavior runs and is recorded\n outcome = behavior((...inner: any[]) => callOriginal(this, inner)).apply(this, args);\n } else if (behavior.kind === 'returns') {\n outcome = behavior.value;\n } else if (behavior.kind === 'throws') {\n throw behavior.error;\n } else if (behavior.kind === 'original') {\n outcome = callOriginal(this, args);\n } else {\n throw new TypeError(`Nested test.calls() scripts are not supported (${method}).`);\n }\n return recordOutcome(call, outcome, false);\n } catch (error) {\n recordOutcome(call, error, true);\n throw error;\n }\n };\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
CHANGED
|
@@ -130,12 +130,25 @@ declare class WatchRegistry {
|
|
|
130
130
|
private readonly states;
|
|
131
131
|
private readonly nameIndex;
|
|
132
132
|
private readonly entries;
|
|
133
|
+
/** Class-token entries that identity-matched at least one construction. */
|
|
134
|
+
private readonly matchedEntryKeys;
|
|
135
|
+
/** Constructed class tokens that matched NO entry, by display name. */
|
|
136
|
+
private readonly unmatchedConstructedByName;
|
|
133
137
|
addEntry(entry: MethodConfigEntry): void;
|
|
134
138
|
private entryLabel;
|
|
135
139
|
/** Entries applying to a resolving token (exact identity or name match). */
|
|
136
140
|
matchEntries(token: unknown): MethodConfigEntry[];
|
|
137
141
|
state(entryKey: unknown, method: string): MethodState | undefined;
|
|
138
142
|
inspect(token: unknown, method: string): MethodInspection;
|
|
143
|
+
/**
|
|
144
|
+
* Class-token entries that never identity-matched a construction while a
|
|
145
|
+
* DIFFERENT class with the same display name did construct. This is the
|
|
146
|
+
* split-module-registry signature (a test-file class object vs the graph's
|
|
147
|
+
* own load of the same file) — or two genuinely distinct same-named tokens
|
|
148
|
+
* where the configured one never resolved. Either way the configured
|
|
149
|
+
* behavior silently did not apply, which must be loud.
|
|
150
|
+
*/
|
|
151
|
+
private identitySplits;
|
|
139
152
|
/** Repeatable snapshot check of all exact expectations. */
|
|
140
153
|
verify(): void;
|
|
141
154
|
}
|
|
@@ -153,7 +166,19 @@ declare class WatchRegistry {
|
|
|
153
166
|
type Token = unknown;
|
|
154
167
|
type ClassLike = new (...args: any[]) => any;
|
|
155
168
|
/** Config maps accept plain objects (string keys) or Maps (exact tokens). */
|
|
156
|
-
|
|
169
|
+
/**
|
|
170
|
+
* Composition input (spec 04 §2). The CANONICAL entry form is an array of
|
|
171
|
+
* tuples — token-first, exact identity:
|
|
172
|
+
*
|
|
173
|
+
* .classes([[ProjectRepository, MemoryProjectRepository]])
|
|
174
|
+
*
|
|
175
|
+
* A `ReadonlyMap<Token, V>` is equivalent (token-keyed). A name-keyed
|
|
176
|
+
* `Record<string, V>` remains accepted as COMPATIBILITY input: string keys
|
|
177
|
+
* resolve against known token display names, fail on unknown names, and
|
|
178
|
+
* fail on ambiguous names (two tokens sharing one display name never
|
|
179
|
+
* collapse into one entry — tokens are identities, names are labels).
|
|
180
|
+
*/
|
|
181
|
+
type ConfigMap<V> = ReadonlyArray<readonly [Token, V]> | ReadonlyMap<Token, V> | Record<string, V>;
|
|
157
182
|
type MethodsConfig = ConfigMap<Record<string, MethodDescriptor> | ReadonlyMap<string, MethodDescriptor>>;
|
|
158
183
|
type UseInput = ApplicationModule | TestIocBuilder;
|
|
159
184
|
type Write = {
|
|
@@ -268,5 +293,26 @@ declare class UnknownTokenKeyError extends TestingError {
|
|
|
268
293
|
/** Invalid descriptor construction (e.g. test.times(-1)). */
|
|
269
294
|
declare class InvalidDescriptorError extends TestingError {
|
|
270
295
|
}
|
|
296
|
+
/**
|
|
297
|
+
* A name-keyed compatibility entry referred to a display name claimed by
|
|
298
|
+
* two or more distinct tokens. Tokens are identities; names are labels —
|
|
299
|
+
* pass the exact token in canonical tuple form instead (spec 04 §5).
|
|
300
|
+
*/
|
|
301
|
+
declare class AmbiguousNameKeyError extends TestingError {
|
|
302
|
+
constructor(name: string, space: string);
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* A .methods() class token never matched any constructed instance while a
|
|
306
|
+
* DIFFERENT class with the same display name did construct — the configured
|
|
307
|
+
* behavior silently did not apply. Usual cause: two module registries loaded
|
|
308
|
+
* the same source file (e.g. a vitest test file's import vs a framework
|
|
309
|
+
* harness's native import of the production graph); fix by routing the
|
|
310
|
+
* harness's imports through the caller's registry (testApp:
|
|
311
|
+
* `.importer((p) => import(p))`) or by passing the token the graph actually
|
|
312
|
+
* uses.
|
|
313
|
+
*/
|
|
314
|
+
declare class TokenIdentitySplitError extends TestingError {
|
|
315
|
+
constructor(names: readonly string[]);
|
|
316
|
+
}
|
|
271
317
|
|
|
272
|
-
export { type BehaviorDescriptor, CallScriptExhaustedError, type CallsDescriptor, type ConfigMap, ENV_REGISTRY, type ExpectationDescriptor, ExpectationOverflowError, InvalidDescriptorError, type MethodDescriptor, type MethodInspection, MethodNotCallableError, type MethodsConfig, MissingIocSeamError, NonObjectMethodTargetError, type OriginalDescriptor, type RawMethodWrapper, type RecordedCall, type ReturnsDescriptor, type TestEnvironment, TestIocBuilder, TestingError, type ThrowsDescriptor, UnknownTokenKeyError, UnwatchedInspectionError, type UseInput, VerificationError, type WatchDescriptor, test, testIoc };
|
|
318
|
+
export { AmbiguousNameKeyError, type BehaviorDescriptor, CallScriptExhaustedError, type CallsDescriptor, type ConfigMap, ENV_REGISTRY, type ExpectationDescriptor, ExpectationOverflowError, InvalidDescriptorError, type MethodDescriptor, type MethodInspection, MethodNotCallableError, type MethodsConfig, MissingIocSeamError, NonObjectMethodTargetError, type OriginalDescriptor, type RawMethodWrapper, type RecordedCall, type ReturnsDescriptor, type TestEnvironment, TestIocBuilder, TestingError, type ThrowsDescriptor, TokenIdentitySplitError, UnknownTokenKeyError, UnwatchedInspectionError, type UseInput, VerificationError, type WatchDescriptor, test, testIoc };
|
package/dist/index.d.ts
CHANGED
|
@@ -130,12 +130,25 @@ declare class WatchRegistry {
|
|
|
130
130
|
private readonly states;
|
|
131
131
|
private readonly nameIndex;
|
|
132
132
|
private readonly entries;
|
|
133
|
+
/** Class-token entries that identity-matched at least one construction. */
|
|
134
|
+
private readonly matchedEntryKeys;
|
|
135
|
+
/** Constructed class tokens that matched NO entry, by display name. */
|
|
136
|
+
private readonly unmatchedConstructedByName;
|
|
133
137
|
addEntry(entry: MethodConfigEntry): void;
|
|
134
138
|
private entryLabel;
|
|
135
139
|
/** Entries applying to a resolving token (exact identity or name match). */
|
|
136
140
|
matchEntries(token: unknown): MethodConfigEntry[];
|
|
137
141
|
state(entryKey: unknown, method: string): MethodState | undefined;
|
|
138
142
|
inspect(token: unknown, method: string): MethodInspection;
|
|
143
|
+
/**
|
|
144
|
+
* Class-token entries that never identity-matched a construction while a
|
|
145
|
+
* DIFFERENT class with the same display name did construct. This is the
|
|
146
|
+
* split-module-registry signature (a test-file class object vs the graph's
|
|
147
|
+
* own load of the same file) — or two genuinely distinct same-named tokens
|
|
148
|
+
* where the configured one never resolved. Either way the configured
|
|
149
|
+
* behavior silently did not apply, which must be loud.
|
|
150
|
+
*/
|
|
151
|
+
private identitySplits;
|
|
139
152
|
/** Repeatable snapshot check of all exact expectations. */
|
|
140
153
|
verify(): void;
|
|
141
154
|
}
|
|
@@ -153,7 +166,19 @@ declare class WatchRegistry {
|
|
|
153
166
|
type Token = unknown;
|
|
154
167
|
type ClassLike = new (...args: any[]) => any;
|
|
155
168
|
/** Config maps accept plain objects (string keys) or Maps (exact tokens). */
|
|
156
|
-
|
|
169
|
+
/**
|
|
170
|
+
* Composition input (spec 04 §2). The CANONICAL entry form is an array of
|
|
171
|
+
* tuples — token-first, exact identity:
|
|
172
|
+
*
|
|
173
|
+
* .classes([[ProjectRepository, MemoryProjectRepository]])
|
|
174
|
+
*
|
|
175
|
+
* A `ReadonlyMap<Token, V>` is equivalent (token-keyed). A name-keyed
|
|
176
|
+
* `Record<string, V>` remains accepted as COMPATIBILITY input: string keys
|
|
177
|
+
* resolve against known token display names, fail on unknown names, and
|
|
178
|
+
* fail on ambiguous names (two tokens sharing one display name never
|
|
179
|
+
* collapse into one entry — tokens are identities, names are labels).
|
|
180
|
+
*/
|
|
181
|
+
type ConfigMap<V> = ReadonlyArray<readonly [Token, V]> | ReadonlyMap<Token, V> | Record<string, V>;
|
|
157
182
|
type MethodsConfig = ConfigMap<Record<string, MethodDescriptor> | ReadonlyMap<string, MethodDescriptor>>;
|
|
158
183
|
type UseInput = ApplicationModule | TestIocBuilder;
|
|
159
184
|
type Write = {
|
|
@@ -268,5 +293,26 @@ declare class UnknownTokenKeyError extends TestingError {
|
|
|
268
293
|
/** Invalid descriptor construction (e.g. test.times(-1)). */
|
|
269
294
|
declare class InvalidDescriptorError extends TestingError {
|
|
270
295
|
}
|
|
296
|
+
/**
|
|
297
|
+
* A name-keyed compatibility entry referred to a display name claimed by
|
|
298
|
+
* two or more distinct tokens. Tokens are identities; names are labels —
|
|
299
|
+
* pass the exact token in canonical tuple form instead (spec 04 §5).
|
|
300
|
+
*/
|
|
301
|
+
declare class AmbiguousNameKeyError extends TestingError {
|
|
302
|
+
constructor(name: string, space: string);
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* A .methods() class token never matched any constructed instance while a
|
|
306
|
+
* DIFFERENT class with the same display name did construct — the configured
|
|
307
|
+
* behavior silently did not apply. Usual cause: two module registries loaded
|
|
308
|
+
* the same source file (e.g. a vitest test file's import vs a framework
|
|
309
|
+
* harness's native import of the production graph); fix by routing the
|
|
310
|
+
* harness's imports through the caller's registry (testApp:
|
|
311
|
+
* `.importer((p) => import(p))`) or by passing the token the graph actually
|
|
312
|
+
* uses.
|
|
313
|
+
*/
|
|
314
|
+
declare class TokenIdentitySplitError extends TestingError {
|
|
315
|
+
constructor(names: readonly string[]);
|
|
316
|
+
}
|
|
271
317
|
|
|
272
|
-
export { type BehaviorDescriptor, CallScriptExhaustedError, type CallsDescriptor, type ConfigMap, ENV_REGISTRY, type ExpectationDescriptor, ExpectationOverflowError, InvalidDescriptorError, type MethodDescriptor, type MethodInspection, MethodNotCallableError, type MethodsConfig, MissingIocSeamError, NonObjectMethodTargetError, type OriginalDescriptor, type RawMethodWrapper, type RecordedCall, type ReturnsDescriptor, type TestEnvironment, TestIocBuilder, TestingError, type ThrowsDescriptor, UnknownTokenKeyError, UnwatchedInspectionError, type UseInput, VerificationError, type WatchDescriptor, test, testIoc };
|
|
318
|
+
export { AmbiguousNameKeyError, type BehaviorDescriptor, CallScriptExhaustedError, type CallsDescriptor, type ConfigMap, ENV_REGISTRY, type ExpectationDescriptor, ExpectationOverflowError, InvalidDescriptorError, type MethodDescriptor, type MethodInspection, MethodNotCallableError, type MethodsConfig, MissingIocSeamError, NonObjectMethodTargetError, type OriginalDescriptor, type RawMethodWrapper, type RecordedCall, type ReturnsDescriptor, type TestEnvironment, TestIocBuilder, TestingError, type ThrowsDescriptor, TokenIdentitySplitError, UnknownTokenKeyError, UnwatchedInspectionError, type UseInput, VerificationError, type WatchDescriptor, test, testIoc };
|
package/dist/index.js
CHANGED
|
@@ -73,6 +73,22 @@ var UnknownTokenKeyError = class extends TestingError {
|
|
|
73
73
|
};
|
|
74
74
|
var InvalidDescriptorError = class extends TestingError {
|
|
75
75
|
};
|
|
76
|
+
var AmbiguousNameKeyError = class extends TestingError {
|
|
77
|
+
constructor(name, space) {
|
|
78
|
+
super(
|
|
79
|
+
`.${space}() key "${name}" is ambiguous: multiple distinct tokens share that display name. Use the canonical tuple form with the exact token: .${space}([[TheToken, ...]]).`
|
|
80
|
+
);
|
|
81
|
+
this.name = "AmbiguousNameKeyError";
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
var TokenIdentitySplitError = class extends TestingError {
|
|
85
|
+
constructor(names) {
|
|
86
|
+
super(
|
|
87
|
+
`token identity split: .methods() entr${names.length === 1 ? "y" : "ies"} for ${names.map((name) => `"${name}"`).join(", ")} never matched a constructed instance, but a DIFFERENT class with the same name was constructed. Two module registries have loaded the same class file (test-file import vs harness graph import) \u2014 the configured behavior did not apply. Route the harness's imports through your registry (e.g. testApp .importer((p) => import(p))) or pass the exact token the graph uses.`
|
|
88
|
+
);
|
|
89
|
+
this.name = "TokenIdentitySplitError";
|
|
90
|
+
}
|
|
91
|
+
};
|
|
76
92
|
|
|
77
93
|
// src/descriptors.ts
|
|
78
94
|
var DESCRIPTOR = /* @__PURE__ */ Symbol.for("noego:testing:descriptor");
|
|
@@ -193,6 +209,10 @@ var WatchRegistry = class {
|
|
|
193
209
|
states = /* @__PURE__ */ new Map();
|
|
194
210
|
nameIndex = /* @__PURE__ */ new Map();
|
|
195
211
|
entries = [];
|
|
212
|
+
/** Class-token entries that identity-matched at least one construction. */
|
|
213
|
+
matchedEntryKeys = /* @__PURE__ */ new Set();
|
|
214
|
+
/** Constructed class tokens that matched NO entry, by display name. */
|
|
215
|
+
unmatchedConstructedByName = /* @__PURE__ */ new Map();
|
|
196
216
|
addEntry(entry) {
|
|
197
217
|
this.entries.push(entry);
|
|
198
218
|
const byMethod = /* @__PURE__ */ new Map();
|
|
@@ -212,9 +232,15 @@ var WatchRegistry = class {
|
|
|
212
232
|
/** Entries applying to a resolving token (exact identity or name match). */
|
|
213
233
|
matchEntries(token) {
|
|
214
234
|
const name = typeof token === "function" ? token.name : typeof token === "string" ? token : void 0;
|
|
215
|
-
|
|
235
|
+
const matched = this.entries.filter(
|
|
216
236
|
(entry) => entry.key === token || entry.byName && name !== void 0 && entry.key === name
|
|
217
237
|
);
|
|
238
|
+
if (matched.length) {
|
|
239
|
+
for (const entry of matched) this.matchedEntryKeys.add(entry.key);
|
|
240
|
+
} else if (typeof token === "function" && name) {
|
|
241
|
+
this.unmatchedConstructedByName.set(name, token);
|
|
242
|
+
}
|
|
243
|
+
return matched;
|
|
218
244
|
}
|
|
219
245
|
state(entryKey, method) {
|
|
220
246
|
return this.states.get(entryKey)?.get(method);
|
|
@@ -230,8 +256,34 @@ var WatchRegistry = class {
|
|
|
230
256
|
}
|
|
231
257
|
throw new UnwatchedInspectionError(label, method);
|
|
232
258
|
}
|
|
259
|
+
/**
|
|
260
|
+
* Class-token entries that never identity-matched a construction while a
|
|
261
|
+
* DIFFERENT class with the same display name did construct. This is the
|
|
262
|
+
* split-module-registry signature (a test-file class object vs the graph's
|
|
263
|
+
* own load of the same file) — or two genuinely distinct same-named tokens
|
|
264
|
+
* where the configured one never resolved. Either way the configured
|
|
265
|
+
* behavior silently did not apply, which must be loud.
|
|
266
|
+
*/
|
|
267
|
+
identitySplits() {
|
|
268
|
+
const splits = [];
|
|
269
|
+
for (const entry of this.entries) {
|
|
270
|
+
if (entry.byName || typeof entry.key !== "function") continue;
|
|
271
|
+
if (this.matchedEntryKeys.has(entry.key)) continue;
|
|
272
|
+
const name = entry.key.name;
|
|
273
|
+
if (!name) continue;
|
|
274
|
+
const constructed = this.unmatchedConstructedByName.get(name);
|
|
275
|
+
if (constructed !== void 0 && constructed !== entry.key) {
|
|
276
|
+
splits.push({ name, entryToken: entry.key });
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return splits;
|
|
280
|
+
}
|
|
233
281
|
/** Repeatable snapshot check of all exact expectations. */
|
|
234
282
|
verify() {
|
|
283
|
+
const splits = this.identitySplits();
|
|
284
|
+
if (splits.length) {
|
|
285
|
+
throw new TokenIdentitySplitError(splits.map((split) => split.name));
|
|
286
|
+
}
|
|
235
287
|
const failures = [];
|
|
236
288
|
for (const byMethod of this.states.values()) {
|
|
237
289
|
for (const state of byMethod.values()) {
|
|
@@ -380,7 +432,16 @@ function buildMethodWrapper(target, method, descriptor, state) {
|
|
|
380
432
|
// src/builder.ts
|
|
381
433
|
var COMPONENT_OPTIONS_KEY = /* @__PURE__ */ Symbol.for("ioc:component:options");
|
|
382
434
|
function* configEntries(config) {
|
|
383
|
-
if (config
|
|
435
|
+
if (Array.isArray(config)) {
|
|
436
|
+
for (const entry of config) {
|
|
437
|
+
if (!Array.isArray(entry) || entry.length !== 2) {
|
|
438
|
+
throw new InvalidDescriptorError(
|
|
439
|
+
"canonical composition entries are [token, value] tuples, e.g. .classes([[Token, Impl]])"
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
yield [entry[0], false, entry[1]];
|
|
443
|
+
}
|
|
444
|
+
} else if (config instanceof Map) {
|
|
384
445
|
for (const [key, value] of config) yield [key, false, value];
|
|
385
446
|
} else {
|
|
386
447
|
for (const key of [...Object.getOwnPropertyNames(config), ...Object.getOwnPropertySymbols(config)]) {
|
|
@@ -483,16 +544,24 @@ var TestIocBuilder = class _TestIocBuilder {
|
|
|
483
544
|
throw new MissingIocSeamError();
|
|
484
545
|
}
|
|
485
546
|
const knownByName = /* @__PURE__ */ new Map();
|
|
547
|
+
const ambiguousNames = /* @__PURE__ */ new Set();
|
|
486
548
|
const knownLifetimes = /* @__PURE__ */ new Map();
|
|
487
549
|
const note = (token, loadAs) => {
|
|
488
|
-
|
|
489
|
-
|
|
550
|
+
const name = typeof token === "function" && token.name ? token.name : typeof token === "string" ? token : null;
|
|
551
|
+
if (name !== null) {
|
|
552
|
+
const existing = knownByName.get(name);
|
|
553
|
+
if (existing !== void 0 && existing !== token) ambiguousNames.add(name);
|
|
554
|
+
knownByName.set(name, token);
|
|
555
|
+
}
|
|
490
556
|
if (loadAs !== void 0) knownLifetimes.set(token, loadAs);
|
|
491
557
|
};
|
|
492
558
|
const effective = /* @__PURE__ */ new Map();
|
|
493
559
|
const methodWrites = [];
|
|
494
560
|
const resolveKey = (key, byName, space) => {
|
|
495
561
|
if (!byName) return key;
|
|
562
|
+
if (ambiguousNames.has(key)) {
|
|
563
|
+
throw new AmbiguousNameKeyError(key, space);
|
|
564
|
+
}
|
|
496
565
|
const known = knownByName.get(key);
|
|
497
566
|
if (known !== void 0) return known;
|
|
498
567
|
if (space === "values" || space === "functions") return key;
|
|
@@ -537,8 +606,13 @@ var TestIocBuilder = class _TestIocBuilder {
|
|
|
537
606
|
space: "class",
|
|
538
607
|
token,
|
|
539
608
|
implementation: write.implementation,
|
|
540
|
-
//
|
|
541
|
-
|
|
609
|
+
// Lifetime priority: the replacement's own @Component scope, then
|
|
610
|
+
// the lifetime the composition already knows for the token, then
|
|
611
|
+
// the TOKEN class's declared @Component scope — a plain stub
|
|
612
|
+
// class replacing a Singleton-scoped production service must not
|
|
613
|
+
// silently degrade to Transient (captive-lifetime validation
|
|
614
|
+
// would reject the production dependents).
|
|
615
|
+
loadAs: componentScope(write.implementation) ?? knownLifetimes.get(token) ?? componentScope(token)
|
|
542
616
|
});
|
|
543
617
|
break;
|
|
544
618
|
}
|
|
@@ -563,6 +637,9 @@ var TestIocBuilder = class _TestIocBuilder {
|
|
|
563
637
|
break;
|
|
564
638
|
}
|
|
565
639
|
case "methods": {
|
|
640
|
+
if (write.byName && ambiguousNames.has(write.key)) {
|
|
641
|
+
throw new AmbiguousNameKeyError(write.key, "methods");
|
|
642
|
+
}
|
|
566
643
|
const token = write.byName && knownByName.has(write.key) ? knownByName.get(write.key) : write.key;
|
|
567
644
|
methodWrites.push({
|
|
568
645
|
key: token,
|
|
@@ -637,6 +714,7 @@ function testIoc(...inputs) {
|
|
|
637
714
|
return TestIocBuilder.create(inputs);
|
|
638
715
|
}
|
|
639
716
|
export {
|
|
717
|
+
AmbiguousNameKeyError,
|
|
640
718
|
CallScriptExhaustedError,
|
|
641
719
|
ENV_REGISTRY,
|
|
642
720
|
ExpectationOverflowError,
|
|
@@ -646,6 +724,7 @@ export {
|
|
|
646
724
|
NonObjectMethodTargetError,
|
|
647
725
|
TestIocBuilder,
|
|
648
726
|
TestingError,
|
|
727
|
+
TokenIdentitySplitError,
|
|
649
728
|
UnknownTokenKeyError,
|
|
650
729
|
UnwatchedInspectionError,
|
|
651
730
|
VerificationError,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/builder.ts","../src/errors.ts","../src/descriptors.ts","../src/method_state.ts"],"sourcesContent":["/**\n * `testIoc` — the canonical shared real-IoC test composition builder.\n *\n * Persistent immutable: every fluent call returns a new derived builder\n * sharing the ordered write log structurally. Non-conflicting writes are\n * order-insensitive; the last write to the same effective identity wins on\n * that derived branch. `.build()` is non-consuming and creates fresh runtime,\n * watch, and expectation state (spec 15, PBR-01..15).\n */\n\nimport {\n createContainer,\n flattenModule,\n LoadAs,\n SCOPED_CONTAINER,\n type ApplicationModule,\n type IContainer,\n} from '@noego/ioc';\n\nimport { ENV_REGISTRY, isDescriptor, type MethodDescriptor } from './descriptors';\nimport { createMethodDecorator, WatchRegistry, tokenLabel } from './method_state';\nimport { InvalidDescriptorError, MissingIocSeamError, UnknownTokenKeyError } from './errors';\n\nconst COMPONENT_OPTIONS_KEY = Symbol.for('ioc:component:options');\n\ntype Token = unknown;\ntype ClassLike = new (...args: any[]) => any;\n\n/** Config maps accept plain objects (string keys) or Maps (exact tokens). */\nexport type ConfigMap<V> = Record<string, V> | ReadonlyMap<Token, V>;\n\nexport type MethodsConfig = ConfigMap<Record<string, MethodDescriptor> | ReadonlyMap<string, MethodDescriptor>>;\n\nexport type UseInput = ApplicationModule | TestIocBuilder;\n\ntype Write =\n | { op: 'use'; module: ApplicationModule }\n | { op: 'classes'; key: Token; byName: boolean; implementation: ClassLike }\n | { op: 'functions'; key: Token; byName: boolean; factory: (...args: any[]) => any }\n | { op: 'values'; key: Token; byName: boolean; value: unknown }\n | { op: 'methods'; key: Token; byName: boolean; methods: ReadonlyMap<string, MethodDescriptor> };\n\nexport interface TestEnvironment {\n readonly root: IContainer;\n get<T>(token: unknown, params?: any[]): Promise<T> | T;\n instance<T>(cls: new (...args: any[]) => T, params?: any[]): Promise<T> | T;\n extend(): IContainer;\n verify(): Promise<void>;\n dispose(): Promise<void>;\n readonly [ENV_REGISTRY]: WatchRegistry;\n}\n\nfunction* configEntries<V>(config: ConfigMap<V>): Iterable<[Token, boolean, V]> {\n if (config instanceof Map) {\n for (const [key, value] of config) yield [key, false, value];\n } else {\n for (const key of [...Object.getOwnPropertyNames(config), ...Object.getOwnPropertySymbols(config)]) {\n yield [key, typeof key === 'string', (config as any)[key]];\n }\n }\n}\n\nfunction componentScope(cls: any): LoadAs | undefined {\n const options =\n typeof Reflect !== 'undefined' && (Reflect as any).getMetadata\n ? (Reflect as any).getMetadata(COMPONENT_OPTIONS_KEY, cls)\n : undefined;\n return options?.scope;\n}\n\nfunction lifetimeToLoadAs(lifetime: string): LoadAs {\n if (lifetime === 'singleton') return LoadAs.Singleton;\n if (lifetime === 'scoped') return LoadAs.Scoped;\n return LoadAs.Transient;\n}\n\nexport class TestIocBuilder {\n private constructor(private readonly log: readonly Write[]) {\n Object.freeze(this);\n }\n\n /** @internal */\n static create(inputs: readonly UseInput[]): TestIocBuilder {\n return new TestIocBuilder([]).useAll(inputs);\n }\n\n /** @internal — read by .use(builderPreset) */\n get writes(): readonly Write[] {\n return this.log;\n }\n\n private derive(writes: Write[]): TestIocBuilder {\n return new TestIocBuilder([...this.log, ...writes]);\n }\n\n private useAll(inputs: readonly UseInput[]): TestIocBuilder {\n let builder: TestIocBuilder = this;\n for (const input of inputs) builder = builder.use(input);\n return builder;\n }\n\n /** Apply a reusable composition preset: an ApplicationModule or a builder. */\n use(preset: UseInput): TestIocBuilder {\n if (preset instanceof TestIocBuilder) {\n return this.derive([...preset.writes]);\n }\n return this.derive([{ op: 'use', module: preset }]);\n }\n\n /** Replace the implementation for IoC class tokens in the built environment. */\n classes(config: ConfigMap<ClassLike>): TestIocBuilder {\n const writes: Write[] = [];\n for (const [key, byName, implementation] of configEntries(config)) {\n if (typeof implementation !== 'function' || !implementation.prototype) {\n throw new InvalidDescriptorError(\n `.classes() value for \"${tokenLabel(key)}\" must be a class constructor.`,\n );\n }\n writes.push({ op: 'classes', key, byName, implementation });\n }\n return this.derive(writes);\n }\n\n /** Replace IoC factory/provider registrations. */\n functions(config: ConfigMap<(...args: any[]) => any>): TestIocBuilder {\n const writes: Write[] = [];\n for (const [key, byName, factory] of configEntries(config)) {\n if (typeof factory !== 'function') {\n throw new InvalidDescriptorError(`.functions() value for \"${tokenLabel(key)}\" must be a function.`);\n }\n writes.push({ op: 'functions', key, byName, factory });\n }\n return this.derive(writes);\n }\n\n /** Provide/replace IoC value registrations. */\n values(config: ConfigMap<unknown>): TestIocBuilder {\n const writes: Write[] = [];\n for (const [key, byName, value] of configEntries(config)) {\n writes.push({ op: 'values', key, byName, value });\n }\n return this.derive(writes);\n }\n\n /** Install method behavior/observation descriptors on IoC-managed instances. */\n methods(config: MethodsConfig): TestIocBuilder {\n const writes: Write[] = [];\n for (const [key, byName, methodsInput] of configEntries(config)) {\n const methods = new Map<string, MethodDescriptor>();\n const entries =\n methodsInput instanceof Map\n ? methodsInput.entries()\n : Object.entries(methodsInput as Record<string, MethodDescriptor>);\n for (const [name, descriptor] of entries) {\n if (typeof descriptor !== 'function' && !isDescriptor(descriptor)) {\n throw new InvalidDescriptorError(\n `.methods() entry ${tokenLabel(key)}.${name} must be a test.* descriptor or a raw wrapper function.`,\n );\n }\n methods.set(name, descriptor);\n }\n writes.push({ op: 'methods', key, byName, methods });\n }\n return this.derive(writes);\n }\n\n /** Materialize a fresh, isolated real-IoC environment. Non-consuming. */\n async build(): Promise<TestEnvironment> {\n const root = createContainer();\n if (typeof (root as any).setInstanceDecorator !== 'function') {\n throw new MissingIocSeamError();\n }\n\n // ---- Materialize the ordered log: last write wins per effective identity\n // Known tokens (for resolving string keys of classes/functions/values)\n const knownByName = new Map<string, Token>();\n const knownLifetimes = new Map<Token, LoadAs>();\n const note = (token: Token, loadAs: LoadAs | undefined) => {\n if (typeof token === 'function' && (token as any).name) knownByName.set((token as any).name, token);\n else if (typeof token === 'string') knownByName.set(token, token);\n if (loadAs !== undefined) knownLifetimes.set(token, loadAs);\n };\n\n type Effective =\n | { space: 'class'; token: Token; implementation: ClassLike; loadAs?: LoadAs }\n | { space: 'factory'; token: Token; factory: (...args: any[]) => any; loadAs?: LoadAs; deps?: Token[] }\n | { space: 'value'; token: Token; value: unknown };\n\n // ordered map: identity → latest effective write (Map preserves first-write\n // position which is fine — later writes replace content, LWW)\n const effective = new Map<Token, Effective>();\n const methodWrites: { key: Token; byName: boolean; methods: Map<string, MethodDescriptor> }[] = [];\n\n const resolveKey = (key: Token, byName: boolean, space: string): Token => {\n if (!byName) return key;\n const known = knownByName.get(key as string);\n if (known !== undefined) return known;\n if (space === 'values' || space === 'functions') return key; // string tokens are first-class\n throw new UnknownTokenKeyError(key as string, space, [...knownByName.keys()]);\n };\n\n for (const write of this.log) {\n switch (write.op) {\n case 'use': {\n for (const reg of flattenModule(write.module)) {\n const loadAs = lifetimeToLoadAs(reg.lifetime);\n note(reg.token, loadAs);\n if (reg.kind === 'class') {\n note(reg.implementation as Token, loadAs);\n effective.set(reg.token, {\n space: 'class',\n token: reg.token,\n implementation: reg.implementation as ClassLike,\n loadAs,\n });\n knownLifetimes.set(reg.token, loadAs);\n knownLifetimes.set(reg.implementation as Token, loadAs);\n } else if (reg.kind === 'factory') {\n effective.set(reg.token, {\n space: 'factory',\n token: reg.token,\n factory: reg.implementation as (...args: any[]) => any,\n loadAs,\n deps: [...reg.dependencies],\n });\n } else {\n effective.set(reg.token, { space: 'value', token: reg.token, value: reg.implementation });\n knownLifetimes.set(reg.token, LoadAs.Singleton);\n }\n }\n break;\n }\n case 'classes': {\n const token = resolveKey(write.key, write.byName, 'classes');\n note(write.implementation, undefined);\n if (!write.byName) note(token, undefined);\n effective.set(token, {\n space: 'class',\n token,\n implementation: write.implementation,\n // preserve configured lifetime unless the replacement declares its own scope\n loadAs: componentScope(write.implementation) ?? knownLifetimes.get(token),\n });\n break;\n }\n case 'functions': {\n const token = resolveKey(write.key, write.byName, 'functions');\n const prior = effective.get(token);\n effective.set(token, {\n space: 'factory',\n token,\n factory: write.factory,\n // preserve configured lifetime unless the scenario overrides it\n loadAs: prior && prior.space === 'factory' ? prior.loadAs : knownLifetimes.get(token),\n deps: prior && prior.space === 'factory' ? prior.deps : undefined,\n });\n note(token, undefined);\n break;\n }\n case 'values': {\n const token = resolveKey(write.key, write.byName, 'values');\n effective.set(token, { space: 'value', token, value: write.value });\n note(token, LoadAs.Singleton);\n break;\n }\n case 'methods': {\n const token = write.byName && knownByName.has(write.key as string)\n ? knownByName.get(write.key as string)!\n : write.key;\n methodWrites.push({\n key: token,\n byName: write.byName && !knownByName.has(write.key as string),\n methods: new Map(write.methods),\n });\n break;\n }\n }\n }\n\n // ---- Apply effective registrations to the fresh root\n for (const entry of effective.values()) {\n if (entry.space === 'class') {\n this.registerClassBinding(root, entry.token, entry.implementation, entry.loadAs);\n } else if (entry.space === 'factory') {\n root.registerFunction(entry.token, entry.factory, {\n loadAs: entry.loadAs,\n param: entry.deps,\n } as any);\n } else {\n const value = entry.value;\n root.registerFunction(entry.token, () => value, { loadAs: LoadAs.Singleton } as any);\n }\n }\n\n // ---- Fresh watch/expectation state + the ioc decoration seam\n const registry = new WatchRegistry();\n // merge method writes: LWW per (entry key, method), deep-merge per token\n const mergedMethods = new Map<Token, { byName: boolean; methods: Map<string, MethodDescriptor> }>();\n for (const write of methodWrites) {\n const existing = mergedMethods.get(write.key);\n if (existing) {\n for (const [name, descriptor] of write.methods) existing.methods.set(name, descriptor);\n } else {\n mergedMethods.set(write.key, { byName: write.byName, methods: new Map(write.methods) });\n }\n }\n for (const [key, { byName, methods }] of mergedMethods) {\n registry.addEntry({ key, byName, methods });\n }\n (root as any).setInstanceDecorator(createMethodDecorator(registry));\n\n const env: TestEnvironment = {\n root: root as IContainer,\n get: (token, params) => root.get(token as any, params),\n instance: (cls, params) => root.instance(cls, params),\n extend: () => root.extend() as IContainer,\n verify: async () => registry.verify(),\n dispose: () => root.dispose(),\n [ENV_REGISTRY]: registry,\n };\n return env;\n }\n\n /**\n * Register a class binding. When token === implementation this is a plain\n * class registration. Otherwise an alias factory resolves the implementation\n * through real IoC, mirroring the implementation's effective lifetime so\n * lifetime validation (captive-lifetime checks) stays honest.\n */\n private registerClassBinding(\n root: IContainer,\n token: Token,\n implementation: ClassLike,\n configuredLoadAs?: LoadAs,\n ): void {\n const loadAs = configuredLoadAs ?? componentScope(implementation) ?? LoadAs.Transient;\n root.registerClass(implementation as any, configuredLoadAs !== undefined ? ({ loadAs } as any) : undefined);\n if (token === implementation) return;\n\n if (loadAs === LoadAs.Singleton) {\n root.registerFunction(token, () => root.get(implementation as any), {\n loadAs: LoadAs.Singleton,\n } as any);\n } else {\n root.registerFunction(token, (scope: IContainer) => scope.get(implementation as any), {\n loadAs,\n param: [SCOPED_CONTAINER],\n } as any);\n }\n }\n}\n\n/** Create a persistent immutable real-IoC test composition builder. */\nexport function testIoc(...inputs: UseInput[]): TestIocBuilder {\n return TestIocBuilder.create(inputs);\n}\n","/**\n * Diagnostics are first-class: every error names the real token/method and\n * what was expected vs what happened, never only internal wrapper machinery.\n */\n\nexport class TestingError extends Error {\n constructor(message: string) {\n super(message);\n this.name = new.target.name;\n }\n}\n\n/** Thrown at build() when the installed @noego/ioc lacks the decoration seam. */\nexport class MissingIocSeamError extends TestingError {\n constructor() {\n super(\n '@noego/testing requires an @noego/ioc version that provides ' +\n 'Container.setInstanceDecorator (>= 0.5.x with the instance-decoration seam). ' +\n 'Upgrade @noego/ioc.',\n );\n }\n}\n\n/** test.inspect() on a method that is not watched in this environment. */\nexport class UnwatchedInspectionError extends TestingError {\n constructor(token: string, method: string) {\n super(\n `Method \"${method}\" on ${token} is not watched in this environment. ` +\n 'Only watched methods are inspectable — install test.watch() or any ' +\n 'test.* behavior/expectation descriptor for it.',\n );\n }\n}\n\n/** A call arrived after a test.calls([...]) script was fully consumed. */\nexport class CallScriptExhaustedError extends TestingError {\n constructor(token: string, method: string, scriptLength: number, callIndex: number) {\n super(\n `Call #${callIndex} to ${token}.${method} exceeds its test.calls() script ` +\n `of ${scriptLength} ${scriptLength === 1 ? 'entry' : 'entries'}.`,\n );\n }\n}\n\n/** An exact expectation (once/times/never) was exceeded at call time. */\nexport class ExpectationOverflowError extends TestingError {\n constructor(token: string, method: string, expected: number, attempted: number) {\n super(\n expected === 0\n ? `${token}.${method} was expected never to be called, but it was invoked.`\n : `${token}.${method} was expected exactly ${expected} ` +\n `${expected === 1 ? 'call' : 'calls'}, but call #${attempted} arrived.`,\n );\n }\n}\n\n/** Aggregated under-count failures reported by env.verify(). */\nexport class VerificationError extends TestingError {\n constructor(failures: readonly { token: string; method: string; expected: number; actual: number }[]) {\n super(\n 'Exact method expectations were not satisfied:\\n' +\n failures\n .map(\n (f) =>\n ` - ${f.token}.${f.method}: expected exactly ${f.expected} ` +\n `${f.expected === 1 ? 'call' : 'calls'}, observed ${f.actual}`,\n )\n .join('\\n'),\n );\n }\n}\n\n/** .methods configured for a token whose resolved value has no such callable method. */\nexport class MethodNotCallableError extends TestingError {\n constructor(token: string, method: string) {\n super(\n `Cannot install a test.* descriptor on ${token}.${method}: the resolved ` +\n 'instance has no callable method with that name.',\n );\n }\n}\n\n/** .methods configured for a token that resolved to a non-object value. */\nexport class NonObjectMethodTargetError extends TestingError {\n constructor(token: string) {\n super(\n `.methods() is configured for ${token}, but that token resolved to a ` +\n 'non-object value. Method descriptors apply only to IoC-managed instances.',\n );\n }\n}\n\n/** A string configuration key could not be resolved to a known IoC token. */\nexport class UnknownTokenKeyError extends TestingError {\n constructor(key: string, space: string, known: readonly string[]) {\n super(\n `Unknown ${space} key \"${key}\" — it does not match any token known to this ` +\n 'builder. Pass the class/token itself via a Map, or include the ' +\n 'registration through .use(...). Known tokens: ' +\n (known.length ? known.join(', ') : '(none)'),\n );\n }\n}\n\n/** Invalid descriptor construction (e.g. test.times(-1)). */\nexport class InvalidDescriptorError extends TestingError {}\n","/**\n * Lowercase `test.*` descriptors — immutable frozen values.\n *\n * Descriptors carry NO mutable state (no cursors, no counters, no histories);\n * all mutable invocation state lives in the built environment, so one\n * descriptor value is safe to share across builders and repeated builds.\n */\n\nimport { InvalidDescriptorError } from './errors';\n\nexport const DESCRIPTOR = Symbol.for('noego:testing:descriptor');\n\n/** A raw custom method wrapper: (original) => replacement. NOT auto-watched. */\nexport type RawMethodWrapper = (\n original: (...args: any[]) => any,\n) => (...args: any[]) => any;\n\nexport interface ReturnsDescriptor {\n readonly [DESCRIPTOR]: true;\n readonly kind: 'returns';\n readonly value: unknown;\n}\n\nexport interface ThrowsDescriptor {\n readonly [DESCRIPTOR]: true;\n readonly kind: 'throws';\n readonly error: unknown;\n}\n\nexport interface OriginalDescriptor {\n readonly [DESCRIPTOR]: true;\n readonly kind: 'original';\n}\n\nexport interface CallsDescriptor {\n readonly [DESCRIPTOR]: true;\n readonly kind: 'calls';\n readonly script: readonly BehaviorDescriptor[];\n}\n\nexport type BehaviorDescriptor =\n | ReturnsDescriptor\n | ThrowsDescriptor\n | OriginalDescriptor\n | CallsDescriptor;\n\nexport interface WatchDescriptor {\n readonly [DESCRIPTOR]: true;\n readonly kind: 'watch';\n readonly wrapper?: RawMethodWrapper;\n}\n\nexport interface ExpectationDescriptor {\n readonly [DESCRIPTOR]: true;\n readonly kind: 'expect';\n /** Exact required call count; 0 for never(). */\n readonly expected: number;\n /** Behavior used for allowed calls; undefined = original effective behavior. */\n readonly behavior?: BehaviorDescriptor;\n}\n\n/** Everything installable through .methods({...}). */\nexport type MethodDescriptor =\n | BehaviorDescriptor\n | WatchDescriptor\n | ExpectationDescriptor\n | RawMethodWrapper;\n\nexport function isDescriptor(value: unknown): value is Exclude<MethodDescriptor, RawMethodWrapper> {\n return typeof value === 'object' && value !== null && (value as any)[DESCRIPTOR] === true;\n}\n\nfunction frozen<T extends object>(value: T): T {\n return Object.freeze(value);\n}\n\nfunction assertBehavior(value: unknown, where: string): asserts value is BehaviorDescriptor {\n if (!isDescriptor(value) || !['returns', 'throws', 'original', 'calls'].includes((value as any).kind)) {\n throw new InvalidDescriptorError(\n `${where} requires a behavior descriptor (test.returns/throws/original/calls).`,\n );\n }\n}\n\nexport const test = {\n /** Return the supplied value when the method is called. Auto-watches. */\n returns(value: unknown): ReturnsDescriptor {\n return frozen({ [DESCRIPTOR]: true as const, kind: 'returns' as const, value });\n },\n\n /** Throw/reject with the supplied error. Auto-watches. */\n throws(error: unknown): ThrowsDescriptor {\n return frozen({ [DESCRIPTOR]: true as const, kind: 'throws' as const, error });\n },\n\n /** Invoke the original effective method. Auto-watches. */\n original(): OriginalDescriptor {\n return frozen({ [DESCRIPTOR]: true as const, kind: 'original' as const });\n },\n\n /**\n * Per-invocation behavior script: call 1 uses entry 1, and so on. A call\n * after exhaustion fails immediately. Unused entries do not fail verification.\n */\n calls(script: readonly BehaviorDescriptor[]): CallsDescriptor {\n if (!Array.isArray(script)) {\n throw new InvalidDescriptorError('test.calls() requires an array of behavior descriptors.');\n }\n script.forEach((entry, i) => assertBehavior(entry, `test.calls() entry #${i + 1}`));\n return frozen({ [DESCRIPTOR]: true as const, kind: 'calls' as const, script: Object.freeze([...script]) });\n },\n\n /**\n * Keep original behavior and record calls. With a raw wrapper argument, the\n * wrapper's behavior runs and is recorded.\n */\n watch(wrapper?: RawMethodWrapper): WatchDescriptor {\n if (wrapper !== undefined && typeof wrapper !== 'function') {\n throw new InvalidDescriptorError('test.watch() accepts only a raw wrapper function.');\n }\n return frozen({ [DESCRIPTOR]: true as const, kind: 'watch' as const, wrapper });\n },\n\n /** Require exactly one call; with no behavior, the original runs. */\n once(behavior?: BehaviorDescriptor): ExpectationDescriptor {\n if (behavior !== undefined) assertBehavior(behavior, 'test.once()');\n return frozen({ [DESCRIPTOR]: true as const, kind: 'expect' as const, expected: 1, behavior });\n },\n\n /** Require exactly `count` calls; with no behavior, the original runs. */\n times(count: number, behavior?: BehaviorDescriptor): ExpectationDescriptor {\n if (!Number.isInteger(count) || count < 0) {\n throw new InvalidDescriptorError(\n `test.times() requires a non-negative integer count, received ${String(count)}.`,\n );\n }\n if (behavior !== undefined) assertBehavior(behavior, 'test.times()');\n return frozen({ [DESCRIPTOR]: true as const, kind: 'expect' as const, expected: count, behavior });\n },\n\n /** Require zero calls; the first invocation fails and skips the original. */\n never(): ExpectationDescriptor {\n return frozen({ [DESCRIPTOR]: true as const, kind: 'expect' as const, expected: 0 });\n },\n\n /** Read the recorded history for a watched method in one environment. */\n inspect(environment: unknown, token: unknown, method: string): MethodInspection {\n const registry = (environment as any)?.[ENV_REGISTRY];\n if (!registry) {\n throw new InvalidDescriptorError(\n 'test.inspect() requires a built @noego/testing environment as its first argument.',\n );\n }\n return registry.inspect(token, method);\n },\n};\n\nObject.freeze(test);\n\n/** Symbol under which a built environment exposes its watch registry. */\nexport const ENV_REGISTRY = Symbol.for('noego:testing:env-registry');\n\nexport interface RecordedCall {\n /** 1-based invocation index in this environment. */\n readonly index: number;\n readonly args: readonly unknown[];\n /** Present once the call returned (resolved value for async methods). */\n readonly result?: unknown;\n /** Present once the call threw/rejected. */\n readonly error?: unknown;\n /** True while an async outcome is still pending. */\n readonly pending: boolean;\n readonly timestamp: number;\n}\n\nexport interface MethodInspection {\n readonly count: number;\n readonly calls: readonly RecordedCall[];\n}\n","/**\n * Environment-owned method behavior/observation runtime.\n *\n * All mutable state (histories, expectation counters, calls-script cursors)\n * lives here, created fresh at every build(). Descriptors stay immutable.\n */\n\nimport {\n type BehaviorDescriptor,\n type ExpectationDescriptor,\n type MethodDescriptor,\n type MethodInspection,\n type RawMethodWrapper,\n type RecordedCall,\n isDescriptor,\n} from './descriptors';\nimport {\n CallScriptExhaustedError,\n ExpectationOverflowError,\n MethodNotCallableError,\n NonObjectMethodTargetError,\n UnwatchedInspectionError,\n VerificationError,\n} from './errors';\n\nconst CONTEXT_WRAPPED = Symbol.for('ioc:context-wrapped');\nconst CONTEXT_OWNER = Symbol.for('ioc:context-owner');\n\nexport function tokenLabel(token: unknown): string {\n if (typeof token === 'function') return (token as { name?: string }).name || '[anonymous class]';\n if (typeof token === 'symbol') return String(token);\n return String(token);\n}\n\ninterface MutableCall {\n index: number;\n args: readonly unknown[];\n result?: unknown;\n error?: unknown;\n pending: boolean;\n timestamp: number;\n}\n\nexport class MethodState {\n readonly calls: MutableCall[] = [];\n /** Actual invocation count (includes the call currently executing). */\n actual = 0;\n /** Cursor into a test.calls() script. */\n scriptCursor = 0;\n\n constructor(\n readonly tokenName: string,\n readonly method: string,\n readonly descriptor: Exclude<MethodDescriptor, RawMethodWrapper>,\n ) {}\n\n get expectation(): ExpectationDescriptor | undefined {\n return this.descriptor.kind === 'expect' ? this.descriptor : undefined;\n }\n\n inspection(): MethodInspection {\n return {\n count: this.calls.length,\n calls: this.calls.map((c) => ({ ...c, args: [...c.args] }) as RecordedCall),\n };\n }\n}\n\n/** One method-config entry: how it was keyed, and its per-method descriptors. */\nexport interface MethodConfigEntry {\n /** Exact token (class/symbol/string) or a name string matched lazily. */\n readonly key: unknown;\n readonly byName: boolean;\n readonly methods: ReadonlyMap<string, MethodDescriptor>;\n}\n\nexport class WatchRegistry {\n /** entry-identity → method → state. Entries share states across instances. */\n private readonly states = new Map<unknown, Map<string, MethodState>>();\n private readonly nameIndex = new Map<string, unknown>();\n private readonly entries: MethodConfigEntry[] = [];\n\n addEntry(entry: MethodConfigEntry): void {\n this.entries.push(entry);\n const byMethod = new Map<string, MethodState>();\n for (const [method, descriptor] of entry.methods) {\n if (typeof descriptor === 'function') continue; // raw wrapper: unwatched, no state\n byMethod.set(method, new MethodState(this.entryLabel(entry), method, descriptor));\n }\n this.states.set(entry.key, byMethod);\n if (entry.byName) this.nameIndex.set(entry.key as string, entry.key);\n else if (typeof entry.key === 'function' && (entry.key as any).name) {\n this.nameIndex.set((entry.key as any).name, entry.key);\n }\n }\n\n private entryLabel(entry: MethodConfigEntry): string {\n return entry.byName ? String(entry.key) : tokenLabel(entry.key);\n }\n\n /** Entries applying to a resolving token (exact identity or name match). */\n matchEntries(token: unknown): MethodConfigEntry[] {\n const name = typeof token === 'function' ? (token as any).name : typeof token === 'string' ? token : undefined;\n return this.entries.filter(\n (entry) => entry.key === token || (entry.byName && name !== undefined && entry.key === name),\n );\n }\n\n state(entryKey: unknown, method: string): MethodState | undefined {\n return this.states.get(entryKey)?.get(method);\n }\n\n inspect(token: unknown, method: string): MethodInspection {\n const label = tokenLabel(token);\n const keys: unknown[] = [token];\n const name = typeof token === 'function' ? (token as any).name : typeof token === 'string' ? token : undefined;\n if (name !== undefined && this.nameIndex.has(name)) keys.push(this.nameIndex.get(name));\n for (const key of keys) {\n const state = this.states.get(key)?.get(method);\n if (state) return state.inspection();\n }\n throw new UnwatchedInspectionError(label, method);\n }\n\n /** Repeatable snapshot check of all exact expectations. */\n verify(): void {\n const failures: { token: string; method: string; expected: number; actual: number }[] = [];\n for (const byMethod of this.states.values()) {\n for (const state of byMethod.values()) {\n const expectation = state.expectation;\n if (!expectation) continue;\n if (state.actual !== expectation.expected) {\n failures.push({\n token: state.tokenName,\n method: state.method,\n expected: expectation.expected,\n actual: state.actual,\n });\n }\n }\n }\n if (failures.length) throw new VerificationError(failures);\n }\n}\n\nfunction isPromiseLike(value: unknown): value is Promise<unknown> {\n return !!value && typeof (value as any).then === 'function';\n}\n\nfunction recordOutcome(call: MutableCall, outcome: unknown, threw: boolean): unknown {\n if (!threw && isPromiseLike(outcome)) {\n call.pending = true;\n outcome.then(\n (value) => {\n call.result = value;\n call.pending = false;\n },\n (error) => {\n call.error = error;\n call.pending = false;\n },\n );\n return outcome;\n }\n call.pending = false;\n if (threw) call.error = outcome;\n else call.result = outcome;\n return outcome;\n}\n\n/**\n * Build the ioc instance decorator for one environment.\n *\n * Identity-stable (WeakMap), preserves sync/async call shape and `this`\n * binding, forwards the ioc context symbols by delegating to the underlying\n * (context-wrapped) target for everything unconfigured.\n */\nexport function createMethodDecorator(registry: WatchRegistry): (instance: any, token: any) => any {\n const wrappers = new WeakMap<object, any>();\n\n return function decorate(instance: any, token: any): any {\n const entries = registry.matchEntries(token);\n if (entries.length === 0) return instance;\n\n if (instance === null || (typeof instance !== 'object' && typeof instance !== 'function')) {\n throw new NonObjectMethodTargetError(tokenLabel(token));\n }\n\n if (wrappers.has(instance)) return wrappers.get(instance);\n\n // Effective per-method config: entries merge in write order, LWW per method\n const effective = new Map<string, { entryKey: unknown; descriptor: MethodDescriptor }>();\n for (const entry of entries) {\n for (const [method, descriptor] of entry.methods) {\n effective.set(method, { entryKey: entry.key, descriptor });\n }\n }\n\n // Validate configured methods exist and are callable\n for (const method of effective.keys()) {\n if (typeof instance[method] !== 'function') {\n throw new MethodNotCallableError(tokenLabel(token), method);\n }\n }\n\n const methodCache = new Map<string, (...args: any[]) => any>();\n\n const proxy = new Proxy(instance, {\n get(target, prop, receiver) {\n if (typeof prop === 'string' && effective.has(prop)) {\n let wrapped = methodCache.get(prop);\n if (!wrapped) {\n const { entryKey, descriptor } = effective.get(prop)!;\n wrapped = buildMethodWrapper(target, prop, descriptor, registry.state(entryKey, prop));\n methodCache.set(prop, wrapped);\n }\n return wrapped;\n }\n // Everything else (including ioc context symbols) delegates to the\n // underlying — usually context-wrapped — target.\n if (prop === CONTEXT_WRAPPED || prop === CONTEXT_OWNER) return (target as any)[prop];\n return Reflect.get(target, prop, receiver);\n },\n });\n\n wrappers.set(instance, proxy);\n return proxy;\n };\n}\n\nfunction buildMethodWrapper(\n target: any,\n method: string,\n descriptor: MethodDescriptor,\n state: MethodState | undefined,\n): (...args: any[]) => any {\n // The original effective method, read through the underlying target so the\n // ioc context proxy still owns context entry for real invocations.\n const callOriginal = (self: any, args: any[]) => {\n const fn = target[method];\n return fn.apply(self === undefined ? target : self, args);\n };\n\n // Raw custom wrapper: installed as-is, NOT watched, no recording.\n if (typeof descriptor === 'function') {\n const replacement = descriptor((...args: any[]) => callOriginal(target, args));\n return function (this: any, ...args: any[]) {\n return replacement.apply(this, args);\n };\n }\n\n if (!isDescriptor(descriptor)) {\n // Should be unreachable: builder validates descriptors on write.\n throw new TypeError(`Invalid method descriptor for ${method}`);\n }\n\n return function (this: any, ...args: any[]) {\n if (!state) throw new TypeError(`Missing method state for ${method}`);\n\n const expectation = state.expectation;\n const attempted = state.actual + 1;\n\n // Overflow fails immediately and never runs behavior/original\n if (expectation && attempted > expectation.expected) {\n state.actual = attempted;\n throw new ExpectationOverflowError(state.tokenName, method, expectation.expected, attempted);\n }\n\n state.actual = attempted;\n const call: MutableCall = {\n index: attempted,\n args: [...args],\n pending: false,\n timestamp: Date.now(),\n };\n state.calls.push(call);\n\n // Effective behavior for this invocation\n let behavior: BehaviorDescriptor | 'original-effective' | RawMethodWrapper;\n const base =\n descriptor.kind === 'expect'\n ? (descriptor.behavior ?? 'original-effective')\n : descriptor.kind === 'watch'\n ? (descriptor.wrapper ?? 'original-effective')\n : descriptor;\n\n if (base !== 'original-effective' && typeof base !== 'function' && base !== undefined && base.kind === 'calls') {\n if (state.scriptCursor >= base.script.length) {\n throw new CallScriptExhaustedError(state.tokenName, method, base.script.length, attempted);\n }\n behavior = base.script[state.scriptCursor]!;\n state.scriptCursor += 1;\n } else {\n behavior = base;\n }\n\n try {\n let outcome: unknown;\n if (behavior === 'original-effective') {\n outcome = callOriginal(this, args);\n } else if (typeof behavior === 'function') {\n // test.watch(rawWrapper): wrapper behavior runs and is recorded\n outcome = behavior((...inner: any[]) => callOriginal(this, inner)).apply(this, args);\n } else if (behavior.kind === 'returns') {\n outcome = behavior.value;\n } else if (behavior.kind === 'throws') {\n throw behavior.error;\n } else if (behavior.kind === 'original') {\n outcome = callOriginal(this, args);\n } else {\n throw new TypeError(`Nested test.calls() scripts are not supported (${method}).`);\n }\n return recordOutcome(call, outcome, false);\n } catch (error) {\n recordOutcome(call, error, true);\n throw error;\n }\n };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/builder.ts","../src/errors.ts","../src/descriptors.ts","../src/method_state.ts"],"sourcesContent":["/**\n * `testIoc` — the canonical shared real-IoC test composition builder.\n *\n * Persistent immutable: every fluent call returns a new derived builder\n * sharing the ordered write log structurally. Non-conflicting writes are\n * order-insensitive; the last write to the same effective identity wins on\n * that derived branch. `.build()` is non-consuming and creates fresh runtime,\n * watch, and expectation state (spec 15, PBR-01..15).\n */\n\nimport {\n createContainer,\n flattenModule,\n LoadAs,\n SCOPED_CONTAINER,\n type ApplicationModule,\n type IContainer,\n} from '@noego/ioc';\n\nimport { ENV_REGISTRY, isDescriptor, type MethodDescriptor } from './descriptors';\nimport { createMethodDecorator, WatchRegistry, tokenLabel } from './method_state';\nimport { InvalidDescriptorError, MissingIocSeamError, UnknownTokenKeyError, AmbiguousNameKeyError,\n} from './errors';\n\nconst COMPONENT_OPTIONS_KEY = Symbol.for('ioc:component:options');\n\ntype Token = unknown;\ntype ClassLike = new (...args: any[]) => any;\n\n/** Config maps accept plain objects (string keys) or Maps (exact tokens). */\n/**\n * Composition input (spec 04 §2). The CANONICAL entry form is an array of\n * tuples — token-first, exact identity:\n *\n * .classes([[ProjectRepository, MemoryProjectRepository]])\n *\n * A `ReadonlyMap<Token, V>` is equivalent (token-keyed). A name-keyed\n * `Record<string, V>` remains accepted as COMPATIBILITY input: string keys\n * resolve against known token display names, fail on unknown names, and\n * fail on ambiguous names (two tokens sharing one display name never\n * collapse into one entry — tokens are identities, names are labels).\n */\nexport type ConfigMap<V> =\n | ReadonlyArray<readonly [Token, V]>\n | ReadonlyMap<Token, V>\n | Record<string, V>;\n\nexport type MethodsConfig = ConfigMap<Record<string, MethodDescriptor> | ReadonlyMap<string, MethodDescriptor>>;\n\nexport type UseInput = ApplicationModule | TestIocBuilder;\n\ntype Write =\n | { op: 'use'; module: ApplicationModule }\n | { op: 'classes'; key: Token; byName: boolean; implementation: ClassLike }\n | { op: 'functions'; key: Token; byName: boolean; factory: (...args: any[]) => any }\n | { op: 'values'; key: Token; byName: boolean; value: unknown }\n | { op: 'methods'; key: Token; byName: boolean; methods: ReadonlyMap<string, MethodDescriptor> };\n\nexport interface TestEnvironment {\n readonly root: IContainer;\n get<T>(token: unknown, params?: any[]): Promise<T> | T;\n instance<T>(cls: new (...args: any[]) => T, params?: any[]): Promise<T> | T;\n extend(): IContainer;\n verify(): Promise<void>;\n dispose(): Promise<void>;\n readonly [ENV_REGISTRY]: WatchRegistry;\n}\n\nfunction* configEntries<V>(config: ConfigMap<V>): Iterable<[Token, boolean, V]> {\n if (Array.isArray(config)) {\n // Canonical tuple form: exact token identity, never name resolution.\n for (const entry of config as ReadonlyArray<readonly [Token, V]>) {\n if (!Array.isArray(entry) || entry.length !== 2) {\n throw new InvalidDescriptorError(\n 'canonical composition entries are [token, value] tuples, e.g. .classes([[Token, Impl]])',\n );\n }\n yield [entry[0], false, entry[1]];\n }\n } else if (config instanceof Map) {\n for (const [key, value] of config) yield [key, false, value];\n } else {\n for (const key of [...Object.getOwnPropertyNames(config), ...Object.getOwnPropertySymbols(config)]) {\n yield [key, typeof key === 'string', (config as any)[key]];\n }\n }\n}\n\nfunction componentScope(cls: any): LoadAs | undefined {\n const options =\n typeof Reflect !== 'undefined' && (Reflect as any).getMetadata\n ? (Reflect as any).getMetadata(COMPONENT_OPTIONS_KEY, cls)\n : undefined;\n return options?.scope;\n}\n\nfunction lifetimeToLoadAs(lifetime: string): LoadAs {\n if (lifetime === 'singleton') return LoadAs.Singleton;\n if (lifetime === 'scoped') return LoadAs.Scoped;\n return LoadAs.Transient;\n}\n\nexport class TestIocBuilder {\n private constructor(private readonly log: readonly Write[]) {\n Object.freeze(this);\n }\n\n /** @internal */\n static create(inputs: readonly UseInput[]): TestIocBuilder {\n return new TestIocBuilder([]).useAll(inputs);\n }\n\n /** @internal — read by .use(builderPreset) */\n get writes(): readonly Write[] {\n return this.log;\n }\n\n private derive(writes: Write[]): TestIocBuilder {\n return new TestIocBuilder([...this.log, ...writes]);\n }\n\n private useAll(inputs: readonly UseInput[]): TestIocBuilder {\n let builder: TestIocBuilder = this;\n for (const input of inputs) builder = builder.use(input);\n return builder;\n }\n\n /** Apply a reusable composition preset: an ApplicationModule or a builder. */\n use(preset: UseInput): TestIocBuilder {\n if (preset instanceof TestIocBuilder) {\n return this.derive([...preset.writes]);\n }\n return this.derive([{ op: 'use', module: preset }]);\n }\n\n /** Replace the implementation for IoC class tokens in the built environment. */\n classes(config: ConfigMap<ClassLike>): TestIocBuilder {\n const writes: Write[] = [];\n for (const [key, byName, implementation] of configEntries(config)) {\n if (typeof implementation !== 'function' || !implementation.prototype) {\n throw new InvalidDescriptorError(\n `.classes() value for \"${tokenLabel(key)}\" must be a class constructor.`,\n );\n }\n writes.push({ op: 'classes', key, byName, implementation });\n }\n return this.derive(writes);\n }\n\n /** Replace IoC factory/provider registrations. */\n functions(config: ConfigMap<(...args: any[]) => any>): TestIocBuilder {\n const writes: Write[] = [];\n for (const [key, byName, factory] of configEntries(config)) {\n if (typeof factory !== 'function') {\n throw new InvalidDescriptorError(`.functions() value for \"${tokenLabel(key)}\" must be a function.`);\n }\n writes.push({ op: 'functions', key, byName, factory });\n }\n return this.derive(writes);\n }\n\n /** Provide/replace IoC value registrations. */\n values(config: ConfigMap<unknown>): TestIocBuilder {\n const writes: Write[] = [];\n for (const [key, byName, value] of configEntries(config)) {\n writes.push({ op: 'values', key, byName, value });\n }\n return this.derive(writes);\n }\n\n /** Install method behavior/observation descriptors on IoC-managed instances. */\n methods(config: MethodsConfig): TestIocBuilder {\n const writes: Write[] = [];\n for (const [key, byName, methodsInput] of configEntries(config)) {\n const methods = new Map<string, MethodDescriptor>();\n const entries =\n methodsInput instanceof Map\n ? methodsInput.entries()\n : Object.entries(methodsInput as Record<string, MethodDescriptor>);\n for (const [name, descriptor] of entries) {\n if (typeof descriptor !== 'function' && !isDescriptor(descriptor)) {\n throw new InvalidDescriptorError(\n `.methods() entry ${tokenLabel(key)}.${name} must be a test.* descriptor or a raw wrapper function.`,\n );\n }\n methods.set(name, descriptor);\n }\n writes.push({ op: 'methods', key, byName, methods });\n }\n return this.derive(writes);\n }\n\n /** Materialize a fresh, isolated real-IoC environment. Non-consuming. */\n async build(): Promise<TestEnvironment> {\n const root = createContainer();\n if (typeof (root as any).setInstanceDecorator !== 'function') {\n throw new MissingIocSeamError();\n }\n\n // ---- Materialize the ordered log: last write wins per effective identity\n // Known tokens (for resolving string keys of classes/functions/values)\n const knownByName = new Map<string, Token>();\n /** Display names claimed by more than one distinct token (spec 04 §5/§17). */\n const ambiguousNames = new Set<string>();\n const knownLifetimes = new Map<Token, LoadAs>();\n const note = (token: Token, loadAs: LoadAs | undefined) => {\n const name =\n typeof token === 'function' && (token as any).name\n ? (token as any).name as string\n : typeof token === 'string'\n ? token\n : null;\n if (name !== null) {\n const existing = knownByName.get(name);\n if (existing !== undefined && existing !== token) ambiguousNames.add(name);\n knownByName.set(name, token);\n }\n if (loadAs !== undefined) knownLifetimes.set(token, loadAs);\n };\n\n type Effective =\n | { space: 'class'; token: Token; implementation: ClassLike; loadAs?: LoadAs }\n | { space: 'factory'; token: Token; factory: (...args: any[]) => any; loadAs?: LoadAs; deps?: Token[] }\n | { space: 'value'; token: Token; value: unknown };\n\n // ordered map: identity → latest effective write (Map preserves first-write\n // position which is fine — later writes replace content, LWW)\n const effective = new Map<Token, Effective>();\n const methodWrites: { key: Token; byName: boolean; methods: Map<string, MethodDescriptor> }[] = [];\n\n const resolveKey = (key: Token, byName: boolean, space: string): Token => {\n if (!byName) return key;\n if (ambiguousNames.has(key as string)) {\n throw new AmbiguousNameKeyError(key as string, space);\n }\n const known = knownByName.get(key as string);\n if (known !== undefined) return known;\n if (space === 'values' || space === 'functions') return key; // string tokens are first-class\n throw new UnknownTokenKeyError(key as string, space, [...knownByName.keys()]);\n };\n\n for (const write of this.log) {\n switch (write.op) {\n case 'use': {\n for (const reg of flattenModule(write.module)) {\n const loadAs = lifetimeToLoadAs(reg.lifetime);\n note(reg.token, loadAs);\n if (reg.kind === 'class') {\n note(reg.implementation as Token, loadAs);\n effective.set(reg.token, {\n space: 'class',\n token: reg.token,\n implementation: reg.implementation as ClassLike,\n loadAs,\n });\n knownLifetimes.set(reg.token, loadAs);\n knownLifetimes.set(reg.implementation as Token, loadAs);\n } else if (reg.kind === 'factory') {\n effective.set(reg.token, {\n space: 'factory',\n token: reg.token,\n factory: reg.implementation as (...args: any[]) => any,\n loadAs,\n deps: [...reg.dependencies],\n });\n } else {\n effective.set(reg.token, { space: 'value', token: reg.token, value: reg.implementation });\n knownLifetimes.set(reg.token, LoadAs.Singleton);\n }\n }\n break;\n }\n case 'classes': {\n const token = resolveKey(write.key, write.byName, 'classes');\n note(write.implementation, undefined);\n if (!write.byName) note(token, undefined);\n effective.set(token, {\n space: 'class',\n token,\n implementation: write.implementation,\n // Lifetime priority: the replacement's own @Component scope, then\n // the lifetime the composition already knows for the token, then\n // the TOKEN class's declared @Component scope — a plain stub\n // class replacing a Singleton-scoped production service must not\n // silently degrade to Transient (captive-lifetime validation\n // would reject the production dependents).\n loadAs:\n componentScope(write.implementation)\n ?? knownLifetimes.get(token)\n ?? componentScope(token),\n });\n break;\n }\n case 'functions': {\n const token = resolveKey(write.key, write.byName, 'functions');\n const prior = effective.get(token);\n effective.set(token, {\n space: 'factory',\n token,\n factory: write.factory,\n // preserve configured lifetime unless the scenario overrides it\n loadAs: prior && prior.space === 'factory' ? prior.loadAs : knownLifetimes.get(token),\n deps: prior && prior.space === 'factory' ? prior.deps : undefined,\n });\n note(token, undefined);\n break;\n }\n case 'values': {\n const token = resolveKey(write.key, write.byName, 'values');\n effective.set(token, { space: 'value', token, value: write.value });\n note(token, LoadAs.Singleton);\n break;\n }\n case 'methods': {\n if (write.byName && ambiguousNames.has(write.key as string)) {\n throw new AmbiguousNameKeyError(write.key as string, 'methods');\n }\n const token = write.byName && knownByName.has(write.key as string)\n ? knownByName.get(write.key as string)!\n : write.key;\n methodWrites.push({\n key: token,\n byName: write.byName && !knownByName.has(write.key as string),\n methods: new Map(write.methods),\n });\n break;\n }\n }\n }\n\n // ---- Apply effective registrations to the fresh root\n for (const entry of effective.values()) {\n if (entry.space === 'class') {\n this.registerClassBinding(root, entry.token, entry.implementation, entry.loadAs);\n } else if (entry.space === 'factory') {\n root.registerFunction(entry.token, entry.factory, {\n loadAs: entry.loadAs,\n param: entry.deps,\n } as any);\n } else {\n const value = entry.value;\n root.registerFunction(entry.token, () => value, { loadAs: LoadAs.Singleton } as any);\n }\n }\n\n // ---- Fresh watch/expectation state + the ioc decoration seam\n const registry = new WatchRegistry();\n // merge method writes: LWW per (entry key, method), deep-merge per token\n const mergedMethods = new Map<Token, { byName: boolean; methods: Map<string, MethodDescriptor> }>();\n for (const write of methodWrites) {\n const existing = mergedMethods.get(write.key);\n if (existing) {\n for (const [name, descriptor] of write.methods) existing.methods.set(name, descriptor);\n } else {\n mergedMethods.set(write.key, { byName: write.byName, methods: new Map(write.methods) });\n }\n }\n for (const [key, { byName, methods }] of mergedMethods) {\n registry.addEntry({ key, byName, methods });\n }\n (root as any).setInstanceDecorator(createMethodDecorator(registry));\n\n const env: TestEnvironment = {\n root: root as IContainer,\n get: (token, params) => root.get(token as any, params),\n instance: (cls, params) => root.instance(cls, params),\n extend: () => root.extend() as IContainer,\n verify: async () => registry.verify(),\n dispose: () => root.dispose(),\n [ENV_REGISTRY]: registry,\n };\n return env;\n }\n\n /**\n * Register a class binding. When token === implementation this is a plain\n * class registration. Otherwise an alias factory resolves the implementation\n * through real IoC, mirroring the implementation's effective lifetime so\n * lifetime validation (captive-lifetime checks) stays honest.\n */\n private registerClassBinding(\n root: IContainer,\n token: Token,\n implementation: ClassLike,\n configuredLoadAs?: LoadAs,\n ): void {\n const loadAs = configuredLoadAs ?? componentScope(implementation) ?? LoadAs.Transient;\n root.registerClass(implementation as any, configuredLoadAs !== undefined ? ({ loadAs } as any) : undefined);\n if (token === implementation) return;\n\n if (loadAs === LoadAs.Singleton) {\n root.registerFunction(token, () => root.get(implementation as any), {\n loadAs: LoadAs.Singleton,\n } as any);\n } else {\n root.registerFunction(token, (scope: IContainer) => scope.get(implementation as any), {\n loadAs,\n param: [SCOPED_CONTAINER],\n } as any);\n }\n }\n}\n\n/** Create a persistent immutable real-IoC test composition builder. */\nexport function testIoc(...inputs: UseInput[]): TestIocBuilder {\n return TestIocBuilder.create(inputs);\n}\n","/**\n * Diagnostics are first-class: every error names the real token/method and\n * what was expected vs what happened, never only internal wrapper machinery.\n */\n\nexport class TestingError extends Error {\n constructor(message: string) {\n super(message);\n this.name = new.target.name;\n }\n}\n\n/** Thrown at build() when the installed @noego/ioc lacks the decoration seam. */\nexport class MissingIocSeamError extends TestingError {\n constructor() {\n super(\n '@noego/testing requires an @noego/ioc version that provides ' +\n 'Container.setInstanceDecorator (>= 0.5.x with the instance-decoration seam). ' +\n 'Upgrade @noego/ioc.',\n );\n }\n}\n\n/** test.inspect() on a method that is not watched in this environment. */\nexport class UnwatchedInspectionError extends TestingError {\n constructor(token: string, method: string) {\n super(\n `Method \"${method}\" on ${token} is not watched in this environment. ` +\n 'Only watched methods are inspectable — install test.watch() or any ' +\n 'test.* behavior/expectation descriptor for it.',\n );\n }\n}\n\n/** A call arrived after a test.calls([...]) script was fully consumed. */\nexport class CallScriptExhaustedError extends TestingError {\n constructor(token: string, method: string, scriptLength: number, callIndex: number) {\n super(\n `Call #${callIndex} to ${token}.${method} exceeds its test.calls() script ` +\n `of ${scriptLength} ${scriptLength === 1 ? 'entry' : 'entries'}.`,\n );\n }\n}\n\n/** An exact expectation (once/times/never) was exceeded at call time. */\nexport class ExpectationOverflowError extends TestingError {\n constructor(token: string, method: string, expected: number, attempted: number) {\n super(\n expected === 0\n ? `${token}.${method} was expected never to be called, but it was invoked.`\n : `${token}.${method} was expected exactly ${expected} ` +\n `${expected === 1 ? 'call' : 'calls'}, but call #${attempted} arrived.`,\n );\n }\n}\n\n/** Aggregated under-count failures reported by env.verify(). */\nexport class VerificationError extends TestingError {\n constructor(failures: readonly { token: string; method: string; expected: number; actual: number }[]) {\n super(\n 'Exact method expectations were not satisfied:\\n' +\n failures\n .map(\n (f) =>\n ` - ${f.token}.${f.method}: expected exactly ${f.expected} ` +\n `${f.expected === 1 ? 'call' : 'calls'}, observed ${f.actual}`,\n )\n .join('\\n'),\n );\n }\n}\n\n/** .methods configured for a token whose resolved value has no such callable method. */\nexport class MethodNotCallableError extends TestingError {\n constructor(token: string, method: string) {\n super(\n `Cannot install a test.* descriptor on ${token}.${method}: the resolved ` +\n 'instance has no callable method with that name.',\n );\n }\n}\n\n/** .methods configured for a token that resolved to a non-object value. */\nexport class NonObjectMethodTargetError extends TestingError {\n constructor(token: string) {\n super(\n `.methods() is configured for ${token}, but that token resolved to a ` +\n 'non-object value. Method descriptors apply only to IoC-managed instances.',\n );\n }\n}\n\n/** A string configuration key could not be resolved to a known IoC token. */\nexport class UnknownTokenKeyError extends TestingError {\n constructor(key: string, space: string, known: readonly string[]) {\n super(\n `Unknown ${space} key \"${key}\" — it does not match any token known to this ` +\n 'builder. Pass the class/token itself via a Map, or include the ' +\n 'registration through .use(...). Known tokens: ' +\n (known.length ? known.join(', ') : '(none)'),\n );\n }\n}\n\n/** Invalid descriptor construction (e.g. test.times(-1)). */\nexport class InvalidDescriptorError extends TestingError {}\n\n/**\n * A name-keyed compatibility entry referred to a display name claimed by\n * two or more distinct tokens. Tokens are identities; names are labels —\n * pass the exact token in canonical tuple form instead (spec 04 §5).\n */\nexport class AmbiguousNameKeyError extends TestingError {\n constructor(name: string, space: string) {\n super(\n `.${space}() key \"${name}\" is ambiguous: multiple distinct tokens share that display name. ` +\n `Use the canonical tuple form with the exact token: .${space}([[TheToken, ...]]).`,\n );\n this.name = 'AmbiguousNameKeyError';\n }\n}\n\n\n/**\n * A .methods() class token never matched any constructed instance while a\n * DIFFERENT class with the same display name did construct — the configured\n * behavior silently did not apply. Usual cause: two module registries loaded\n * the same source file (e.g. a vitest test file's import vs a framework\n * harness's native import of the production graph); fix by routing the\n * harness's imports through the caller's registry (testApp:\n * `.importer((p) => import(p))`) or by passing the token the graph actually\n * uses.\n */\nexport class TokenIdentitySplitError extends TestingError {\n constructor(names: readonly string[]) {\n super(\n `token identity split: .methods() entr${names.length === 1 ? 'y' : 'ies'} for ` +\n `${names.map((name) => `\"${name}\"`).join(', ')} never matched a constructed instance, ` +\n `but a DIFFERENT class with the same name was constructed. Two module registries ` +\n `have loaded the same class file (test-file import vs harness graph import) — the ` +\n `configured behavior did not apply. Route the harness's imports through your ` +\n `registry (e.g. testApp .importer((p) => import(p))) or pass the exact token the graph uses.`,\n );\n this.name = 'TokenIdentitySplitError';\n }\n}\n","/**\n * Lowercase `test.*` descriptors — immutable frozen values.\n *\n * Descriptors carry NO mutable state (no cursors, no counters, no histories);\n * all mutable invocation state lives in the built environment, so one\n * descriptor value is safe to share across builders and repeated builds.\n */\n\nimport { InvalidDescriptorError } from './errors';\n\nexport const DESCRIPTOR = Symbol.for('noego:testing:descriptor');\n\n/** A raw custom method wrapper: (original) => replacement. NOT auto-watched. */\nexport type RawMethodWrapper = (\n original: (...args: any[]) => any,\n) => (...args: any[]) => any;\n\nexport interface ReturnsDescriptor {\n readonly [DESCRIPTOR]: true;\n readonly kind: 'returns';\n readonly value: unknown;\n}\n\nexport interface ThrowsDescriptor {\n readonly [DESCRIPTOR]: true;\n readonly kind: 'throws';\n readonly error: unknown;\n}\n\nexport interface OriginalDescriptor {\n readonly [DESCRIPTOR]: true;\n readonly kind: 'original';\n}\n\nexport interface CallsDescriptor {\n readonly [DESCRIPTOR]: true;\n readonly kind: 'calls';\n readonly script: readonly BehaviorDescriptor[];\n}\n\nexport type BehaviorDescriptor =\n | ReturnsDescriptor\n | ThrowsDescriptor\n | OriginalDescriptor\n | CallsDescriptor;\n\nexport interface WatchDescriptor {\n readonly [DESCRIPTOR]: true;\n readonly kind: 'watch';\n readonly wrapper?: RawMethodWrapper;\n}\n\nexport interface ExpectationDescriptor {\n readonly [DESCRIPTOR]: true;\n readonly kind: 'expect';\n /** Exact required call count; 0 for never(). */\n readonly expected: number;\n /** Behavior used for allowed calls; undefined = original effective behavior. */\n readonly behavior?: BehaviorDescriptor;\n}\n\n/** Everything installable through .methods({...}). */\nexport type MethodDescriptor =\n | BehaviorDescriptor\n | WatchDescriptor\n | ExpectationDescriptor\n | RawMethodWrapper;\n\nexport function isDescriptor(value: unknown): value is Exclude<MethodDescriptor, RawMethodWrapper> {\n return typeof value === 'object' && value !== null && (value as any)[DESCRIPTOR] === true;\n}\n\nfunction frozen<T extends object>(value: T): T {\n return Object.freeze(value);\n}\n\nfunction assertBehavior(value: unknown, where: string): asserts value is BehaviorDescriptor {\n if (!isDescriptor(value) || !['returns', 'throws', 'original', 'calls'].includes((value as any).kind)) {\n throw new InvalidDescriptorError(\n `${where} requires a behavior descriptor (test.returns/throws/original/calls).`,\n );\n }\n}\n\nexport const test = {\n /** Return the supplied value when the method is called. Auto-watches. */\n returns(value: unknown): ReturnsDescriptor {\n return frozen({ [DESCRIPTOR]: true as const, kind: 'returns' as const, value });\n },\n\n /** Throw/reject with the supplied error. Auto-watches. */\n throws(error: unknown): ThrowsDescriptor {\n return frozen({ [DESCRIPTOR]: true as const, kind: 'throws' as const, error });\n },\n\n /** Invoke the original effective method. Auto-watches. */\n original(): OriginalDescriptor {\n return frozen({ [DESCRIPTOR]: true as const, kind: 'original' as const });\n },\n\n /**\n * Per-invocation behavior script: call 1 uses entry 1, and so on. A call\n * after exhaustion fails immediately. Unused entries do not fail verification.\n */\n calls(script: readonly BehaviorDescriptor[]): CallsDescriptor {\n if (!Array.isArray(script)) {\n throw new InvalidDescriptorError('test.calls() requires an array of behavior descriptors.');\n }\n script.forEach((entry, i) => assertBehavior(entry, `test.calls() entry #${i + 1}`));\n return frozen({ [DESCRIPTOR]: true as const, kind: 'calls' as const, script: Object.freeze([...script]) });\n },\n\n /**\n * Keep original behavior and record calls. With a raw wrapper argument, the\n * wrapper's behavior runs and is recorded.\n */\n watch(wrapper?: RawMethodWrapper): WatchDescriptor {\n if (wrapper !== undefined && typeof wrapper !== 'function') {\n throw new InvalidDescriptorError('test.watch() accepts only a raw wrapper function.');\n }\n return frozen({ [DESCRIPTOR]: true as const, kind: 'watch' as const, wrapper });\n },\n\n /** Require exactly one call; with no behavior, the original runs. */\n once(behavior?: BehaviorDescriptor): ExpectationDescriptor {\n if (behavior !== undefined) assertBehavior(behavior, 'test.once()');\n return frozen({ [DESCRIPTOR]: true as const, kind: 'expect' as const, expected: 1, behavior });\n },\n\n /** Require exactly `count` calls; with no behavior, the original runs. */\n times(count: number, behavior?: BehaviorDescriptor): ExpectationDescriptor {\n if (!Number.isInteger(count) || count < 0) {\n throw new InvalidDescriptorError(\n `test.times() requires a non-negative integer count, received ${String(count)}.`,\n );\n }\n if (behavior !== undefined) assertBehavior(behavior, 'test.times()');\n return frozen({ [DESCRIPTOR]: true as const, kind: 'expect' as const, expected: count, behavior });\n },\n\n /** Require zero calls; the first invocation fails and skips the original. */\n never(): ExpectationDescriptor {\n return frozen({ [DESCRIPTOR]: true as const, kind: 'expect' as const, expected: 0 });\n },\n\n /** Read the recorded history for a watched method in one environment. */\n inspect(environment: unknown, token: unknown, method: string): MethodInspection {\n const registry = (environment as any)?.[ENV_REGISTRY];\n if (!registry) {\n throw new InvalidDescriptorError(\n 'test.inspect() requires a built @noego/testing environment as its first argument.',\n );\n }\n return registry.inspect(token, method);\n },\n};\n\nObject.freeze(test);\n\n/** Symbol under which a built environment exposes its watch registry. */\nexport const ENV_REGISTRY = Symbol.for('noego:testing:env-registry');\n\nexport interface RecordedCall {\n /** 1-based invocation index in this environment. */\n readonly index: number;\n readonly args: readonly unknown[];\n /** Present once the call returned (resolved value for async methods). */\n readonly result?: unknown;\n /** Present once the call threw/rejected. */\n readonly error?: unknown;\n /** True while an async outcome is still pending. */\n readonly pending: boolean;\n readonly timestamp: number;\n}\n\nexport interface MethodInspection {\n readonly count: number;\n readonly calls: readonly RecordedCall[];\n}\n","/**\n * Environment-owned method behavior/observation runtime.\n *\n * All mutable state (histories, expectation counters, calls-script cursors)\n * lives here, created fresh at every build(). Descriptors stay immutable.\n */\n\nimport {\n type BehaviorDescriptor,\n type ExpectationDescriptor,\n type MethodDescriptor,\n type MethodInspection,\n type RawMethodWrapper,\n type RecordedCall,\n isDescriptor,\n} from './descriptors';\nimport {\n CallScriptExhaustedError,\n TokenIdentitySplitError,\n ExpectationOverflowError,\n MethodNotCallableError,\n NonObjectMethodTargetError,\n UnwatchedInspectionError,\n VerificationError,\n} from './errors';\n\nconst CONTEXT_WRAPPED = Symbol.for('ioc:context-wrapped');\nconst CONTEXT_OWNER = Symbol.for('ioc:context-owner');\n\nexport function tokenLabel(token: unknown): string {\n if (typeof token === 'function') return (token as { name?: string }).name || '[anonymous class]';\n if (typeof token === 'symbol') return String(token);\n return String(token);\n}\n\ninterface MutableCall {\n index: number;\n args: readonly unknown[];\n result?: unknown;\n error?: unknown;\n pending: boolean;\n timestamp: number;\n}\n\nexport class MethodState {\n readonly calls: MutableCall[] = [];\n /** Actual invocation count (includes the call currently executing). */\n actual = 0;\n /** Cursor into a test.calls() script. */\n scriptCursor = 0;\n\n constructor(\n readonly tokenName: string,\n readonly method: string,\n readonly descriptor: Exclude<MethodDescriptor, RawMethodWrapper>,\n ) {}\n\n get expectation(): ExpectationDescriptor | undefined {\n return this.descriptor.kind === 'expect' ? this.descriptor : undefined;\n }\n\n inspection(): MethodInspection {\n return {\n count: this.calls.length,\n calls: this.calls.map((c) => ({ ...c, args: [...c.args] }) as RecordedCall),\n };\n }\n}\n\n/** One method-config entry: how it was keyed, and its per-method descriptors. */\nexport interface MethodConfigEntry {\n /** Exact token (class/symbol/string) or a name string matched lazily. */\n readonly key: unknown;\n readonly byName: boolean;\n readonly methods: ReadonlyMap<string, MethodDescriptor>;\n}\n\nexport class WatchRegistry {\n /** entry-identity → method → state. Entries share states across instances. */\n private readonly states = new Map<unknown, Map<string, MethodState>>();\n private readonly nameIndex = new Map<string, unknown>();\n private readonly entries: MethodConfigEntry[] = [];\n /** Class-token entries that identity-matched at least one construction. */\n private readonly matchedEntryKeys = new Set<unknown>();\n /** Constructed class tokens that matched NO entry, by display name. */\n private readonly unmatchedConstructedByName = new Map<string, unknown>();\n\n addEntry(entry: MethodConfigEntry): void {\n this.entries.push(entry);\n const byMethod = new Map<string, MethodState>();\n for (const [method, descriptor] of entry.methods) {\n if (typeof descriptor === 'function') continue; // raw wrapper: unwatched, no state\n byMethod.set(method, new MethodState(this.entryLabel(entry), method, descriptor));\n }\n this.states.set(entry.key, byMethod);\n if (entry.byName) this.nameIndex.set(entry.key as string, entry.key);\n else if (typeof entry.key === 'function' && (entry.key as any).name) {\n this.nameIndex.set((entry.key as any).name, entry.key);\n }\n }\n\n private entryLabel(entry: MethodConfigEntry): string {\n return entry.byName ? String(entry.key) : tokenLabel(entry.key);\n }\n\n /** Entries applying to a resolving token (exact identity or name match). */\n matchEntries(token: unknown): MethodConfigEntry[] {\n const name = typeof token === 'function' ? (token as any).name : typeof token === 'string' ? token : undefined;\n const matched = this.entries.filter(\n (entry) => entry.key === token || (entry.byName && name !== undefined && entry.key === name),\n );\n // Split-registry tripwire bookkeeping (spec 04 §17 \"duplicate\n // human-readable token names without identity collision\"): remember which\n // class-token entries genuinely bound, and which constructions bound\n // nothing — verify() cross-references the two by display name.\n if (matched.length) {\n for (const entry of matched) this.matchedEntryKeys.add(entry.key);\n } else if (typeof token === 'function' && name) {\n this.unmatchedConstructedByName.set(name, token);\n }\n return matched;\n }\n\n state(entryKey: unknown, method: string): MethodState | undefined {\n return this.states.get(entryKey)?.get(method);\n }\n\n inspect(token: unknown, method: string): MethodInspection {\n const label = tokenLabel(token);\n const keys: unknown[] = [token];\n const name = typeof token === 'function' ? (token as any).name : typeof token === 'string' ? token : undefined;\n if (name !== undefined && this.nameIndex.has(name)) keys.push(this.nameIndex.get(name));\n for (const key of keys) {\n const state = this.states.get(key)?.get(method);\n if (state) return state.inspection();\n }\n throw new UnwatchedInspectionError(label, method);\n }\n\n /**\n * Class-token entries that never identity-matched a construction while a\n * DIFFERENT class with the same display name did construct. This is the\n * split-module-registry signature (a test-file class object vs the graph's\n * own load of the same file) — or two genuinely distinct same-named tokens\n * where the configured one never resolved. Either way the configured\n * behavior silently did not apply, which must be loud.\n */\n private identitySplits(): { name: string; entryToken: unknown }[] {\n const splits: { name: string; entryToken: unknown }[] = [];\n for (const entry of this.entries) {\n if (entry.byName || typeof entry.key !== 'function') continue;\n if (this.matchedEntryKeys.has(entry.key)) continue;\n const name = (entry.key as { name?: string }).name;\n if (!name) continue;\n const constructed = this.unmatchedConstructedByName.get(name);\n if (constructed !== undefined && constructed !== entry.key) {\n splits.push({ name, entryToken: entry.key });\n }\n }\n return splits;\n }\n\n /** Repeatable snapshot check of all exact expectations. */\n verify(): void {\n const splits = this.identitySplits();\n if (splits.length) {\n throw new TokenIdentitySplitError(splits.map((split) => split.name));\n }\n const failures: { token: string; method: string; expected: number; actual: number }[] = [];\n for (const byMethod of this.states.values()) {\n for (const state of byMethod.values()) {\n const expectation = state.expectation;\n if (!expectation) continue;\n if (state.actual !== expectation.expected) {\n failures.push({\n token: state.tokenName,\n method: state.method,\n expected: expectation.expected,\n actual: state.actual,\n });\n }\n }\n }\n if (failures.length) throw new VerificationError(failures);\n }\n}\n\nfunction isPromiseLike(value: unknown): value is Promise<unknown> {\n return !!value && typeof (value as any).then === 'function';\n}\n\nfunction recordOutcome(call: MutableCall, outcome: unknown, threw: boolean): unknown {\n if (!threw && isPromiseLike(outcome)) {\n call.pending = true;\n outcome.then(\n (value) => {\n call.result = value;\n call.pending = false;\n },\n (error) => {\n call.error = error;\n call.pending = false;\n },\n );\n return outcome;\n }\n call.pending = false;\n if (threw) call.error = outcome;\n else call.result = outcome;\n return outcome;\n}\n\n/**\n * Build the ioc instance decorator for one environment.\n *\n * Identity-stable (WeakMap), preserves sync/async call shape and `this`\n * binding, forwards the ioc context symbols by delegating to the underlying\n * (context-wrapped) target for everything unconfigured.\n */\nexport function createMethodDecorator(registry: WatchRegistry): (instance: any, token: any) => any {\n const wrappers = new WeakMap<object, any>();\n\n return function decorate(instance: any, token: any): any {\n const entries = registry.matchEntries(token);\n if (entries.length === 0) return instance;\n\n if (instance === null || (typeof instance !== 'object' && typeof instance !== 'function')) {\n throw new NonObjectMethodTargetError(tokenLabel(token));\n }\n\n if (wrappers.has(instance)) return wrappers.get(instance);\n\n // Effective per-method config: entries merge in write order, LWW per method\n const effective = new Map<string, { entryKey: unknown; descriptor: MethodDescriptor }>();\n for (const entry of entries) {\n for (const [method, descriptor] of entry.methods) {\n effective.set(method, { entryKey: entry.key, descriptor });\n }\n }\n\n // Validate configured methods exist and are callable\n for (const method of effective.keys()) {\n if (typeof instance[method] !== 'function') {\n throw new MethodNotCallableError(tokenLabel(token), method);\n }\n }\n\n const methodCache = new Map<string, (...args: any[]) => any>();\n\n const proxy = new Proxy(instance, {\n get(target, prop, receiver) {\n if (typeof prop === 'string' && effective.has(prop)) {\n let wrapped = methodCache.get(prop);\n if (!wrapped) {\n const { entryKey, descriptor } = effective.get(prop)!;\n wrapped = buildMethodWrapper(target, prop, descriptor, registry.state(entryKey, prop));\n methodCache.set(prop, wrapped);\n }\n return wrapped;\n }\n // Everything else (including ioc context symbols) delegates to the\n // underlying — usually context-wrapped — target.\n if (prop === CONTEXT_WRAPPED || prop === CONTEXT_OWNER) return (target as any)[prop];\n return Reflect.get(target, prop, receiver);\n },\n });\n\n wrappers.set(instance, proxy);\n return proxy;\n };\n}\n\nfunction buildMethodWrapper(\n target: any,\n method: string,\n descriptor: MethodDescriptor,\n state: MethodState | undefined,\n): (...args: any[]) => any {\n // The original effective method, read through the underlying target so the\n // ioc context proxy still owns context entry for real invocations.\n const callOriginal = (self: any, args: any[]) => {\n const fn = target[method];\n return fn.apply(self === undefined ? target : self, args);\n };\n\n // Raw custom wrapper: installed as-is, NOT watched, no recording.\n if (typeof descriptor === 'function') {\n const replacement = descriptor((...args: any[]) => callOriginal(target, args));\n return function (this: any, ...args: any[]) {\n return replacement.apply(this, args);\n };\n }\n\n if (!isDescriptor(descriptor)) {\n // Should be unreachable: builder validates descriptors on write.\n throw new TypeError(`Invalid method descriptor for ${method}`);\n }\n\n return function (this: any, ...args: any[]) {\n if (!state) throw new TypeError(`Missing method state for ${method}`);\n\n const expectation = state.expectation;\n const attempted = state.actual + 1;\n\n // Overflow fails immediately and never runs behavior/original\n if (expectation && attempted > expectation.expected) {\n state.actual = attempted;\n throw new ExpectationOverflowError(state.tokenName, method, expectation.expected, attempted);\n }\n\n state.actual = attempted;\n const call: MutableCall = {\n index: attempted,\n args: [...args],\n pending: false,\n timestamp: Date.now(),\n };\n state.calls.push(call);\n\n // Effective behavior for this invocation\n let behavior: BehaviorDescriptor | 'original-effective' | RawMethodWrapper;\n const base =\n descriptor.kind === 'expect'\n ? (descriptor.behavior ?? 'original-effective')\n : descriptor.kind === 'watch'\n ? (descriptor.wrapper ?? 'original-effective')\n : descriptor;\n\n if (base !== 'original-effective' && typeof base !== 'function' && base !== undefined && base.kind === 'calls') {\n if (state.scriptCursor >= base.script.length) {\n throw new CallScriptExhaustedError(state.tokenName, method, base.script.length, attempted);\n }\n behavior = base.script[state.scriptCursor]!;\n state.scriptCursor += 1;\n } else {\n behavior = base;\n }\n\n try {\n let outcome: unknown;\n if (behavior === 'original-effective') {\n outcome = callOriginal(this, args);\n } else if (typeof behavior === 'function') {\n // test.watch(rawWrapper): wrapper behavior runs and is recorded\n outcome = behavior((...inner: any[]) => callOriginal(this, inner)).apply(this, args);\n } else if (behavior.kind === 'returns') {\n outcome = behavior.value;\n } else if (behavior.kind === 'throws') {\n throw behavior.error;\n } else if (behavior.kind === 'original') {\n outcome = callOriginal(this, args);\n } else {\n throw new TypeError(`Nested test.calls() scripts are not supported (${method}).`);\n }\n return recordOutcome(call, outcome, false);\n } catch (error) {\n recordOutcome(call, error, true);\n throw error;\n }\n };\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@noego/testing",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Canonical NoEgo shared testing owner: testIoc real-IoC composition and the lowercase test.* method behavior/watch/expectation language",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -30,17 +30,16 @@
|
|
|
30
30
|
"publishConfig": {
|
|
31
31
|
"access": "public"
|
|
32
32
|
},
|
|
33
|
-
"dependencies": {},
|
|
34
33
|
"devDependencies": {
|
|
34
|
+
"@noego/ioc": "^0.6.1",
|
|
35
35
|
"@types/jest": "^29.5.14",
|
|
36
36
|
"@types/node": "^20.10.4",
|
|
37
37
|
"jest": "^29.7.0",
|
|
38
|
+
"reflect-metadata": "^0.2.2",
|
|
38
39
|
"ts-jest": "^29.3.2",
|
|
39
40
|
"ts-node": "^10.9.2",
|
|
40
41
|
"tsup": "^8.5.0",
|
|
41
|
-
"typescript": "^5.8.3"
|
|
42
|
-
"@noego/ioc": "file:../ioc",
|
|
43
|
-
"reflect-metadata": "^0.2.2"
|
|
42
|
+
"typescript": "^5.8.3"
|
|
44
43
|
},
|
|
45
44
|
"peerDependencies": {
|
|
46
45
|
"@noego/ioc": ">=0.5.0"
|