@kb-labs/shared-testing 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,916 @@
1
+ import { resetPlatform, platform } from '@kb-labs/core-runtime';
2
+ import { vi } from 'vitest';
3
+
4
+ // src/setup-platform.ts
5
+ function setupTestPlatform(options = {}) {
6
+ resetPlatform();
7
+ const adapterMap = [
8
+ ["llm", options.llm],
9
+ ["cache", options.cache],
10
+ ["embeddings", options.embeddings],
11
+ ["vectorStore", options.vectorStore],
12
+ ["storage", options.storage],
13
+ ["analytics", options.analytics],
14
+ ["logger", options.logger],
15
+ ["eventBus", options.eventBus]
16
+ ];
17
+ for (const [key, instance] of adapterMap) {
18
+ if (instance !== void 0) {
19
+ platform.setAdapter(key, instance);
20
+ }
21
+ }
22
+ return {
23
+ platform,
24
+ cleanup: () => resetPlatform()
25
+ };
26
+ }
27
+ var MockLLMBuilder = class {
28
+ rules = [];
29
+ defaultResponse = "mock response";
30
+ streamChunks = ["mock"];
31
+ errorToThrow = null;
32
+ toolCallsToReturn = [];
33
+ toolCallResponseContent = "";
34
+ /**
35
+ * Match a specific prompt (exact string or regex).
36
+ * Returns a handler to set the response.
37
+ */
38
+ onComplete(matcher) {
39
+ return {
40
+ respondWith: (response) => {
41
+ this.rules.push({ matcher, response });
42
+ return this;
43
+ }
44
+ };
45
+ }
46
+ /**
47
+ * Set the default response for any prompt that doesn't match a rule.
48
+ */
49
+ onAnyComplete() {
50
+ return {
51
+ respondWith: (response) => {
52
+ this.defaultResponse = response;
53
+ return this;
54
+ }
55
+ };
56
+ }
57
+ /**
58
+ * Configure streaming to yield specific chunks.
59
+ */
60
+ streaming(chunks) {
61
+ this.streamChunks = chunks;
62
+ return this;
63
+ }
64
+ /**
65
+ * Make the LLM throw an error on every call.
66
+ */
67
+ failing(error) {
68
+ this.errorToThrow = error;
69
+ return this;
70
+ }
71
+ /**
72
+ * Configure chatWithTools() to return specific tool calls.
73
+ */
74
+ withToolCalls(calls, content = "") {
75
+ this.toolCallsToReturn = calls;
76
+ this.toolCallResponseContent = content;
77
+ return this;
78
+ }
79
+ /**
80
+ * Build the mock ILLM instance. Called automatically by mockLLM().
81
+ */
82
+ build() {
83
+ const calls = [];
84
+ const toolCallRecords = [];
85
+ const rules = this.rules;
86
+ const defaultResponse = this.defaultResponse;
87
+ const streamChunks = this.streamChunks;
88
+ const errorToThrow = this.errorToThrow;
89
+ const toolCallsToReturn = this.toolCallsToReturn;
90
+ const toolCallResponseContent = this.toolCallResponseContent;
91
+ function resolveResponse(prompt) {
92
+ for (const rule of rules) {
93
+ if (matchesPrompt(rule.matcher, prompt)) {
94
+ return toResponse(rule.response, prompt);
95
+ }
96
+ }
97
+ return toResponse(defaultResponse, prompt);
98
+ }
99
+ const completeFn = vi.fn(async (prompt, options) => {
100
+ if (errorToThrow) {
101
+ throw errorToThrow;
102
+ }
103
+ const response = resolveResponse(prompt);
104
+ calls.push({ prompt, options, response });
105
+ return response;
106
+ });
107
+ const streamFn = vi.fn(async function* (_prompt, _options) {
108
+ if (errorToThrow) {
109
+ throw errorToThrow;
110
+ }
111
+ for (const chunk of streamChunks) {
112
+ yield chunk;
113
+ }
114
+ });
115
+ const chatWithToolsFn = vi.fn(async (messages, options) => {
116
+ if (errorToThrow) {
117
+ throw errorToThrow;
118
+ }
119
+ const lastUserMsg = messages.filter((m) => m.role === "user").pop();
120
+ const prompt = lastUserMsg?.content ?? "";
121
+ const baseResponse = resolveResponse(prompt);
122
+ const response = {
123
+ ...baseResponse,
124
+ content: toolCallsToReturn.length > 0 ? toolCallResponseContent : baseResponse.content,
125
+ toolCalls: toolCallsToReturn.length > 0 ? toolCallsToReturn : void 0
126
+ };
127
+ toolCallRecords.push({ messages, options, response });
128
+ return response;
129
+ });
130
+ const instance = {
131
+ complete: completeFn,
132
+ stream: streamFn,
133
+ chatWithTools: chatWithToolsFn,
134
+ calls,
135
+ toolCalls: toolCallRecords,
136
+ get lastCall() {
137
+ return calls[calls.length - 1];
138
+ },
139
+ resetCalls: () => {
140
+ calls.length = 0;
141
+ toolCallRecords.length = 0;
142
+ completeFn.mockClear();
143
+ streamFn.mockClear();
144
+ chatWithToolsFn.mockClear();
145
+ }
146
+ };
147
+ return instance;
148
+ }
149
+ };
150
+ function matchesPrompt(matcher, prompt) {
151
+ if (typeof matcher === "string") {
152
+ return prompt.includes(matcher);
153
+ }
154
+ if (matcher instanceof RegExp) {
155
+ return matcher.test(prompt);
156
+ }
157
+ return matcher(prompt);
158
+ }
159
+ function toResponse(value, prompt) {
160
+ const resolved = typeof value === "function" ? value(prompt) : value;
161
+ if (typeof resolved === "string") {
162
+ return {
163
+ content: resolved,
164
+ usage: { promptTokens: 0, completionTokens: 0 },
165
+ model: "mock"
166
+ };
167
+ }
168
+ return resolved;
169
+ }
170
+ function mockLLM() {
171
+ const builder = new MockLLMBuilder();
172
+ let built = null;
173
+ function ensureBuilt() {
174
+ if (!built) {
175
+ built = builder.build();
176
+ }
177
+ return built;
178
+ }
179
+ const proxy = new Proxy(builder, {
180
+ get(target, prop, _receiver) {
181
+ if (prop in target && typeof target[prop] === "function") {
182
+ const method = target[prop].bind(target);
183
+ return (...args) => {
184
+ built = null;
185
+ const result = method(...args);
186
+ if (result === target || result && typeof result === "object" && "respondWith" in result) {
187
+ if (result === target) {
188
+ return proxy;
189
+ }
190
+ return {
191
+ respondWith: (...rArgs) => {
192
+ built = null;
193
+ result.respondWith(...rArgs);
194
+ return proxy;
195
+ }
196
+ };
197
+ }
198
+ return result;
199
+ };
200
+ }
201
+ const instance = ensureBuilt();
202
+ return instance[prop];
203
+ }
204
+ });
205
+ return proxy;
206
+ }
207
+ function mockCache(initial) {
208
+ const store = /* @__PURE__ */ new Map();
209
+ const sortedSets = /* @__PURE__ */ new Map();
210
+ if (initial) {
211
+ for (const [key, value] of Object.entries(initial)) {
212
+ store.set(key, { value, expiresAt: null });
213
+ }
214
+ }
215
+ function isExpired(entry) {
216
+ return entry.expiresAt !== null && Date.now() > entry.expiresAt;
217
+ }
218
+ const spies = [];
219
+ function spy(fn) {
220
+ const s = vi.fn(fn);
221
+ spies.push(s);
222
+ return s;
223
+ }
224
+ const getFn = spy(async (key) => {
225
+ const entry = store.get(key);
226
+ if (!entry || isExpired(entry)) {
227
+ if (entry) {
228
+ store.delete(key);
229
+ }
230
+ return null;
231
+ }
232
+ return entry.value;
233
+ });
234
+ const setFn = spy(async (key, value, ttl) => {
235
+ store.set(key, {
236
+ value,
237
+ expiresAt: ttl ? Date.now() + ttl : null
238
+ });
239
+ });
240
+ const deleteFn = spy(async (key) => {
241
+ store.delete(key);
242
+ });
243
+ const clearFn = spy(async (pattern) => {
244
+ if (!pattern) {
245
+ store.clear();
246
+ return;
247
+ }
248
+ const prefix = pattern.replace(/\*$/, "");
249
+ for (const key of store.keys()) {
250
+ if (key.startsWith(prefix)) {
251
+ store.delete(key);
252
+ }
253
+ }
254
+ });
255
+ const zaddFn = spy(async (key, score, member) => {
256
+ if (!sortedSets.has(key)) {
257
+ sortedSets.set(key, []);
258
+ }
259
+ const set = sortedSets.get(key);
260
+ const idx = set.findIndex((m) => m.member === member);
261
+ if (idx >= 0) {
262
+ set.splice(idx, 1);
263
+ }
264
+ set.push({ score, member });
265
+ set.sort((a, b) => a.score - b.score);
266
+ });
267
+ const zrangebyscoreFn = spy(async (key, min, max) => {
268
+ const set = sortedSets.get(key);
269
+ if (!set) {
270
+ return [];
271
+ }
272
+ return set.filter((m) => m.score >= min && m.score <= max).map((m) => m.member);
273
+ });
274
+ const zremFn = spy(async (key, member) => {
275
+ const set = sortedSets.get(key);
276
+ if (!set) {
277
+ return;
278
+ }
279
+ const idx = set.findIndex((m) => m.member === member);
280
+ if (idx >= 0) {
281
+ set.splice(idx, 1);
282
+ }
283
+ });
284
+ const setIfNotExistsFn = spy(async (key, value, ttl) => {
285
+ const existing = store.get(key);
286
+ if (existing && !isExpired(existing)) {
287
+ return false;
288
+ }
289
+ store.set(key, {
290
+ value,
291
+ expiresAt: ttl ? Date.now() + ttl : null
292
+ });
293
+ return true;
294
+ });
295
+ const instance = {
296
+ get: getFn,
297
+ set: setFn,
298
+ delete: deleteFn,
299
+ clear: clearFn,
300
+ zadd: zaddFn,
301
+ zrangebyscore: zrangebyscoreFn,
302
+ zrem: zremFn,
303
+ setIfNotExists: setIfNotExistsFn,
304
+ get store() {
305
+ return store;
306
+ },
307
+ get sortedSets() {
308
+ return sortedSets;
309
+ },
310
+ reset: () => {
311
+ store.clear();
312
+ sortedSets.clear();
313
+ for (const s of spies) {
314
+ s.mockClear();
315
+ }
316
+ }
317
+ };
318
+ return instance;
319
+ }
320
+ function mockStorage(initial) {
321
+ const files = /* @__PURE__ */ new Map();
322
+ if (initial) {
323
+ for (const [path, content] of Object.entries(initial)) {
324
+ files.set(path, typeof content === "string" ? Buffer.from(content) : content);
325
+ }
326
+ }
327
+ const readFn = vi.fn(async (path) => {
328
+ return files.get(path) ?? null;
329
+ });
330
+ const writeFn = vi.fn(async (path, data) => {
331
+ files.set(path, data);
332
+ });
333
+ const deleteFn = vi.fn(async (path) => {
334
+ files.delete(path);
335
+ });
336
+ const listFn = vi.fn(async (prefix) => {
337
+ const result = [];
338
+ for (const path of files.keys()) {
339
+ if (path.startsWith(prefix)) {
340
+ result.push(path);
341
+ }
342
+ }
343
+ return result.sort();
344
+ });
345
+ const existsFn = vi.fn(async (path) => {
346
+ return files.has(path);
347
+ });
348
+ const instance = {
349
+ read: readFn,
350
+ write: writeFn,
351
+ delete: deleteFn,
352
+ list: listFn,
353
+ exists: existsFn,
354
+ get files() {
355
+ return files;
356
+ },
357
+ reset: () => {
358
+ files.clear();
359
+ readFn.mockClear();
360
+ writeFn.mockClear();
361
+ deleteFn.mockClear();
362
+ listFn.mockClear();
363
+ existsFn.mockClear();
364
+ }
365
+ };
366
+ return instance;
367
+ }
368
+ function mockLogger(sharedMessages) {
369
+ const messages = sharedMessages ?? [];
370
+ function createSimpleLogMethod(level) {
371
+ return vi.fn((msg, meta) => {
372
+ messages.push({ level, msg, meta });
373
+ });
374
+ }
375
+ function createErrorLogMethod(level) {
376
+ return vi.fn((msg, error, meta) => {
377
+ messages.push({ level, msg, error, meta });
378
+ });
379
+ }
380
+ const instance = {
381
+ trace: createSimpleLogMethod("trace"),
382
+ debug: createSimpleLogMethod("debug"),
383
+ info: createSimpleLogMethod("info"),
384
+ warn: createSimpleLogMethod("warn"),
385
+ error: createErrorLogMethod("error"),
386
+ fatal: createErrorLogMethod("fatal"),
387
+ child: (_bindings) => mockLogger(messages),
388
+ get messages() {
389
+ return messages;
390
+ },
391
+ reset: () => {
392
+ messages.length = 0;
393
+ instance.trace.mockClear();
394
+ instance.debug.mockClear();
395
+ instance.info.mockClear();
396
+ instance.warn.mockClear();
397
+ instance.error.mockClear();
398
+ instance.fatal.mockClear();
399
+ }
400
+ };
401
+ return instance;
402
+ }
403
+ function createMockTrace() {
404
+ return {
405
+ traceId: "test-trace-id",
406
+ spanId: "test-span-id",
407
+ parentSpanId: void 0,
408
+ addEvent: () => {
409
+ },
410
+ setAttribute: () => {
411
+ },
412
+ recordError: () => {
413
+ }
414
+ };
415
+ }
416
+ function createMockUI() {
417
+ const messages = [];
418
+ const mockColor = (text) => text;
419
+ const mockColors = {
420
+ success: mockColor,
421
+ error: mockColor,
422
+ warning: mockColor,
423
+ info: mockColor,
424
+ primary: mockColor,
425
+ accent: mockColor,
426
+ highlight: mockColor,
427
+ secondary: mockColor,
428
+ emphasis: mockColor,
429
+ muted: mockColor,
430
+ foreground: mockColor,
431
+ dim: mockColor,
432
+ bold: mockColor,
433
+ underline: mockColor,
434
+ inverse: mockColor
435
+ };
436
+ return {
437
+ colors: mockColors,
438
+ symbols: {
439
+ success: "+",
440
+ error: "x",
441
+ warning: "!",
442
+ info: "i",
443
+ bullet: "-",
444
+ clock: "T",
445
+ folder: "D",
446
+ package: "P",
447
+ pointer: ">",
448
+ section: "#",
449
+ separator: "-",
450
+ border: "|",
451
+ topLeft: "+",
452
+ topRight: "+",
453
+ bottomLeft: "+",
454
+ bottomRight: "+",
455
+ leftT: "+",
456
+ rightT: "+"
457
+ },
458
+ write: vi.fn((text) => messages.push(`WRITE: ${text}`)),
459
+ info: vi.fn((msg) => messages.push(`INFO: ${msg}`)),
460
+ success: vi.fn((msg) => messages.push(`SUCCESS: ${msg}`)),
461
+ warn: vi.fn((msg) => messages.push(`WARN: ${msg}`)),
462
+ error: vi.fn((err) => messages.push(`ERROR: ${err instanceof Error ? err.message : err}`)),
463
+ debug: vi.fn((msg) => messages.push(`DEBUG: ${msg}`)),
464
+ spinner: vi.fn((msg) => {
465
+ messages.push(`SPINNER: ${msg}`);
466
+ return {
467
+ update: vi.fn((m) => messages.push(`SPINNER UPDATE: ${m}`)),
468
+ succeed: vi.fn((m) => messages.push(`SPINNER SUCCEED: ${m ?? msg}`)),
469
+ fail: vi.fn((m) => messages.push(`SPINNER FAIL: ${m ?? msg}`)),
470
+ stop: vi.fn()
471
+ };
472
+ }),
473
+ table: vi.fn((data) => messages.push(`TABLE: ${JSON.stringify(data)}`)),
474
+ json: vi.fn((data) => messages.push(`JSON: ${JSON.stringify(data)}`)),
475
+ newline: vi.fn(() => messages.push("")),
476
+ divider: vi.fn(() => messages.push("-".repeat(40))),
477
+ box: vi.fn((content, title) => {
478
+ if (title) {
479
+ messages.push(`+- ${title} -+`);
480
+ }
481
+ messages.push(content);
482
+ if (title) {
483
+ messages.push(`+${"-".repeat(title.length + 4)}+`);
484
+ }
485
+ }),
486
+ sideBox: vi.fn((options) => {
487
+ messages.push(`+- ${options.title} -+`);
488
+ if (options.summary) {
489
+ Object.entries(options.summary).forEach(([key, value]) => {
490
+ messages.push(` ${key}: ${value}`);
491
+ });
492
+ }
493
+ if (options.sections) {
494
+ options.sections.forEach((section) => {
495
+ if (section.header) {
496
+ messages.push(` ${section.header}:`);
497
+ }
498
+ section.items.forEach((item) => messages.push(` ${item}`));
499
+ });
500
+ }
501
+ if (options.timing) {
502
+ messages.push(` Timing: ${options.timing}ms`);
503
+ }
504
+ messages.push("+" + "-".repeat(options.title.length + 4) + "+");
505
+ }),
506
+ confirm: vi.fn(async () => true),
507
+ prompt: vi.fn(async () => "")
508
+ };
509
+ }
510
+ function createMockRuntime() {
511
+ const mockFS = {
512
+ readFile: vi.fn(async () => ""),
513
+ readFileBuffer: vi.fn(async () => new Uint8Array()),
514
+ writeFile: vi.fn(async () => {
515
+ }),
516
+ readdir: vi.fn(async () => []),
517
+ readdirWithStats: vi.fn(async () => []),
518
+ mkdir: vi.fn(async () => {
519
+ }),
520
+ rm: vi.fn(async () => {
521
+ }),
522
+ copy: vi.fn(async () => {
523
+ }),
524
+ move: vi.fn(async () => {
525
+ }),
526
+ glob: vi.fn(async () => []),
527
+ stat: vi.fn(async () => ({
528
+ isFile: () => false,
529
+ isDirectory: () => false,
530
+ size: 0,
531
+ mtime: Date.now(),
532
+ ctime: Date.now()
533
+ })),
534
+ exists: vi.fn(async () => false),
535
+ resolve: (path) => path,
536
+ relative: (path) => path,
537
+ join: (...segments) => segments.join("/"),
538
+ dirname: (path) => path.split("/").slice(0, -1).join("/"),
539
+ basename: (path) => path.split("/").pop() ?? "",
540
+ extname: (path) => {
541
+ const base = path.split("/").pop() ?? "";
542
+ const idx = base.lastIndexOf(".");
543
+ return idx > 0 ? base.slice(idx) : "";
544
+ }
545
+ };
546
+ const mockFetch = vi.fn(async () => new Response("mock"));
547
+ const mockEnv = vi.fn((_key) => void 0);
548
+ return {
549
+ fs: mockFS,
550
+ fetch: mockFetch,
551
+ env: mockEnv
552
+ };
553
+ }
554
+ function createMockEnvironmentAPI() {
555
+ return {
556
+ create: vi.fn(async () => ({
557
+ environmentId: "env_mock_1",
558
+ provider: "mock",
559
+ status: "ready",
560
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
561
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
562
+ })),
563
+ status: vi.fn(async (environmentId) => ({
564
+ environmentId,
565
+ status: "ready",
566
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
567
+ })),
568
+ destroy: vi.fn(async () => {
569
+ }),
570
+ renewLease: vi.fn(async () => ({
571
+ leaseId: "lease_mock_1",
572
+ acquiredAt: (/* @__PURE__ */ new Date()).toISOString(),
573
+ expiresAt: new Date(Date.now() + 10 * 60 * 1e3).toISOString()
574
+ }))
575
+ };
576
+ }
577
+ function createMockWorkspaceAPI() {
578
+ return {
579
+ materialize: vi.fn(async () => ({
580
+ workspaceId: "ws_mock_1",
581
+ provider: "mock",
582
+ status: "ready",
583
+ rootPath: "/tmp/ws_mock_1",
584
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
585
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
586
+ })),
587
+ attach: vi.fn(async (request) => ({
588
+ workspaceId: request.workspaceId,
589
+ environmentId: request.environmentId,
590
+ mountPath: request.mountPath ?? "/workspace",
591
+ attachedAt: (/* @__PURE__ */ new Date()).toISOString()
592
+ })),
593
+ release: vi.fn(async () => {
594
+ }),
595
+ status: vi.fn(async (workspaceId) => ({
596
+ workspaceId,
597
+ status: "ready",
598
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
599
+ }))
600
+ };
601
+ }
602
+ function createMockSnapshotAPI() {
603
+ return {
604
+ capture: vi.fn(async (request) => ({
605
+ snapshotId: request.snapshotId ?? "snap_mock_1",
606
+ provider: "mock",
607
+ status: "ready",
608
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
609
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
610
+ workspaceId: request.workspaceId,
611
+ environmentId: request.environmentId
612
+ })),
613
+ restore: vi.fn(async (request) => ({
614
+ snapshotId: request.snapshotId,
615
+ restoredAt: (/* @__PURE__ */ new Date()).toISOString(),
616
+ workspaceId: request.workspaceId,
617
+ environmentId: request.environmentId,
618
+ targetPath: request.targetPath
619
+ })),
620
+ status: vi.fn(async (snapshotId) => ({
621
+ snapshotId,
622
+ status: "ready",
623
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
624
+ })),
625
+ delete: vi.fn(async () => {
626
+ }),
627
+ gc: vi.fn(async (request) => ({
628
+ scanned: 0,
629
+ deleted: 0,
630
+ dryRun: request?.dryRun ?? false
631
+ }))
632
+ };
633
+ }
634
+ function createInfraApiMocks() {
635
+ return {
636
+ environment: createMockEnvironmentAPI(),
637
+ workspace: createMockWorkspaceAPI(),
638
+ snapshot: createMockSnapshotAPI()
639
+ };
640
+ }
641
+ function createMockPluginAPI() {
642
+ const infra = createInfraApiMocks();
643
+ return {
644
+ lifecycle: {
645
+ onCleanup: vi.fn()
646
+ },
647
+ state: {
648
+ get: vi.fn(async () => void 0),
649
+ set: vi.fn(async () => {
650
+ }),
651
+ delete: vi.fn(async () => {
652
+ }),
653
+ has: vi.fn(async () => false),
654
+ getMany: vi.fn(async () => /* @__PURE__ */ new Map()),
655
+ setMany: vi.fn(async () => {
656
+ })
657
+ },
658
+ artifacts: {
659
+ write: vi.fn(async () => "/mock/path"),
660
+ list: vi.fn(async () => []),
661
+ read: vi.fn(async () => ""),
662
+ readBuffer: vi.fn(async () => new Uint8Array()),
663
+ exists: vi.fn(async () => false),
664
+ path: vi.fn(() => "/mock/path")
665
+ },
666
+ shell: {
667
+ exec: vi.fn(async () => ({ code: 0, stdout: "", stderr: "", ok: true }))
668
+ },
669
+ events: {
670
+ emit: vi.fn(async () => {
671
+ })
672
+ },
673
+ invoke: {
674
+ call: vi.fn(async (_pluginId, _input, _options) => void 0)
675
+ },
676
+ workflows: {
677
+ run: vi.fn(async () => "mock-run-id"),
678
+ wait: vi.fn(async () => void 0),
679
+ status: vi.fn(async () => null),
680
+ cancel: vi.fn(async () => {
681
+ }),
682
+ list: vi.fn(async () => [])
683
+ },
684
+ jobs: {
685
+ submit: vi.fn(async () => "mock-job-id"),
686
+ schedule: vi.fn(async () => "mock-scheduled-job-id"),
687
+ wait: vi.fn(async () => void 0),
688
+ status: vi.fn(async () => null),
689
+ cancel: vi.fn(async () => false),
690
+ list: vi.fn(async () => [])
691
+ },
692
+ cron: {
693
+ register: vi.fn(async () => {
694
+ }),
695
+ unregister: vi.fn(async () => {
696
+ }),
697
+ list: vi.fn(async () => []),
698
+ pause: vi.fn(async () => {
699
+ }),
700
+ resume: vi.fn(async () => {
701
+ }),
702
+ trigger: vi.fn(async () => {
703
+ })
704
+ },
705
+ environment: infra.environment,
706
+ workspace: infra.workspace,
707
+ snapshot: infra.snapshot
708
+ };
709
+ }
710
+ var createMockPlatformApi = createMockPluginAPI;
711
+ function createMockPluginContextV3(options = {}) {
712
+ return createTestContext(options);
713
+ }
714
+ function createTestContext(options = {}) {
715
+ const {
716
+ pluginId = "test-plugin",
717
+ pluginVersion = "0.0.0",
718
+ host = "cli",
719
+ hostContext,
720
+ config,
721
+ cwd = process.cwd(),
722
+ outdir,
723
+ tenantId,
724
+ signal,
725
+ platform: platformOverrides,
726
+ ui: uiOverrides,
727
+ syncSingleton = true
728
+ } = options;
729
+ const resolvedOutdir = outdir ?? `${cwd}/.kb/output`;
730
+ const defaultHostContext = hostContext ?? (() => {
731
+ switch (host) {
732
+ case "cli":
733
+ return { host: "cli", argv: ["test"], flags: {} };
734
+ case "rest":
735
+ return {
736
+ host: "rest",
737
+ method: "GET",
738
+ path: "/test",
739
+ requestId: "test-req-001",
740
+ traceId: "test-trace-001"
741
+ };
742
+ case "workflow":
743
+ return { host: "workflow", workflowId: "test-wf", runId: "test-run", stepId: "test-step" };
744
+ case "webhook":
745
+ return { host: "webhook", event: "test:event" };
746
+ }
747
+ })();
748
+ const defaultLogger = mockLogger();
749
+ const defaultLLM = mockLLM();
750
+ const defaultCache = mockCache();
751
+ const defaultStorage = mockStorage();
752
+ const defaultPlatform = {
753
+ logger: defaultLogger,
754
+ llm: defaultLLM,
755
+ embeddings: {
756
+ embed: vi.fn(async () => []),
757
+ embedBatch: vi.fn(async () => [[]]),
758
+ dimensions: 1536,
759
+ getDimensions: vi.fn(async () => 1536)
760
+ },
761
+ vectorStore: {
762
+ search: vi.fn(async () => []),
763
+ upsert: vi.fn(async () => {
764
+ }),
765
+ delete: vi.fn(async () => {
766
+ }),
767
+ count: vi.fn(async () => 0)
768
+ },
769
+ cache: defaultCache,
770
+ storage: defaultStorage,
771
+ analytics: {
772
+ track: vi.fn(async () => {
773
+ }),
774
+ identify: vi.fn(async () => {
775
+ }),
776
+ flush: vi.fn(async () => {
777
+ })
778
+ },
779
+ eventBus: {
780
+ publish: vi.fn(async () => {
781
+ }),
782
+ subscribe: vi.fn(() => () => {
783
+ })
784
+ },
785
+ logs: {
786
+ query: vi.fn(async () => ({ logs: [], total: 0, hasMore: false, source: "buffer" })),
787
+ getById: vi.fn(async () => null),
788
+ search: vi.fn(async () => ({ logs: [], total: 0, hasMore: false })),
789
+ subscribe: vi.fn(() => () => {
790
+ }),
791
+ getStats: vi.fn(async () => ({})),
792
+ getCapabilities: vi.fn(() => ({ hasBuffer: false, hasPersistence: false, hasSearch: false, hasStreaming: false }))
793
+ }
794
+ };
795
+ const finalPlatform = {
796
+ ...defaultPlatform,
797
+ ...platformOverrides
798
+ };
799
+ let cleanupFn = () => {
800
+ };
801
+ if (syncSingleton) {
802
+ const result = setupTestPlatform({
803
+ llm: finalPlatform.llm,
804
+ cache: finalPlatform.cache,
805
+ storage: finalPlatform.storage,
806
+ logger: finalPlatform.logger,
807
+ analytics: finalPlatform.analytics,
808
+ embeddings: finalPlatform.embeddings,
809
+ vectorStore: finalPlatform.vectorStore,
810
+ eventBus: finalPlatform.eventBus
811
+ });
812
+ cleanupFn = result.cleanup;
813
+ }
814
+ const defaultUI = createMockUI();
815
+ const finalUI = {
816
+ ...defaultUI,
817
+ ...uiOverrides
818
+ };
819
+ const ctx = {
820
+ host,
821
+ requestId: "test-trace:test-span",
822
+ pluginId,
823
+ pluginVersion,
824
+ tenantId,
825
+ cwd,
826
+ outdir: resolvedOutdir,
827
+ config,
828
+ signal,
829
+ trace: createMockTrace(),
830
+ hostContext: defaultHostContext,
831
+ ui: finalUI,
832
+ platform: finalPlatform,
833
+ runtime: createMockRuntime(),
834
+ api: createMockPluginAPI()
835
+ };
836
+ return { ctx, cleanup: cleanupFn };
837
+ }
838
+
839
+ // src/test-command.ts
840
+ async function testCommand(handler, options = {}) {
841
+ const {
842
+ flags,
843
+ argv = [],
844
+ query,
845
+ body,
846
+ params,
847
+ input: rawInput,
848
+ host = "cli",
849
+ config,
850
+ cwd,
851
+ tenantId,
852
+ signal,
853
+ platform: platform2,
854
+ ui,
855
+ syncSingleton = true
856
+ } = options;
857
+ const ctxOptions = {
858
+ host,
859
+ config,
860
+ cwd,
861
+ tenantId,
862
+ signal,
863
+ platform: platform2,
864
+ ui,
865
+ syncSingleton
866
+ };
867
+ const { ctx, cleanup } = createTestContext(ctxOptions);
868
+ let input;
869
+ if (rawInput !== void 0) {
870
+ input = rawInput;
871
+ } else if (host === "rest") {
872
+ input = {
873
+ ...query !== void 0 ? { query } : {},
874
+ ...body !== void 0 ? { body } : {},
875
+ ...params !== void 0 ? { params } : {}
876
+ };
877
+ } else {
878
+ input = { flags: flags ?? {}, argv };
879
+ }
880
+ let raw;
881
+ try {
882
+ raw = await handler.execute(ctx, input);
883
+ } finally {
884
+ await handler.cleanup?.();
885
+ }
886
+ let exitCode = 0;
887
+ let result;
888
+ let meta;
889
+ if (raw != null && typeof raw === "object" && "exitCode" in raw) {
890
+ const cmdResult = raw;
891
+ exitCode = cmdResult.exitCode;
892
+ result = cmdResult.result;
893
+ meta = cmdResult.meta;
894
+ } else if (raw === void 0 || raw === null) {
895
+ exitCode = 0;
896
+ result = void 0;
897
+ meta = void 0;
898
+ } else {
899
+ exitCode = 0;
900
+ result = raw;
901
+ meta = void 0;
902
+ }
903
+ return {
904
+ exitCode,
905
+ result,
906
+ meta,
907
+ raw,
908
+ ui: ctx.ui,
909
+ ctx,
910
+ cleanup
911
+ };
912
+ }
913
+
914
+ export { createInfraApiMocks, createMockEnvironmentAPI, createMockPlatformApi, createMockPluginAPI, createMockPluginContextV3, createMockRuntime, createMockSnapshotAPI, createMockTrace, createMockUI, createMockWorkspaceAPI, createTestContext, mockCache, mockLLM, mockLogger, mockStorage, setupTestPlatform, testCommand };
915
+ //# sourceMappingURL=index.js.map
916
+ //# sourceMappingURL=index.js.map