@noego/testing 0.1.0 → 0.2.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.d.cts CHANGED
@@ -1,262 +1,272 @@
1
- import { Clock, Instant, Duration, Scheduler, ScheduleInput, ScheduledTaskHandle, ScheduledTaskId, IdGenerator, BrandedId, RandomSource, FetchRequest, FetchResponse, FetchError, FetchClient, ProcessCommand, ProcessEvent, ProcessRunner, ProcessResult, SpawnCommand, ProcessHandle, KeyValueStore, Key, Codec, PutOptions, ObjectStore, ObjectKey, StoredObject, StoredObjectInput, ObjectVersion, DomainEvent, EventBus, EventHandler, Subscription, LogSink, LogEnvelope, TelemetrySink, TelemetryEnvelope, TraceSink, TraceEnvelope } from '@noego/runtime';
1
+ import { IContainer, ApplicationModule } from '@noego/ioc';
2
2
 
3
3
  /**
4
- * Deterministic clock. Time never moves unless the test moves it.
5
- * Defaults to a fixed instant so snapshots are stable.
6
- */
7
- declare class ManualClock extends Clock {
8
- static readonly defaultStart: Instant;
9
- private current;
10
- constructor(start?: Instant);
11
- now(): Instant;
12
- set(instant: Instant): void;
13
- advanceBy(duration: Duration): void;
14
- }
15
-
16
- interface PendingTask {
17
- readonly id: ScheduledTaskId;
18
- readonly label: string;
19
- readonly deadline: Instant;
20
- }
21
- /**
22
- * Deterministic scheduler driven by a ManualClock.
4
+ * Lowercase `test.*` descriptors immutable frozen values.
23
5
  *
24
- * Locked semantics:
25
- * - deadlines are (scheduledAt + delay);
26
- * - equal deadlines run in insertion order;
27
- * - cancelled tasks never execute;
28
- * - callbacks scheduled by callbacks are ordered deterministically (insertion
29
- * sequence is global and monotonic);
30
- * - async callbacks settle before advancing continues;
31
- * - runUntilIdle fails with a bounded diagnostic on infinite reschedule loops;
32
- * - assertNoPendingTasks fails teardown when tasks remain, unless allowed;
33
- * - CHOSEN MODEL: advancing time RUNS every task whose deadline falls inside
34
- * the advanced window, in deadline order. advanceBy(Duration.zero) runs
35
- * tasks already due.
6
+ * Descriptors carry NO mutable state (no cursors, no counters, no histories);
7
+ * all mutable invocation state lives in the built environment, so one
8
+ * descriptor value is safe to share across builders and repeated builds.
36
9
  */
