@kb-labs/shared-testing-platform 2.96.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,20 @@
1
+ # @kb-labs/shared-testing-platform
2
+
3
+ Platform setup utilities for testing KB Labs plugins.
4
+
5
+ Requires `@kb-labs/core-runtime` (concrete implementation). For pure mocks only, use `@kb-labs/shared-testing`.
6
+
7
+ ## Usage
8
+
9
+ ```typescript
10
+ import { createTestContext, testCommand, mockLLM } from '@kb-labs/sdk/testing';
11
+
12
+ const llm = mockLLM().onAnyComplete().respondWith('hello');
13
+ const { ctx, cleanup } = createTestContext({ platform: { llm } });
14
+ ```
15
+
16
+ ## Exports
17
+
18
+ - `setupTestPlatform` — sets test mocks into the global platform singleton
19
+ - `createTestContext` — factory for `PluginContextV3` with injected mocks
20
+ - `testCommand` — single-function runner for plugin command handlers
@@ -0,0 +1,270 @@
1
+ import { platform } from '@kb-labs/core-runtime';
2
+ import { ILLM, ICache, IEmbeddings, IVectorStore, IStorage, IAnalytics, ILogger, IEventBus } from '@kb-labs/core-platform';
3
+ import { HostContext, PlatformServices, UIFacade, PluginContextV3, PluginAPI, EnvironmentAPI, RuntimeAPI, SnapshotAPI, TraceContext, WorkspaceAPI } from '@kb-labs/plugin-contracts';
4
+ import { IDocumentDatabase, EnsureCollectionOpts, BaseDocument, DocumentFilter, FindOptions, ProjectOpts, SignalOpts, DocumentUpdate, BulkOp, BulkResult, IDocumentTransaction, IKVStore, SetOpts } from '@kb-labs/core-platform/adapters';
5
+ export { LLMCall, LLMToolCallRecord, LogEntry, MockCacheInstance, MockEmbeddings, MockLLM, MockLLMInstance, MockLoggerInstance, MockStorageInstance, mockCache, mockLLM, mockLogger, mockStorage } from '@kb-labs/shared-testing';
6
+
7
+ /**
8
+ * @module @kb-labs/shared-testing-platform/setup-platform
9
+ *
10
+ * Solves the "singleton gap" problem: composables (useLLM, useCache, etc.)
11
+ * read from the global platform singleton, but createTestContext() only
12
+ * populates ctx.platform. This module bridges the gap by setting test mocks
13
+ * directly into the global singleton.
14
+ */
15
+
16
+ /**
17
+ * Options for setting up the test platform.
18
+ * Only adapters that are provided will be registered.
19
+ * Omitted adapters will use the PlatformContainer's built-in fallbacks.
20
+ */
21
+ interface TestPlatformOptions {
22
+ llm?: ILLM;
23
+ cache?: ICache;
24
+ embeddings?: IEmbeddings;
25
+ vectorStore?: IVectorStore;
26
+ storage?: IStorage;
27
+ analytics?: IAnalytics;
28
+ logger?: ILogger;
29
+ eventBus?: IEventBus;
30
+ }
31
+ interface TestPlatformResult {
32
+ /** The global platform singleton (with test mocks applied) */
33
+ platform: typeof platform;
34
+ /** Call in afterEach() to reset the singleton to a clean state */
35
+ cleanup: () => void;
36
+ }
37
+ /**
38
+ * Setup the global platform singleton with test mocks.
39
+ *
40
+ * This ensures that useLLM(), useCache() and other composables
41
+ * return the test mocks instead of uninitialized/stale adapters.
42
+ *
43
+ * @example
44
+ * ```typescript
45
+ * import { setupTestPlatform, mockLLM } from '@kb-labs/shared-testing-platform';
46
+ *
47
+ * describe('my handler', () => {
48
+ * let cleanup: () => void;
49
+ *
50
+ * beforeEach(() => {
51
+ * const result = setupTestPlatform({
52
+ * llm: mockLLM().onAnyComplete().respondWith('hello'),
53
+ * });
54
+ * cleanup = result.cleanup;
55
+ * });
56
+ *
57
+ * afterEach(() => cleanup());
58
+ * });
59
+ * ```
60
+ */
61
+ declare function setupTestPlatform(options?: TestPlatformOptions): TestPlatformResult;
62
+
63
+ /**
64
+ * @module @kb-labs/shared-testing-platform/create-test-context
65
+ *
66
+ * Enhanced test context factory that bridges ctx.platform and the global singleton.
67
+ *
68
+ * Unlike the original createTestContext() from SDK, this version:
69
+ * - Uses mockLLM/mockCache/mockLogger with vi.fn() spies (not noop functions)
70
+ * - Syncs ctx.platform adapters with the global singleton via setupTestPlatform()
71
+ * - Provides a cleanup function to reset the singleton in afterEach()
72
+ *
73
+ * @example
74
+ * ```typescript
75
+ * import { createTestContext, mockLLM } from '@kb-labs/shared-testing-platform';
76
+ *
77
+ * const llm = mockLLM().onAnyComplete().respondWith('hello');
78
+ * const { ctx, cleanup } = createTestContext({ platform: { llm } });
79
+ *
80
+ * // Both work — ctx.platform and useLLM() return the same mock
81
+ * await handler.execute(ctx, args);
82
+ * expect(llm.complete).toHaveBeenCalled();
83
+ *
84
+ * cleanup(); // Reset singleton in afterEach
85
+ * ```
86
+ */
87
+
88
+ interface CreateTestContextOptions {
89
+ pluginId?: string;
90
+ pluginVersion?: string;
91
+ host?: 'cli' | 'rest' | 'workflow' | 'webhook';
92
+ hostContext?: HostContext;
93
+ config?: unknown;
94
+ cwd?: string;
95
+ outdir?: string;
96
+ tenantId?: string;
97
+ signal?: AbortSignal;
98
+ /** Override platform services. Also synced to global singleton. */
99
+ platform?: Partial<PlatformServices>;
100
+ /** Override UI facade */
101
+ ui?: Partial<UIFacade>;
102
+ /**
103
+ * If true, sync platform adapters to the global singleton
104
+ * so that useLLM(), useCache(), etc. return the test mocks.
105
+ * @default true
106
+ */
107
+ syncSingleton?: boolean;
108
+ }
109
+ interface TestContextResult<TConfig = unknown> {
110
+ /** The plugin context with test mocks */
111
+ ctx: PluginContextV3<TConfig>;
112
+ /** Call in afterEach() to reset the global singleton */
113
+ cleanup: () => void;
114
+ }
115
+ declare function createMockTrace(): TraceContext;
116
+ declare function createMockUI(): UIFacade;
117
+ declare function createMockRuntime(): RuntimeAPI;
118
+ declare function createMockEnvironmentAPI(): EnvironmentAPI;
119
+ declare function createMockWorkspaceAPI(): WorkspaceAPI;
120
+ declare function createMockSnapshotAPI(): SnapshotAPI;
121
+ declare function createInfraApiMocks(): Pick<PluginAPI, 'environment' | 'workspace' | 'snapshot'>;
122
+ declare function createMockPluginAPI(): PluginAPI;
123
+ declare const createMockPlatformApi: typeof createMockPluginAPI;
124
+ declare function createMockPluginContextV3<TConfig = unknown>(options?: CreateTestContextOptions): TestContextResult<TConfig>;
125
+ /**
126
+ * Create a test context for plugin development.
127
+ *
128
+ * Uses mock builders with vi.fn() spies and syncs platform adapters
129
+ * to the global singleton by default so that composables work in tests.
130
+ */
131
+ declare function createTestContext<TConfig = unknown>(options?: CreateTestContextOptions): TestContextResult<TConfig>;
132
+
133
+ /**
134
+ * @module @kb-labs/shared-testing-platform/test-command
135
+ *
136
+ * Single-function test runner for plugin command handlers.
137
+ */
138
+
139
+ interface TestableHandler<TConfig = unknown, TInput = unknown, TResult = unknown> {
140
+ execute(context: PluginContextV3<TConfig>, input: TInput): Promise<TResult> | TResult;
141
+ cleanup?(): Promise<void> | void;
142
+ }
143
+ interface TestCommandOptions<TConfig = unknown> {
144
+ flags?: Record<string, unknown>;
145
+ argv?: string[];
146
+ query?: Record<string, unknown>;
147
+ body?: unknown;
148
+ params?: Record<string, unknown>;
149
+ input?: unknown;
150
+ host?: 'cli' | 'rest' | 'workflow' | 'webhook';
151
+ config?: TConfig;
152
+ cwd?: string;
153
+ tenantId?: string;
154
+ signal?: AbortSignal;
155
+ platform?: Partial<PlatformServices>;
156
+ ui?: Partial<UIFacade>;
157
+ syncSingleton?: boolean;
158
+ }
159
+ interface TestCommandResult<TResult = unknown> {
160
+ exitCode: number;
161
+ result: TResult | undefined;
162
+ meta: Record<string, unknown> | undefined;
163
+ raw: unknown;
164
+ ui: UIFacade;
165
+ ctx: PluginContextV3;
166
+ cleanup: () => void;
167
+ }
168
+ declare function testCommand<TResult = unknown, TConfig = unknown>(handler: TestableHandler<TConfig, any, any>, options?: TestCommandOptions<TConfig>): Promise<TestCommandResult<TResult>>;
169
+
170
+ /**
171
+ * In-memory `IDocumentDatabase` for tests.
172
+ *
173
+ * Scope: behavioural fidelity sufficient for governance / wrapper tests —
174
+ * CRUD, filters, updates, atomic bulkWrite, transactions, ensureCollection,
175
+ * unique indexes. Not optimised, not for production. Sweep-based TTL is
176
+ * intentionally absent (not exercised by the governance suite).
177
+ */
178
+
179
+ interface InMemoryDocumentDatabaseOptions {
180
+ /** Seed clock value for new documents. Defaults to `Date.now()` at call site. */
181
+ now?: () => number;
182
+ }
183
+ /**
184
+ * In-memory `IDocumentDatabase` for tests.
185
+ *
186
+ * Storage layout: `Map<collectionName, Collection>`. Each `Collection` holds
187
+ * a `Map<id, doc>` and a list of declared indexes (used for uniqueness checks).
188
+ */
189
+ declare class InMemoryDocumentDatabase implements IDocumentDatabase {
190
+ private readonly collections;
191
+ private readonly now;
192
+ private closed;
193
+ constructor(opts?: InMemoryDocumentDatabaseOptions);
194
+ private getCollection;
195
+ private throwIfClosed;
196
+ ensureCollection(name: string, options?: EnsureCollectionOpts): Promise<void>;
197
+ find<T extends BaseDocument, P = T>(collection: string, filter: DocumentFilter<T>, options?: FindOptions & ProjectOpts<T, P> & SignalOpts): Promise<P[]>;
198
+ findStream<T extends BaseDocument, P = T>(collection: string, filter: DocumentFilter<T>, options?: FindOptions & ProjectOpts<T, P> & SignalOpts & {
199
+ batchSize?: number;
200
+ }): AsyncIterable<P>;
201
+ findById<T extends BaseDocument>(collection: string, id: string): Promise<T | null>;
202
+ count<T extends BaseDocument>(collection: string, filter: DocumentFilter<T>): Promise<number>;
203
+ insertOne<T extends BaseDocument>(collection: string, doc: Omit<T, 'id' | 'createdAt' | 'updatedAt'>): Promise<T>;
204
+ insertMany<T extends BaseDocument>(collection: string, docs: Array<Omit<T, 'id' | 'createdAt' | 'updatedAt'>>): Promise<T[]>;
205
+ updateOne<T extends BaseDocument>(collection: string, filter: DocumentFilter<T>, update: DocumentUpdate<T>, options?: SignalOpts & {
206
+ upsert?: boolean;
207
+ }): Promise<T | null>;
208
+ updateMany<T extends BaseDocument>(collection: string, filter: DocumentFilter<T>, update: DocumentUpdate<T>): Promise<number>;
209
+ updateById<T extends BaseDocument>(collection: string, id: string, update: DocumentUpdate<T>): Promise<T | null>;
210
+ deleteMany<T extends BaseDocument>(collection: string, filter: DocumentFilter<T>): Promise<number>;
211
+ deleteById(collection: string, id: string): Promise<boolean>;
212
+ bulkWrite<T extends BaseDocument>(collection: string, ops: Array<BulkOp<T>>): Promise<BulkResult>;
213
+ transaction<T>(fn: (tx: IDocumentTransaction) => Promise<T>): Promise<T>;
214
+ ping(): Promise<{
215
+ ok: boolean;
216
+ latencyMs: number;
217
+ }>;
218
+ close(): Promise<void>;
219
+ }
220
+ declare function createInMemoryDocumentDatabase(opts?: InMemoryDocumentDatabaseOptions): InMemoryDocumentDatabase;
221
+
222
+ /**
223
+ * In-memory `IKVStore` for tests. Sufficient for governance / wrapper tests:
224
+ * full method surface, TTL respected on read (lazy expiry), atomic CAS / incr
225
+ * via the single-threaded event loop. No background sweeper.
226
+ */
227
+
228
+ declare class InMemoryKVStore implements IKVStore {
229
+ private readonly map;
230
+ private closed;
231
+ private throwIfClosed;
232
+ private isExpired;
233
+ private getLive;
234
+ get<T = unknown>(key: string): Promise<T | null>;
235
+ getMany<T = unknown>(keys: string[]): Promise<Array<T | null>>;
236
+ set<T = unknown>(key: string, value: T, options?: SetOpts): Promise<boolean>;
237
+ setMany<T = unknown>(entries: Array<{
238
+ key: string;
239
+ value: T;
240
+ ttlMs?: number;
241
+ }>): Promise<void>;
242
+ setIfNotExists<T = unknown>(key: string, value: T, options?: {
243
+ ttlMs?: number;
244
+ } & SignalOpts): Promise<boolean>;
245
+ delete(key: string): Promise<boolean>;
246
+ exists(key: string): Promise<boolean>;
247
+ cas<T = unknown>(key: string, expected: T, next: T, options?: {
248
+ ttlMs?: number;
249
+ } & SignalOpts): Promise<boolean>;
250
+ incr(key: string, delta?: number, options?: {
251
+ ttlMs?: number;
252
+ } & SignalOpts): Promise<number>;
253
+ ttl(key: string): Promise<number | null>;
254
+ expire(key: string, ttlMs: number): Promise<boolean>;
255
+ persist(key: string): Promise<boolean>;
256
+ scan(prefix?: string, _options?: {
257
+ batchSize?: number;
258
+ } & SignalOpts): AsyncIterable<{
259
+ key: string;
260
+ value: unknown;
261
+ }>;
262
+ ping(): Promise<{
263
+ ok: boolean;
264
+ latencyMs: number;
265
+ }>;
266
+ close(): Promise<void>;
267
+ }
268
+ declare function createInMemoryKVStore(): InMemoryKVStore;
269
+
270
+ export { type CreateTestContextOptions, InMemoryDocumentDatabase, type InMemoryDocumentDatabaseOptions, InMemoryKVStore, type TestCommandOptions, type TestCommandResult, type TestContextResult, type TestPlatformOptions, type TestPlatformResult, type TestableHandler, createInMemoryDocumentDatabase, createInMemoryKVStore, createInfraApiMocks, createMockEnvironmentAPI, createMockPlatformApi, createMockPluginAPI, createMockPluginContextV3, createMockRuntime, createMockSnapshotAPI, createMockTrace, createMockUI, createMockWorkspaceAPI, createTestContext, setupTestPlatform, testCommand };