@sparkleideas/testing 3.0.0-alpha.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +547 -0
  2. package/__tests__/framework.test.ts +21 -0
  3. package/package.json +61 -0
  4. package/src/fixtures/agent-fixtures.ts +793 -0
  5. package/src/fixtures/agents.ts +212 -0
  6. package/src/fixtures/configurations.ts +491 -0
  7. package/src/fixtures/index.ts +21 -0
  8. package/src/fixtures/mcp-fixtures.ts +1030 -0
  9. package/src/fixtures/memory-entries.ts +328 -0
  10. package/src/fixtures/memory-fixtures.ts +750 -0
  11. package/src/fixtures/swarm-fixtures.ts +837 -0
  12. package/src/fixtures/tasks.ts +309 -0
  13. package/src/helpers/assertion-helpers.ts +616 -0
  14. package/src/helpers/assertions.ts +286 -0
  15. package/src/helpers/create-mock.ts +200 -0
  16. package/src/helpers/index.ts +182 -0
  17. package/src/helpers/mock-factory.ts +711 -0
  18. package/src/helpers/setup-teardown.ts +678 -0
  19. package/src/helpers/swarm-instance.ts +326 -0
  20. package/src/helpers/test-application.ts +310 -0
  21. package/src/helpers/test-utils.ts +670 -0
  22. package/src/index.ts +232 -0
  23. package/src/mocks/index.ts +29 -0
  24. package/src/mocks/mock-mcp-client.ts +723 -0
  25. package/src/mocks/mock-services.ts +793 -0
  26. package/src/regression/api-contract.ts +473 -0
  27. package/src/regression/index.ts +46 -0
  28. package/src/regression/integration-regression.ts +416 -0
  29. package/src/regression/performance-baseline.ts +356 -0
  30. package/src/regression/regression-runner.ts +339 -0
  31. package/src/regression/security-regression.ts +331 -0
  32. package/src/setup.ts +127 -0
  33. package/src/v2-compat/api-compat.test.ts +590 -0
  34. package/src/v2-compat/cli-compat.test.ts +484 -0
  35. package/src/v2-compat/compatibility-validator.ts +1072 -0
  36. package/src/v2-compat/hooks-compat.test.ts +602 -0
  37. package/src/v2-compat/index.ts +58 -0
  38. package/src/v2-compat/mcp-compat.test.ts +557 -0
  39. package/src/v2-compat/report-generator.ts +441 -0
  40. package/tmp.json +0 -0
  41. package/tsconfig.json +20 -0
  42. package/vitest.config.ts +12 -0