37
- declare class ManualScheduler extends Scheduler {
38
- private readonly clock;
39
- private readonly tasks;
40
- private nextSequence;
41
- private nextId;
42
- constructor(clock: ManualClock);
43
- schedule(input: ScheduleInput): ScheduledTaskHandle;
44
- pending(): PendingTask[];
45
- /** Advance the clock, running every task due inside the window in order. */
46
- advanceBy(duration: Duration): Promise<void>;
47
- /** Advance to the earliest pending deadline and run exactly that task. */
48
- runNext(): Promise<void>;
49
- /** Run tasks (advancing time as needed) until none remain. */
50
- runUntilIdle(options?: {
51
- maxTasks?: number;
52
- }): Promise<void>;
53
- /** Teardown assertion: fails when pending tasks remain, unless allowed. */
54
- assertNoPendingTasks(options?: {
55
- allow?: boolean;
56
- }): void;
57
- private live;
58
- private nextDue;
59
- private execute;
60
- private remove;
10
+ declare const DESCRIPTOR: unique symbol;
11
+ /** A raw custom method wrapper: (original) => replacement. NOT auto-watched. */
12
+ type RawMethodWrapper = (original: (...args: any[]) => any) => (...args: any[]) => any;
13
+ interface ReturnsDescriptor {
14
+ readonly [DESCRIPTOR]: true;
15
+ readonly kind: 'returns';
16
+ readonly value: unknown;
61
17
  }
62
-
63
- /**
64
- * Deterministic ID generator: "<brand>-1", "<brand>-2", … per brand.
65
- * Fixed IDs keep snapshots stable; isolation makes collisions impossible.
66
- */
67
- declare class SequenceIdGenerator extends IdGenerator {
68
- private readonly counters;
69
- next<TBrand extends string>(brand: TBrand): BrandedId<TBrand>;
18
+ interface ThrowsDescriptor {
19
+ readonly [DESCRIPTOR]: true;
20
+ readonly kind: 'throws';
21
+ readonly error: unknown;
70
22
  }
71
-
72
- /**
73
- * Deterministic random source (mulberry32). The same seed always yields the
74
- * same sequence.
75
- */
76
- declare class SeededRandomSource extends RandomSource {
77
- private state;
78
- constructor(seed?: number);
79
- private nextUint32;
80
- bytes(length: number): Uint8Array;
81
- integer(minInclusive: number, maxExclusive: number): number;
23
+ interface OriginalDescriptor {
24
+ readonly [DESCRIPTOR]: true;
25
+ readonly kind: 'original';
82
26
  }
83
-
84
- interface FetchScriptEntry {
85
- /** Human-readable description used in failure diagnostics. */
86
- readonly describe: string;
87
- /** Typed predicate the incoming request must satisfy. */
88
- readonly matches: (request: FetchRequest) => boolean;
89
- /** Either respond or fail with a normalized FetchError. */
90
- readonly respond: (request: FetchRequest) => FetchResponse | FetchError;
27
+ interface CallsDescriptor {
28
+ readonly [DESCRIPTOR]: true;
29
+ readonly kind: 'calls';
30
+ readonly script: readonly BehaviorDescriptor[];
91
31
  }
92
- /**
93
- * Scripted FetchClient. Scripts are consumed strictly in order; an unexpected
94
- * or mismatched call fails immediately with a diagnostic.
95
- */
96
- declare class ScriptedFetchClient extends FetchClient {
97
- private readonly script;
98
- private cursor;
99
- readonly executed: FetchRequest[];
100
- constructor(script: readonly FetchScriptEntry[]);
101
- execute(request: FetchRequest): Promise<FetchResponse>;
102
- /** Teardown assertion: every scripted call must have been consumed. */
103
- assertScriptConsumed(): void;
32
+ type BehaviorDescriptor = ReturnsDescriptor | ThrowsDescriptor | OriginalDescriptor | CallsDescriptor;
33
+ interface WatchDescriptor {
34
+ readonly [DESCRIPTOR]: true;
35
+ readonly kind: 'watch';
36
+ readonly wrapper?: RawMethodWrapper;
104
37
  }
105
-
106
- /** Spawn handle with test-only deterministic delivery control. */
107
- interface ScriptedSpawnHandle extends ProcessHandle {
108
- /** Deliver all scripted events now (deterministic, test-controlled). */
109
- flush(): void;
38
+ interface ExpectationDescriptor {
39
+ readonly [DESCRIPTOR]: true;
40
+ readonly kind: 'expect';
41
+ /** Exact required call count; 0 for never(). */
42
+ readonly expected: number;
43
+ /** Behavior used for allowed calls; undefined = original effective behavior. */
44
+ readonly behavior?: BehaviorDescriptor;
110
45
  }
111
- interface ProcessScriptEntry {
112
- readonly describe: string;
113
- readonly matches: (command: ProcessCommand) => boolean;
114
- /** Events emitted (for spawn) / folded into the result (for run). */
115
- readonly events: readonly ProcessEvent[];
46
+ /** Everything installable through .methods({...}). */
47
+ type MethodDescriptor = BehaviorDescriptor | WatchDescriptor | ExpectationDescriptor | RawMethodWrapper;
48
+ declare const test: {
49
+ /** Return the supplied value when the method is called. Auto-watches. */
50
+ returns(value: unknown): ReturnsDescriptor;
51
+ /** Throw/reject with the supplied error. Auto-watches. */
52
+ throws(error: unknown): ThrowsDescriptor;
53
+ /** Invoke the original effective method. Auto-watches. */
54
+ original(): OriginalDescriptor;
55
+ /**
56
+ * Per-invocation behavior script: call 1 uses entry 1, and so on. A call
57
+ * after exhaustion fails immediately. Unused entries do not fail verification.
58
+ */
59
+ calls(script: readonly BehaviorDescriptor[]): CallsDescriptor;
60
+ /**
61
+ * Keep original behavior and record calls. With a raw wrapper argument, the
62
+ * wrapper's behavior runs and is recorded.
63
+ */
64
+ watch(wrapper?: RawMethodWrapper): WatchDescriptor;
65
+ /** Require exactly one call; with no behavior, the original runs. */
66
+ once(behavior?: BehaviorDescriptor): ExpectationDescriptor;
67
+ /** Require exactly `count` calls; with no behavior, the original runs. */
68
+ times(count: number, behavior?: BehaviorDescriptor): ExpectationDescriptor;
69
+ /** Require zero calls; the first invocation fails and skips the original. */
70
+ never(): ExpectationDescriptor;
71
+ /** Read the recorded history for a watched method in one environment. */
72
+ inspect(environment: unknown, token: unknown, method: string): MethodInspection;
73
+ };
74
+ /** Symbol under which a built environment exposes its watch registry. */
75
+ declare const ENV_REGISTRY: unique symbol;
76
+ interface RecordedCall {
77
+ /** 1-based invocation index in this environment. */
78
+ readonly index: number;
79
+ readonly args: readonly unknown[];
80
+ /** Present once the call returned (resolved value for async methods). */
81
+ readonly result?: unknown;
82
+ /** Present once the call threw/rejected. */
83
+ readonly error?: unknown;
84
+ /** True while an async outcome is still pending. */
85
+ readonly pending: boolean;
86
+ readonly timestamp: number;
116
87
  }
117
- /**
118
- * Scripted ProcessRunner. Scripts are consumed in order; unexpected commands
119
- * fail immediately. Spawned handles emit their declared events when
120
- * flush() is called, keeping delivery under test control.
121
- */
122
- declare class ScriptedProcessRunner extends ProcessRunner {
123
- private readonly script;
124
- private cursor;
125
- private readonly openHandles;
126
- constructor(script: readonly ProcessScriptEntry[]);
127
- private consume;
128
- run(command: ProcessCommand): Promise<ProcessResult>;
129
- spawn(command: SpawnCommand): ScriptedSpawnHandle;
130
- /** Teardown assertion: no spawned process may remain open. */
131
- assertNoOpenHandles(): void;
88
+ interface MethodInspection {
89
+ readonly count: number;
90
+ readonly calls: readonly RecordedCall[];
132
91
  }
133
92
 
134
93
  /**
135
- * Deterministic in-memory KeyValueStore with clock-driven TTL semantics.
136
- * Passes the same contract as KV/Redis adapters.
94
+ * Environment-owned method behavior/observation runtime.
95
+ *
96
+ * All mutable state (histories, expectation counters, calls-script cursors)
97
+ * lives here, created fresh at every build(). Descriptors stay immutable.
137
98
  */
138
- declare class MemoryKeyValueStore extends KeyValueStore {
139
- private readonly clock;
99
+
100
+ interface MutableCall {
101
+ index: number;
102
+ args: readonly unknown[];
103
+ result?: unknown;
104
+ error?: unknown;
105
+ pending: boolean;
106
+ timestamp: number;
107
+ }
108
+ declare class MethodState {
109
+ readonly tokenName: string;
110
+ readonly method: string;
111
+ readonly descriptor: Exclude<MethodDescriptor, RawMethodWrapper>;
112
+ readonly calls: MutableCall[];
113
+ /** Actual invocation count (includes the call currently executing). */
114
+ actual: number;
115
+ /** Cursor into a test.calls() script. */
116
+ scriptCursor: number;
117
+ constructor(tokenName: string, method: string, descriptor: Exclude<MethodDescriptor, RawMethodWrapper>);
118
+ get expectation(): ExpectationDescriptor | undefined;
119
+ inspection(): MethodInspection;
120
+ }
121
+ /** One method-config entry: how it was keyed, and its per-method descriptors. */
122
+ interface MethodConfigEntry {
123
+ /** Exact token (class/symbol/string) or a name string matched lazily. */
124
+ readonly key: unknown;
125
+ readonly byName: boolean;
126
+ readonly methods: ReadonlyMap<string, MethodDescriptor>;
127
+ }
128
+ declare class WatchRegistry {
129
+ /** entry-identity → method → state. Entries share states across instances. */
130
+ private readonly states;
131
+ private readonly nameIndex;
140
132
  private readonly entries;
141
- constructor(clock: Clock);
142
- get<T>(key: Key, codec: Codec<T>): Promise<T | null>;
143
- put<T>(key: Key, value: T, codec: Codec<T>, options?: PutOptions): Promise<void>;
144
- delete(key: Key): Promise<void>;
133
+ addEntry(entry: MethodConfigEntry): void;
134
+ private entryLabel;
135
+ /** Entries applying to a resolving token (exact identity or name match). */
136
+ matchEntries(token: unknown): MethodConfigEntry[];
137
+ state(entryKey: unknown, method: string): MethodState | undefined;
138
+ inspect(token: unknown, method: string): MethodInspection;
139
+ /** Repeatable snapshot check of all exact expectations. */
140
+ verify(): void;
145
141
  }
146
142
 
147
143
  /**
148
- * Deterministic in-memory ObjectStore with monotonically increasing versions.
149
- * Passes the same contract as R2/S3 adapters.
144
+ * `testIoc` the canonical shared real-IoC test composition builder.
145
+ *
146
+ * Persistent immutable: every fluent call returns a new derived builder
147
+ * sharing the ordered write log structurally. Non-conflicting writes are
148
+ * order-insensitive; the last write to the same effective identity wins on
149
+ * that derived branch. `.build()` is non-consuming and creates fresh runtime,
150
+ * watch, and expectation state (spec 15, PBR-01..15).
150
151
  */
151
- declare class MemoryObjectStore extends ObjectStore {
152
- private readonly objects;
153
- private nextVersion;
154
- read(key: ObjectKey): Promise<StoredObject | null>;
155
- write(key: ObjectKey, object: StoredObjectInput): Promise<ObjectVersion>;
156
- delete(key: ObjectKey): Promise<void>;
152
+
153
+ type Token = unknown;
154
+ type ClassLike = new (...args: any[]) => any;
155
+ /** Config maps accept plain objects (string keys) or Maps (exact tokens). */
156
+ type ConfigMap<V> = Record<string, V> | ReadonlyMap<Token, V>;
157
+ type MethodsConfig = ConfigMap<Record<string, MethodDescriptor> | ReadonlyMap<string, MethodDescriptor>>;
158
+ type UseInput = ApplicationModule | TestIocBuilder;
159
+ type Write = {
160
+ op: 'use';
161
+ module: ApplicationModule;
162
+ } | {
163
+ op: 'classes';
164
+ key: Token;
165
+ byName: boolean;
166
+ implementation: ClassLike;
167
+ } | {
168
+ op: 'functions';
169
+ key: Token;
170
+ byName: boolean;
171
+ factory: (...args: any[]) => any;
172
+ } | {
173
+ op: 'values';
174
+ key: Token;
175
+ byName: boolean;
176
+ value: unknown;
177
+ } | {
178
+ op: 'methods';
179
+ key: Token;
180
+ byName: boolean;
181
+ methods: ReadonlyMap<string, MethodDescriptor>;
182
+ };
183
+ interface TestEnvironment {
184
+ readonly root: IContainer;
185
+ get<T>(token: unknown, params?: any[]): Promise<T> | T;
186
+ instance<T>(cls: new (...args: any[]) => T, params?: any[]): Promise<T> | T;
187
+ extend(): IContainer;
188
+ verify(): Promise<void>;
189
+ dispose(): Promise<void>;
190
+ readonly [ENV_REGISTRY]: WatchRegistry;
157
191
  }
192
+ declare class TestIocBuilder {
193
+ private readonly log;
194
+ private constructor();
195
+ /** @internal */
196
+ static create(inputs: readonly UseInput[]): TestIocBuilder;
197
+ /** @internal — read by .use(builderPreset) */
198
+ get writes(): readonly Write[];
199
+ private derive;
200
+ private useAll;
201
+ /** Apply a reusable composition preset: an ApplicationModule or a builder. */
202
+ use(preset: UseInput): TestIocBuilder;
203
+ /** Replace the implementation for IoC class tokens in the built environment. */
204
+ classes(config: ConfigMap<ClassLike>): TestIocBuilder;
205
+ /** Replace IoC factory/provider registrations. */
206
+ functions(config: ConfigMap<(...args: any[]) => any>): TestIocBuilder;
207
+ /** Provide/replace IoC value registrations. */
208
+ values(config: ConfigMap<unknown>): TestIocBuilder;
209
+ /** Install method behavior/observation descriptors on IoC-managed instances. */
210
+ methods(config: MethodsConfig): TestIocBuilder;
211
+ /** Materialize a fresh, isolated real-IoC environment. Non-consuming. */
212
+ build(): Promise<TestEnvironment>;
213
+ /**
214
+ * Register a class binding. When token === implementation this is a plain
215
+ * class registration. Otherwise an alias factory resolves the implementation
216
+ * through real IoC, mirroring the implementation's effective lifetime so
217
+ * lifetime validation (captive-lifetime checks) stays honest.
218
+ */
219
+ private registerClassBinding;
220
+ }
221
+ /** Create a persistent immutable real-IoC test composition builder. */
222
+ declare function testIoc(...inputs: UseInput[]): TestIocBuilder;
158
223
 
159
- type DeliveryMode = "immediate" | "queued";
160
224
  /**
161
- * Recording EventBus. Records exact publication order. Delivery modes:
162
- * - immediate (default): handlers run during publish;
163
- * - queued: events buffer until deliverQueued() is called.
164
- * Active subscriptions at teardown fail assertNoActiveSubscriptions().
225
+ * Diagnostics are first-class: every error names the real token/method and
226
+ * what was expected vs what happened, never only internal wrapper machinery.
165
227
  */
166
- declare class RecordingEventBus<TEvent extends DomainEvent> extends EventBus<TEvent> {
167
- private readonly mode;
168
- readonly published: TEvent[];
169
- private readonly handlers;
170
- private readonly queue;
171
- constructor(mode?: DeliveryMode);
172
- publish(event: TEvent): Promise<void>;
173
- subscribe(handler: EventHandler<TEvent>): Subscription;
174
- /** Deliver buffered events (queued mode) in publication order. */
175
- deliverQueued(): Promise<void>;
176
- get activeSubscriptionCount(): number;
177
- assertNoActiveSubscriptions(): void;
178
- private deliver;
228
+ declare class TestingError extends Error {
229
+ constructor(message: string);
179
230
  }
180
-
181
- /** In-memory LogSink recording envelopes in emission order. */
182
- declare class RecordingLogSink extends LogSink {
183
- readonly envelopes: LogEnvelope[];
184
- emit(envelope: LogEnvelope): void;
231
+ /** Thrown at build() when the installed @noego/ioc lacks the decoration seam. */
232
+ declare class MissingIocSeamError extends TestingError {
233
+ constructor();
185
234
  }
186
- /** In-memory TraceSink recording envelopes in emission order. */
187
- declare class RecordingTraceSink extends TraceSink {
188
- readonly envelopes: TraceEnvelope[];
189
- emit(envelope: TraceEnvelope): void;
235
+ /** test.inspect() on a method that is not watched in this environment. */
236
+ declare class UnwatchedInspectionError extends TestingError {
237
+ constructor(token: string, method: string);
190
238
  }
191
- /** In-memory TelemetrySink recording envelopes in emission order. */
192
- declare class RecordingTelemetrySink extends TelemetrySink {
193
- readonly envelopes: TelemetryEnvelope[];
194
- emit(envelope: TelemetryEnvelope): void;
239
+ /** A call arrived after a test.calls([...]) script was fully consumed. */
240
+ declare class CallScriptExhaustedError extends TestingError {
241
+ constructor(token: string, method: string, scriptLength: number, callIndex: number);
195
242
  }
196
- /** LogSink that drops everything (for suites that assert nothing about logs). */
197
- declare class NoopLogSink extends LogSink {
198
- emit(_envelope: LogEnvelope): void;
243
+ /** An exact expectation (once/times/never) was exceeded at call time. */
244
+ declare class ExpectationOverflowError extends TestingError {
245
+ constructor(token: string, method: string, expected: number, attempted: number);
199
246
  }
200
-
201
- /**
202
- * Behavioral contract every Clock implementation must satisfy.
203
- * Runs inside the caller's jest context.
204
- */
205
- declare function runClockContract(name: string, setup: () => {
206
- clock: Clock;
207
- }): void;
208
-
209
- interface KeyValueStoreContractContext {
210
- store: KeyValueStore;
211
- /** Move the store's time source forward (real adapters may wait/mock). */
212
- advanceBy: (duration: Duration) => Promise<void>;
213
- /** Whether the implementation supports TTL expiry. */
214
- supportsTtl: boolean;
247
+ /** Aggregated under-count failures reported by env.verify(). */
248
+ declare class VerificationError extends TestingError {
249
+ constructor(failures: readonly {
250
+ token: string;
251
+ method: string;
252
+ expected: number;
253
+ actual: number;
254
+ }[]);
215
255
  }
216
- /**
217
- * Behavioral contract every KeyValueStore implementation must satisfy —
218
- * memory, KV, and Redis adapters alike.
219
- */
220
- declare function runKeyValueStoreContract(name: string, setup: () => KeyValueStoreContractContext): void;
221
-
222
- /**
223
- * Behavioral contract every ObjectStore implementation must satisfy —
224
- * memory, R2, S3, and filesystem adapters alike.
225
- */
226
- declare function runObjectStoreContract(name: string, setup: () => {
227
- store: ObjectStore;
228
- }): void;
229
-
230
- interface TestEvent extends DomainEvent {
231
- readonly type: "test-event";
232
- readonly value: number;
256
+ /** .methods configured for a token whose resolved value has no such callable method. */
257
+ declare class MethodNotCallableError extends TestingError {
258
+ constructor(token: string, method: string);
233
259
  }
234
- declare const testEvent: (value: number) => TestEvent;
235
- interface EventBusContractContext {
236
- bus: EventBus<TestEvent>;
237
- /** Force delivery of any buffered events (no-op for immediate buses). */
238
- deliver: () => Promise<void>;
260
+ /** .methods configured for a token that resolved to a non-object value. */
261
+ declare class NonObjectMethodTargetError extends TestingError {
262
+ constructor(token: string);
239
263
  }
240
- /**
241
- * Behavioral contract every EventBus implementation must satisfy.
242
- */
243
- declare function runEventBusContract(name: string, setup: () => EventBusContractContext): void;
244
-
245
- interface LeakCheck {
246
- /** Which resource family this check guards (timers, subscriptions, …). */
247
- readonly name: string;
248
- /** Returns a description per leaked resource; empty means clean. */
249
- readonly check: () => readonly string[];
264
+ /** A string configuration key could not be resolved to a known IoC token. */
265
+ declare class UnknownTokenKeyError extends TestingError {
266
+ constructor(key: string, space: string, known: readonly string[]);
250
267
  }
251
- /**
252
- * Aggregates leak checks and fails teardown with one clear report listing
253
- * every leaked resource across all registered checks.
254
- */
255
- declare class LeakDetector {
256
- private readonly checks;
257
- register(check: LeakCheck): void;
258
- findLeaks(): string[];
259
- assertNoLeaks(): void;
268
+ /** Invalid descriptor construction (e.g. test.times(-1)). */
269
+ declare class InvalidDescriptorError extends TestingError {
260
270
  }
261
271
 
262
- export { type DeliveryMode, type EventBusContractContext, type FetchScriptEntry, type KeyValueStoreContractContext, type LeakCheck, LeakDetector, ManualClock, ManualScheduler, MemoryKeyValueStore, MemoryObjectStore, NoopLogSink, type PendingTask, type ProcessScriptEntry, RecordingEventBus, RecordingLogSink, RecordingTelemetrySink, RecordingTraceSink, ScriptedFetchClient, ScriptedProcessRunner, type ScriptedSpawnHandle, SeededRandomSource, SequenceIdGenerator, type TestEvent, runClockContract, runEventBusContract, runKeyValueStoreContract, runObjectStoreContract, testEvent };
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 };