@@ -0,0 +1,711 @@
1
+ /**
2
+ * @sparkleideas/testing - Mock Factory
3
+ *
4
+ * Factory functions for creating comprehensive mocks of V3 services and components.
5
+ * Implements London School TDD patterns with behavior verification.
6
+ */
7
+ import { vi, type Mock } from 'vitest';
8
+ import { createMock, createMockWithBehavior, type MockedInterface } from './create-mock.js';
9
+ import type { V3AgentType, AgentInstance, AgentMetrics } from '../fixtures/agent-fixtures.js';
10
+ import type { MemoryEntry, SearchResult, VectorQuery } from '../fixtures/memory-fixtures.js';
11
+ import type { SwarmState, SwarmConfig, SwarmTask, SwarmTaskResult, CoordinationResult } from '../fixtures/swarm-fixtures.js';
12
+ import type { MCPTool, MCPToolResult, MCPServerConfig, MCPSessionContext } from '../fixtures/mcp-fixtures.js';
13
+
14
+ /**
15
+ * Event bus interface for DDD events
16
+ */
17
+ export interface IEventBus {
18
+ publish(event: DomainEvent): Promise<void>;
19
+ subscribe(eventType: string, handler: EventHandler): () => void;
20
+ unsubscribe(eventType: string, handler: EventHandler): void;
21
+ getSubscriberCount(eventType: string): number;
22
+ }
23
+
24
+ /**
25
+ * Domain event interface
26
+ */
27
+ export interface DomainEvent {
28
+ id: string;
29
+ type: string;
30
+ payload: unknown;
31
+ timestamp: Date;
32
+ correlationId?: string;
33
+ causationId?: string;
34
+ metadata?: Record<string, unknown>;
35
+ }
36
+
37
+ /**
38
+ * Event handler type
39
+ */
40
+ export type EventHandler = (event: DomainEvent) => Promise<void>;
41
+
42
+ /**
43
+ * Task manager interface
44
+ */
45
+ export interface ITaskManager {
46
+ create(definition: TaskDefinition): Promise<Task>;
47
+ execute(taskId: string): Promise<TaskResult>;
48
+ cancel(taskId: string): Promise<void>;
49
+ getStatus(taskId: string): Promise<TaskStatus>;
50
+ getTask(taskId: string): Promise<Task | null>;
51
+ listTasks(filters?: TaskFilters): Promise<Task[]>;
52
+ }
53
+
54
+ /**
55
+ * Task definition interface
56
+ */
57
+ export interface TaskDefinition {
58
+ name: string;
59
+ type: string;
60
+ payload: unknown;
61
+ priority?: number;
62
+ dependencies?: string[];
63
+ deadline?: Date;
64
+ }
65
+
66
+ /**
67
+ * Task interface
68
+ */
69
+ export interface Task {
70
+ id: string;
71
+ name: string;
72
+ type: string;
73
+ status: TaskStatus;
74
+ payload: unknown;
75
+ priority: number;
76
+ createdAt: Date;
77
+ startedAt?: Date;
78
+ completedAt?: Date;
79
+ }
80
+
81
+ /**
82
+ * Task status type
83
+ */
84
+ export type TaskStatus = 'pending' | 'running' | 'completed' | 'failed' | 'cancelled';
85
+
86
+ /**
87
+ * Task result interface
88
+ */
89
+ export interface TaskResult {
90
+ taskId: string;
91
+ success: boolean;
92
+ output?: unknown;
93
+ error?: Error;
94
+ duration: number;
95
+ }
96
+
97
+ /**
98
+ * Task filters interface
99
+ */
100
+ export interface TaskFilters {
101
+ status?: TaskStatus;
102
+ type?: string;
103
+ assignedTo?: string;
104
+ limit?: number;
105
+ offset?: number;
106
+ }
107
+
108
+ /**
109
+ * Agent lifecycle interface
110
+ */
111
+ export interface IAgentLifecycle {
112
+ spawn(config: AgentConfig): Promise<AgentSpawnResult>;
113
+ terminate(agentId: string, options?: TerminateOptions): Promise<void>;
114
+ getAgent(agentId: string): Promise<AgentInstance | null>;
115
+ listAgents(filters?: AgentFilters): Promise<AgentInstance[]>;
116
+ getMetrics(agentId: string): Promise<AgentMetrics>;
117
+ healthCheck(agentId: string): Promise<AgentHealthCheck>;
118
+ }
119
+
120
+ /**
121
+ * Agent config interface
122
+ */
123
+ export interface AgentConfig {
124
+ type: V3AgentType;
125
+ name: string;
126
+ capabilities?: string[];
127
+ priority?: number;
128
+ }
129
+
130
+ /**
131
+ * Agent spawn result interface
132
+ */
133
+ export interface AgentSpawnResult {
134
+ agent: AgentInstance;
135
+ sessionId: string;
136
+ startupTime: number;
137
+ success: boolean;
138
+ }
139
+
140
+ /**
141
+ * Terminate options interface
142
+ */
143
+ export interface TerminateOptions {
144
+ graceful?: boolean;
145
+ timeout?: number;
146
+ cancelTasks?: boolean;
147
+ }
148
+
149
+ /**
150
+ * Agent filters interface
151
+ */
152
+ export interface AgentFilters {
153
+ type?: V3AgentType;
154
+ status?: string;
155
+ capability?: string;
156
+ }
157
+
158
+ /**
159
+ * Agent health check interface
160
+ */
161
+ export interface AgentHealthCheck {
162
+ healthy: boolean;
163
+ issues?: string[];
164
+ lastActivity: Date;
165
+ metrics: AgentMetrics;
166
+ }
167
+
168
+ /**
169
+ * Memory service interface
170
+ */
171
+ export interface IMemoryService {
172
+ store(key: string, value: unknown, metadata?: Record<string, unknown>): Promise<void>;
173
+ retrieve(key: string): Promise<unknown>;
174
+ search(query: VectorQuery): Promise<SearchResult[]>;
175
+ delete(key: string): Promise<void>;
176
+ clear(): Promise<void>;
177
+ getStats(): Promise<MemoryStats>;
178
+ createIndex(name: string, config: IndexConfig): Promise<void>;
179
+ }
180
+
181
+ /**
182
+ * Memory stats interface
183
+ */
184
+ export interface MemoryStats {
185
+ totalEntries: number;
186
+ totalSizeBytes: number;
187
+ vectorCount: number;
188
+ cacheHitRate: number;
189
+ }
190
+
191
+ /**
192
+ * Index config interface
193
+ */
194
+ export interface IndexConfig {
195
+ dimensions: number;
196
+ metric: 'cosine' | 'euclidean' | 'dot';
197
+ M?: number;
198
+ efConstruction?: number;
199
+ }
200
+
201
+ /**
202
+ * Security service interface
203
+ */
204
+ export interface ISecurityService {
205
+ validatePath(path: string): boolean;
206
+ validateInput(input: string, options?: InputValidationOptions): { valid: boolean; errors?: string[] };
207
+ hashPassword(password: string): Promise<string>;
208
+ verifyPassword(password: string, hash: string): Promise<boolean>;
209
+ generateToken(payload: Record<string, unknown>, expiresIn?: number): Promise<string>;
210
+ verifyToken(token: string): Promise<Record<string, unknown>>;
211
+ executeSecurely(command: string, options?: ExecuteOptions): Promise<ExecuteResult>;
212
+ }
213
+
214
+ /**
215
+ * Input validation options
216
+ */
217
+ export interface InputValidationOptions {
218
+ maxLength?: number;
219
+ allowedChars?: RegExp;
220
+ sanitize?: boolean;
221
+ }
222
+
223
+ /**
224
+ * Execute options interface
225
+ */
226
+ export interface ExecuteOptions {
227
+ timeout?: number;
228
+ cwd?: string;
229
+ shell?: boolean;
230
+ allowedCommands?: string[];
231
+ }
232
+
233
+ /**
234
+ * Execute result interface
235
+ */
236
+ export interface ExecuteResult {
237
+ stdout: string;
238
+ stderr: string;
239
+ exitCode: number;
240
+ duration: number;
241
+ }
242
+
243
+ /**
244
+ * Swarm coordinator interface
245
+ */
246
+ export interface ISwarmCoordinator {
247
+ initialize(config: SwarmConfig): Promise<SwarmState>;
248
+ coordinate(agents: string[], task: SwarmTask): Promise<CoordinationResult>;
249
+ shutdown(graceful?: boolean): Promise<void>;
250
+ getState(): SwarmState;
251
+ addAgent(agentId: string): Promise<void>;
252
+ removeAgent(agentId: string): Promise<void>;
253
+ broadcast(message: unknown): Promise<void>;
254
+ }
255
+
256
+ /**
257
+ * MCP client interface
258
+ */
259
+ export interface IMCPClient {
260
+ connect(): Promise<void>;
261
+ disconnect(): Promise<void>;
262
+ callTool(name: string, params: Record<string, unknown>): Promise<MCPToolResult>;
263
+ listTools(): Promise<MCPTool[]>;
264
+ isConnected(): boolean;
265
+ getSession(): MCPSessionContext | null;
266
+ }
267
+
268
+ /**
269
+ * Logger interface
270
+ */
271
+ export interface ILogger {
272
+ debug(message: string, context?: Record<string, unknown>): void;
273
+ info(message: string, context?: Record<string, unknown>): void;
274
+ warn(message: string, context?: Record<string, unknown>): void;
275
+ error(message: string, error?: Error, context?: Record<string, unknown>): void;
276
+ }
277
+
278
+ /**
279
+ * Create mock event bus with behavior tracking
280
+ */
281
+ export function createMockEventBus(): MockedInterface<IEventBus> & { publishedEvents: DomainEvent[] } {
282
+ const publishedEvents: DomainEvent[] = [];
283
+ const subscribers = new Map<string, Set<EventHandler>>();
284
+
285
+ const mock = createMock<IEventBus>();
286
+
287
+ mock.publish.mockImplementation(async (event: DomainEvent) => {
288
+ publishedEvents.push(event);
289
+ const handlers = subscribers.get(event.type);
290
+ if (handlers) {
291
+ for (const handler of handlers) {
292
+ await handler(event);
293
+ }
294
+ }
295
+ });
296
+
297
+ mock.subscribe.mockImplementation((eventType: string, handler: EventHandler) => {
298
+ if (!subscribers.has(eventType)) {
299
+ subscribers.set(eventType, new Set());
300
+ }
301
+ subscribers.get(eventType)!.add(handler);
302
+ return () => subscribers.get(eventType)?.delete(handler);
303
+ });
304
+
305
+ mock.unsubscribe.mockImplementation((eventType: string, handler: EventHandler) => {
306
+ subscribers.get(eventType)?.delete(handler);
307
+ });
308
+
309
+ mock.getSubscriberCount.mockImplementation((eventType: string) => {
310
+ return subscribers.get(eventType)?.size ?? 0;
311
+ });
312
+
313
+ return Object.assign(mock, { publishedEvents });
314
+ }
315
+
316
+ /**
317
+ * Create mock task manager with realistic behavior
318
+ */
319
+ export function createMockTaskManager(): MockedInterface<ITaskManager> & { tasks: Map<string, Task> } {
320
+ const tasks = new Map<string, Task>();
321
+ let taskCounter = 0;
322
+
323
+ const mock = createMock<ITaskManager>();
324
+
325
+ mock.create.mockImplementation(async (definition: TaskDefinition) => {
326
+ const task: Task = {
327
+ id: `task-${++taskCounter}`,
328
+ name: definition.name,
329
+ type: definition.type,
330
+ status: 'pending',
331
+ payload: definition.payload,
332
+ priority: definition.priority ?? 50,
333
+ createdAt: new Date(),
334
+ };
335
+ tasks.set(task.id, task);
336
+ return task;
337
+ });
338
+
339
+ mock.execute.mockImplementation(async (taskId: string) => {
340
+ const task = tasks.get(taskId);
341
+ if (!task) {
342
+ throw new Error(`Task ${taskId} not found`);
343
+ }
344
+ task.status = 'running';
345
+ task.startedAt = new Date();
346
+
347
+ // Simulate execution
348
+ await new Promise(resolve => setTimeout(resolve, 10));
349
+
350
+ task.status = 'completed';
351
+ task.completedAt = new Date();
352
+
353
+ return {
354
+ taskId,
355
+ success: true,
356
+ duration: task.completedAt.getTime() - task.startedAt.getTime(),
357
+ };
358
+ });
359
+
360
+ mock.cancel.mockImplementation(async (taskId: string) => {
361
+ const task = tasks.get(taskId);
362
+ if (task) {
363
+ task.status = 'cancelled';
364
+ }
365
+ });
366
+
367
+ mock.getStatus.mockImplementation(async (taskId: string) => {
368
+ const task = tasks.get(taskId);
369
+ return task?.status ?? 'pending';
370
+ });
371
+
372
+ mock.getTask.mockImplementation(async (taskId: string) => {
373
+ return tasks.get(taskId) ?? null;
374
+ });
375
+
376
+ mock.listTasks.mockImplementation(async (filters?: TaskFilters) => {
377
+ let result = Array.from(tasks.values());
378
+
379
+ if (filters?.status) {
380
+ result = result.filter(t => t.status === filters.status);
381
+ }
382
+ if (filters?.type) {
383
+ result = result.filter(t => t.type === filters.type);
384
+ }
385
+ if (filters?.limit) {
386
+ result = result.slice(filters.offset ?? 0, (filters.offset ?? 0) + filters.limit);
387
+ }
388
+
389
+ return result;
390
+ });
391
+
392
+ return Object.assign(mock, { tasks });
393
+ }
394
+
395
+ /**
396
+ * Create mock agent lifecycle
397
+ */
398
+ export function createMockAgentLifecycle(): MockedInterface<IAgentLifecycle> & { agents: Map<string, AgentInstance> } {
399
+ const agents = new Map<string, AgentInstance>();
400
+ let agentCounter = 0;
401
+
402
+ const mock = createMock<IAgentLifecycle>();
403
+
404
+ mock.spawn.mockImplementation(async (config: AgentConfig) => {
405
+ const agent: AgentInstance = {
406
+ id: `agent-${config.type}-${++agentCounter}`,
407
+ type: config.type,
408
+ name: config.name,
409
+ status: 'idle',
410
+ capabilities: config.capabilities ?? [],
411
+ createdAt: new Date(),
412
+ };
413
+ agents.set(agent.id, agent);
414
+
415
+ return {
416
+ agent,
417
+ sessionId: `session-${Date.now()}`,
418
+ startupTime: Math.random() * 100 + 50,
419
+ success: true,
420
+ };
421
+ });
422
+
423
+ mock.terminate.mockImplementation(async (agentId: string) => {
424
+ const agent = agents.get(agentId);
425
+ if (agent) {
426
+ agent.status = 'terminated';
427
+ }
428
+ });
429
+
430
+ mock.getAgent.mockImplementation(async (agentId: string) => {
431
+ return agents.get(agentId) ?? null;
432
+ });
433
+
434
+ mock.listAgents.mockImplementation(async (filters?: AgentFilters) => {
435
+ let result = Array.from(agents.values());
436
+
437
+ if (filters?.type) {
438
+ result = result.filter(a => a.type === filters.type);
439
+ }
440
+ if (filters?.status) {
441
+ result = result.filter(a => a.status === filters.status);
442
+ }
443
+ if (filters?.capability) {
444
+ result = result.filter(a => a.capabilities.includes(filters.capability!));
445
+ }
446
+
447
+ return result;
448
+ });
449
+
450
+ mock.getMetrics.mockImplementation(async () => ({
451
+ tasksCompleted: Math.floor(Math.random() * 100),
452
+ tasksFailed: Math.floor(Math.random() * 10),
453
+ avgTaskDuration: Math.random() * 1000,
454
+ totalDuration: Math.random() * 10000,
455
+ errorRate: Math.random() * 0.1,
456
+ memoryUsageMb: Math.random() * 256,
457
+ }));
458
+
459
+ mock.healthCheck.mockImplementation(async (agentId: string) => {
460
+ const agent = agents.get(agentId);
461
+ return {
462
+ healthy: agent?.status !== 'error' && agent?.status !== 'terminated',
463
+ lastActivity: new Date(),
464
+ metrics: {
465
+ tasksCompleted: 50,
466
+ tasksFailed: 1,
467
+ avgTaskDuration: 200,
468
+ totalDuration: 10000,
469
+ errorRate: 0.02,
470
+ memoryUsageMb: 128,
471
+ },
472
+ };
473
+ });
474
+
475
+ return Object.assign(mock, { agents });
476
+ }
477
+
478
+ /**
479
+ * Create mock memory service
480
+ */
481
+ export function createMockMemoryService(): MockedInterface<IMemoryService> & { entries: Map<string, { value: unknown; metadata?: Record<string, unknown> }> } {
482
+ const entries = new Map<string, { value: unknown; metadata?: Record<string, unknown> }>();
483
+
484
+ const mock = createMock<IMemoryService>();
485
+
486
+ mock.store.mockImplementation(async (key: string, value: unknown, metadata?: Record<string, unknown>) => {
487
+ entries.set(key, { value, metadata });
488
+ });
489
+
490
+ mock.retrieve.mockImplementation(async (key: string) => {
491
+ return entries.get(key)?.value ?? null;
492
+ });
493
+
494
+ mock.search.mockImplementation(async () => []);
495
+
496
+ mock.delete.mockImplementation(async (key: string) => {
497
+ entries.delete(key);
498
+ });
499
+
500
+ mock.clear.mockImplementation(async () => {
501
+ entries.clear();
502
+ });
503
+
504
+ mock.getStats.mockImplementation(async () => ({
505
+ totalEntries: entries.size,
506
+ totalSizeBytes: entries.size * 100,
507
+ vectorCount: 0,
508
+ cacheHitRate: 0.85,
509
+ }));
510
+
511
+ mock.createIndex.mockResolvedValue(undefined);
512
+
513
+ return Object.assign(mock, { entries });
514
+ }
515
+
516
+ /**
517
+ * Create mock security service
518
+ */
519
+ export function createMockSecurityService(): MockedInterface<ISecurityService> {
520
+ const mock = createMock<ISecurityService>();
521
+
522
+ mock.validatePath.mockImplementation((path: string) => {
523
+ const blocked = ['../', '~/', '/etc/', '/tmp/', '/var/'];
524
+ return !blocked.some(pattern => path.includes(pattern));
525
+ });
526
+
527
+ mock.validateInput.mockImplementation((input: string, options?: InputValidationOptions) => {
528
+ const maxLength = options?.maxLength ?? 10000;
529
+ if (input.length > maxLength) {
530
+ return { valid: false, errors: [`Input exceeds maximum length of ${maxLength}`] };
531
+ }
532
+ return { valid: true };
533
+ });
534
+
535
+ mock.hashPassword.mockImplementation(async (password: string) => {
536
+ return `hashed:${Buffer.from(password).toString('base64')}`;
537
+ });
538
+
539
+ mock.verifyPassword.mockImplementation(async (password: string, hash: string) => {
540
+ return hash === `hashed:${Buffer.from(password).toString('base64')}`;
541
+ });
542
+
543
+ mock.generateToken.mockImplementation(async (payload: Record<string, unknown>) => {
544
+ return `token:${Buffer.from(JSON.stringify(payload)).toString('base64')}`;
545
+ });
546
+
547
+ mock.verifyToken.mockImplementation(async (token: string) => {
548
+ if (!token.startsWith('token:')) {
549
+ throw new Error('Invalid token');
550
+ }
551
+ return JSON.parse(Buffer.from(token.slice(6), 'base64').toString());
552
+ });
553
+
554
+ mock.executeSecurely.mockImplementation(async () => ({
555
+ stdout: '',
556
+ stderr: '',
557
+ exitCode: 0,
558
+ duration: 100,
559
+ }));
560
+
561
+ return mock;
562
+ }
563
+
564
+ /**
565
+ * Create mock swarm coordinator
566
+ */
567
+ export function createMockSwarmCoordinator(): MockedInterface<ISwarmCoordinator> & { state: SwarmState } {
568
+ const state: SwarmState = {
569
+ id: 'swarm-test',
570
+ topology: 'hierarchical-mesh',
571
+ status: 'active',
572
+ agentCount: 0,
573
+ activeAgentCount: 0,
574
+ createdAt: new Date(),
575
+ };
576
+
577
+ const mock = createMock<ISwarmCoordinator>();
578
+
579
+ mock.initialize.mockImplementation(async (config: SwarmConfig) => {
580
+ state.topology = config.topology;
581
+ state.status = 'active';
582
+ return state;
583
+ });
584
+
585
+ mock.coordinate.mockImplementation(async (agents: string[], task: SwarmTask) => ({
586
+ success: true,
587
+ completedTasks: 1,
588
+ failedTasks: 0,
589
+ totalDuration: 1000,
590
+ agentMetrics: new Map(),
591
+ }));
592
+
593
+ mock.shutdown.mockImplementation(async () => {
594
+ state.status = 'shutdown';
595
+ state.activeAgentCount = 0;
596
+ });
597
+
598
+ mock.getState.mockImplementation(() => state);
599
+
600
+ mock.addAgent.mockImplementation(async () => {
601
+ state.agentCount++;
602
+ state.activeAgentCount++;
603
+ });
604
+
605
+ mock.removeAgent.mockImplementation(async () => {
606
+ state.agentCount--;
607
+ state.activeAgentCount--;
608
+ });
609
+
610
+ mock.broadcast.mockResolvedValue(undefined);
611
+
612
+ return Object.assign(mock, { state });
613
+ }
614
+
615
+ /**
616
+ * Create mock MCP client
617
+ */
618
+ export function createMockMCPClient(): MockedInterface<IMCPClient> & { connected: boolean } {
619
+ let connected = false;
620
+
621
+ const mock = createMock<IMCPClient>();
622
+
623
+ mock.connect.mockImplementation(async () => {
624
+ connected = true;
625
+ });
626
+
627
+ mock.disconnect.mockImplementation(async () => {
628
+ connected = false;
629
+ });
630
+
631
+ mock.callTool.mockImplementation(async () => ({
632
+ content: [{ type: 'text', text: 'Success' }],
633
+ }));
634
+
635
+ mock.listTools.mockResolvedValue([]);
636
+
637
+ mock.isConnected.mockImplementation(() => connected);
638
+
639
+ mock.getSession.mockReturnValue(null);
640
+
641
+ return Object.assign(mock, { connected });
642
+ }
643
+
644
+ /**
645
+ * Create mock logger with captured logs
646
+ */
647
+ export function createMockLogger(): MockedInterface<ILogger> & { logs: Array<{ level: string; message: string; context?: Record<string, unknown>; error?: Error }> } {
648
+ const logs: Array<{ level: string; message: string; context?: Record<string, unknown>; error?: Error }> = [];
649
+
650
+ const mock = createMock<ILogger>();
651
+
652
+ mock.debug.mockImplementation((message: string, context?: Record<string, unknown>) => {
653
+ logs.push({ level: 'debug', message, context });
654
+ });
655
+
656
+ mock.info.mockImplementation((message: string, context?: Record<string, unknown>) => {
657
+ logs.push({ level: 'info', message, context });
658
+ });
659
+
660
+ mock.warn.mockImplementation((message: string, context?: Record<string, unknown>) => {
661
+ logs.push({ level: 'warn', message, context });
662
+ });
663
+
664
+ mock.error.mockImplementation((message: string, error?: Error, context?: Record<string, unknown>) => {
665
+ logs.push({ level: 'error', message, context, error });
666
+ });
667
+
668
+ return Object.assign(mock, { logs });
669
+ }
670
+
671
+ /**
672
+ * Create a complete test application with all mock services
673
+ */
674
+ export function createMockApplication(): MockApplication {
675
+ return {
676
+ eventBus: createMockEventBus(),
677
+ taskManager: createMockTaskManager(),
678
+ agentLifecycle: createMockAgentLifecycle(),
679
+ memoryService: createMockMemoryService(),
680
+ securityService: createMockSecurityService(),
681
+ swarmCoordinator: createMockSwarmCoordinator(),
682
+ mcpClient: createMockMCPClient(),
683
+ logger: createMockLogger(),
684
+ };
685
+ }
686
+
687
+ /**
688
+ * Mock application type
689
+ */
690
+ export interface MockApplication {
691
+ eventBus: ReturnType<typeof createMockEventBus>;
692
+ taskManager: ReturnType<typeof createMockTaskManager>;
693
+ agentLifecycle: ReturnType<typeof createMockAgentLifecycle>;
694
+ memoryService: ReturnType<typeof createMockMemoryService>;
695
+ securityService: ReturnType<typeof createMockSecurityService>;
696
+ swarmCoordinator: ReturnType<typeof createMockSwarmCoordinator>;
697
+ mcpClient: ReturnType<typeof createMockMCPClient>;
698
+ logger: ReturnType<typeof createMockLogger>;
699
+ }
700
+
701
+ /**
702
+ * Reset all mocks in the application
703
+ */
704
+ export function resetMockApplication(app: MockApplication): void {
705
+ vi.clearAllMocks();
706
+ app.eventBus.publishedEvents.length = 0;
707
+ app.taskManager.tasks.clear();
708
+ app.agentLifecycle.agents.clear();
709
+ app.memoryService.entries.clear();
710
+ app.logger.logs.length = 0;
711
+ }