@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,473 @@
1
+ /**
2
+ * API Contract Validator
3
+ *
4
+ * Validates MCP tool interfaces to detect breaking changes.
5
+ *
6
+ * @module v3/testing/regression/api-contract
7
+ */
8
+
9
+ import { readFile, writeFile, mkdir } from 'fs/promises';
10
+ import { join, dirname } from 'path';
11
+
12
+ /**
13
+ * Contract definition for a tool or endpoint
14
+ */
15
+ export interface ContractDefinition {
16
+ name: string;
17
+ version: string;
18
+ description: string;
19
+ input: ParameterSchema;
20
+ output: ParameterSchema;
21
+ required: string[];
22
+ optional: string[];
23
+ errors: ErrorDefinition[];
24
+ }
25
+
26
+ /**
27
+ * Parameter schema definition
28
+ */
29
+ interface ParameterSchema {
30
+ type: string;
31
+ properties?: Record<string, PropertyDefinition>;
32
+ items?: PropertyDefinition;
33
+ required?: string[];
34
+ }
35
+
36
+ /**
37
+ * Property definition
38
+ */
39
+ interface PropertyDefinition {
40
+ type: string;
41
+ description?: string;
42
+ enum?: string[];
43
+ default?: unknown;
44
+ properties?: Record<string, PropertyDefinition>;
45
+ items?: PropertyDefinition;
46
+ }
47
+
48
+ /**
49
+ * Error definition
50
+ */
51
+ interface ErrorDefinition {
52
+ code: number;
53
+ message: string;
54
+ description: string;
55
+ }
56
+
57
+ /**
58
+ * Contract validation result
59
+ */
60
+ export interface ContractValidation {
61
+ endpoint: string;
62
+ valid: boolean;
63
+ breaking: boolean;
64
+ message: string;
65
+ diffs: ContractDiff[];
66
+ }
67
+
68
+ /**
69
+ * Contract difference
70
+ */
71
+ export interface ContractDiff {
72
+ type: 'added' | 'removed' | 'changed' | 'deprecated';
73
+ path: string;
74
+ description: string;
75
+ breaking: boolean;
76
+ oldValue?: unknown;
77
+ newValue?: unknown;
78
+ }
79
+
80
+ /**
81
+ * Stored contracts
82
+ */
83
+ interface StoredContracts {
84
+ version: string;
85
+ capturedAt: number;
86
+ contracts: ContractDefinition[];
87
+ }
88
+
89
+ /**
90
+ * MCP tool definitions to validate
91
+ */
92
+ const MCP_TOOLS: ContractDefinition[] = [
93
+ {
94
+ name: 'agent/spawn',
95
+ version: '1.0.0',
96
+ description: 'Spawn a new agent',
97
+ input: {
98
+ type: 'object',
99
+ properties: {
100
+ agentType: { type: 'string', description: 'Type of agent to spawn' },
101
+ id: { type: 'string', description: 'Optional agent ID' },
102
+ config: { type: 'object', description: 'Agent configuration' },
103
+ priority: { type: 'string', enum: ['low', 'normal', 'high', 'critical'] },
104
+ metadata: { type: 'object', description: 'Additional metadata' },
105
+ },
106
+ required: ['agentType'],
107
+ },
108
+ output: {
109
+ type: 'object',
110
+ properties: {
111
+ agentId: { type: 'string' },
112
+ status: { type: 'string' },
113
+ createdAt: { type: 'string' },
114
+ },
115
+ },
116
+ required: ['agentType'],
117
+ optional: ['id', 'config', 'priority', 'metadata'],
118
+ errors: [
119
+ { code: -32602, message: 'Invalid agent type', description: 'The specified agent type is not valid' },
120
+ { code: -32000, message: 'Agent spawn failed', description: 'Failed to spawn agent' },
121
+ ],
122
+ },
123
+ {
124
+ name: 'agent/list',
125
+ version: '1.0.0',
126
+ description: 'List all agents',
127
+ input: {
128
+ type: 'object',
129
+ properties: {
130
+ status: { type: 'string', enum: ['active', 'idle', 'terminated', 'all'] },
131
+ agentType: { type: 'string' },
132
+ },
133
+ },
134
+ output: {
135
+ type: 'object',
136
+ properties: {
137
+ agents: { type: 'array', items: { type: 'object' } },
138
+ totalCount: { type: 'number' },
139
+ },
140
+ },
141
+ required: [],
142
+ optional: ['status', 'agentType'],
143
+ errors: [],
144
+ },
145
+ {
146
+ name: 'memory/store',
147
+ version: '1.0.0',
148
+ description: 'Store a memory entry',
149
+ input: {
150
+ type: 'object',
151
+ properties: {
152
+ content: { type: 'string', description: 'Memory content' },
153
+ category: { type: 'string', description: 'Memory category' },
154
+ metadata: { type: 'object', description: 'Additional metadata' },
155
+ tags: { type: 'array', items: { type: 'string' } },
156
+ ttl: { type: 'number', description: 'Time-to-live in seconds' },
157
+ },
158
+ required: ['content'],
159
+ },
160
+ output: {
161
+ type: 'object',
162
+ properties: {
163
+ id: { type: 'string' },
164
+ storedAt: { type: 'string' },
165
+ expiresAt: { type: 'string' },
166
+ },
167
+ },
168
+ required: ['content'],
169
+ optional: ['category', 'metadata', 'tags', 'ttl'],
170
+ errors: [
171
+ { code: -32000, message: 'Storage failed', description: 'Failed to store memory' },
172
+ ],
173
+ },
174
+ {
175
+ name: 'memory/search',
176
+ version: '1.0.0',
177
+ description: 'Search memory entries',
178
+ input: {
179
+ type: 'object',
180
+ properties: {
181
+ query: { type: 'string', description: 'Search query' },
182
+ category: { type: 'string' },
183
+ limit: { type: 'number', default: 10 },
184
+ threshold: { type: 'number', default: 0.7 },
185
+ semantic: { type: 'boolean', default: true },
186
+ },
187
+ required: ['query'],
188
+ },
189
+ output: {
190
+ type: 'object',
191
+ properties: {
192
+ results: { type: 'array', items: { type: 'object' } },
193
+ totalMatches: { type: 'number' },
194
+ searchTime: { type: 'number' },
195
+ },
196
+ },
197
+ required: ['query'],
198
+ optional: ['category', 'limit', 'threshold', 'semantic'],
199
+ errors: [],
200
+ },
201
+ {
202
+ name: 'swarm/init',
203
+ version: '1.0.0',
204
+ description: 'Initialize a swarm',
205
+ input: {
206
+ type: 'object',
207
+ properties: {
208
+ topology: { type: 'string', enum: ['hierarchical', 'mesh', 'star', 'ring', 'adaptive'] },
209
+ maxAgents: { type: 'number' },
210
+ config: { type: 'object' },
211
+ },
212
+ required: ['topology'],
213
+ },
214
+ output: {
215
+ type: 'object',
216
+ properties: {
217
+ swarmId: { type: 'string' },
218
+ status: { type: 'string' },
219
+ topology: { type: 'string' },
220
+ },
221
+ },
222
+ required: ['topology'],
223
+ optional: ['maxAgents', 'config'],
224
+ errors: [
225
+ { code: -32602, message: 'Invalid topology', description: 'The specified topology is not valid' },
226
+ ],
227
+ },
228
+ {
229
+ name: 'task/orchestrate',
230
+ version: '1.0.0',
231
+ description: 'Orchestrate a complex task',
232
+ input: {
233
+ type: 'object',
234
+ properties: {
235
+ description: { type: 'string' },
236
+ strategy: { type: 'string', enum: ['sequential', 'parallel', 'adaptive'] },
237
+ maxAgents: { type: 'number' },
238
+ timeout: { type: 'number' },
239
+ },
240
+ required: ['description'],
241
+ },
242
+ output: {
243
+ type: 'object',
244
+ properties: {
245
+ taskId: { type: 'string' },
246
+ phases: { type: 'array' },
247
+ estimatedDuration: { type: 'number' },
248
+ },
249
+ },
250
+ required: ['description'],
251
+ optional: ['strategy', 'maxAgents', 'timeout'],
252
+ errors: [],
253
+ },
254
+ ];
255
+
256
+ /**
257
+ * API Contract Validator
258
+ *
259
+ * Validates MCP tool contracts for breaking changes.
260
+ */
261
+ export class APIContractValidator {
262
+ private readonly contractPath: string;
263
+ private cachedContracts: StoredContracts | null = null;
264
+
265
+ constructor(basePath: string = '.regression-baselines') {
266
+ this.contractPath = join(basePath, 'contracts.json');
267
+ }
268
+
269
+ /**
270
+ * Capture current contracts as baseline
271
+ */
272
+ async captureContracts(): Promise<StoredContracts> {
273
+ const contracts: StoredContracts = {
274
+ version: '1.0.0',
275
+ capturedAt: Date.now(),
276
+ contracts: MCP_TOOLS,
277
+ };
278
+
279
+ await this.saveContracts(contracts);
280
+ this.cachedContracts = contracts;
281
+
282
+ return contracts;
283
+ }
284
+
285
+ /**
286
+ * Validate all contracts against baseline
287
+ */
288
+ async validateAll(): Promise<ContractValidation[]> {
289
+ const baseline = await this.loadContracts();
290
+ if (!baseline) {
291
+ console.warn('No contract baseline found. Capturing initial contracts...');
292
+ await this.captureContracts();
293
+ return [];
294
+ }
295
+
296
+ const validations: ContractValidation[] = [];
297
+
298
+ for (const current of MCP_TOOLS) {
299
+ const baselineContract = baseline.contracts.find((c) => c.name === current.name);
300
+
301
+ if (!baselineContract) {
302
+ // New endpoint added
303
+ validations.push({
304
+ endpoint: current.name,
305
+ valid: true,
306
+ breaking: false,
307
+ message: 'New endpoint added',
308
+ diffs: [{
309
+ type: 'added',
310
+ path: current.name,
311
+ description: `New endpoint: ${current.description}`,
312
+ breaking: false,
313
+ }],
314
+ });
315
+ continue;
316
+ }
317
+
318
+ const diffs = this.compareContracts(baselineContract, current);
319
+ const hasBreakingChanges = diffs.some((d) => d.breaking);
320
+
321
+ validations.push({
322
+ endpoint: current.name,
323
+ valid: !hasBreakingChanges,
324
+ breaking: hasBreakingChanges,
325
+ message: hasBreakingChanges
326
+ ? `Breaking changes detected: ${diffs.filter((d) => d.breaking).map((d) => d.description).join(', ')}`
327
+ : diffs.length > 0
328
+ ? `Non-breaking changes: ${diffs.map((d) => d.description).join(', ')}`
329
+ : 'No changes',
330
+ diffs,
331
+ });
332
+ }
333
+
334
+ // Check for removed endpoints
335
+ for (const baselineContract of baseline.contracts) {
336
+ const stillExists = MCP_TOOLS.find((c) => c.name === baselineContract.name);
337
+ if (!stillExists) {
338
+ validations.push({
339
+ endpoint: baselineContract.name,
340
+ valid: false,
341
+ breaking: true,
342
+ message: 'Endpoint removed',
343
+ diffs: [{
344
+ type: 'removed',
345
+ path: baselineContract.name,
346
+ description: `Endpoint removed: ${baselineContract.name}`,
347
+ breaking: true,
348
+ }],
349
+ });
350
+ }
351
+ }
352
+
353
+ return validations;
354
+ }
355
+
356
+ /**
357
+ * Compare two contracts for differences
358
+ */
359
+ private compareContracts(baseline: ContractDefinition, current: ContractDefinition): ContractDiff[] {
360
+ const diffs: ContractDiff[] = [];
361
+
362
+ // Check required parameters
363
+ for (const param of baseline.required) {
364
+ if (!current.required.includes(param)) {
365
+ if (current.optional.includes(param)) {
366
+ // Parameter became optional - not breaking
367
+ diffs.push({
368
+ type: 'changed',
369
+ path: `${baseline.name}.input.${param}`,
370
+ description: `Parameter '${param}' changed from required to optional`,
371
+ breaking: false,
372
+ oldValue: 'required',
373
+ newValue: 'optional',
374
+ });
375
+ } else {
376
+ // Parameter removed - breaking
377
+ diffs.push({
378
+ type: 'removed',
379
+ path: `${baseline.name}.input.${param}`,
380
+ description: `Required parameter '${param}' removed`,
381
+ breaking: true,
382
+ });
383
+ }
384
+ }
385
+ }
386
+
387
+ // Check for new required parameters (breaking)
388
+ for (const param of current.required) {
389
+ if (!baseline.required.includes(param) && !baseline.optional.includes(param)) {
390
+ diffs.push({
391
+ type: 'added',
392
+ path: `${baseline.name}.input.${param}`,
393
+ description: `New required parameter '${param}' added`,
394
+ breaking: true,
395
+ });
396
+ }
397
+ }
398
+
399
+ // Check for new optional parameters (not breaking)
400
+ for (const param of current.optional) {
401
+ if (!baseline.required.includes(param) && !baseline.optional.includes(param)) {
402
+ diffs.push({
403
+ type: 'added',
404
+ path: `${baseline.name}.input.${param}`,
405
+ description: `New optional parameter '${param}' added`,
406
+ breaking: false,
407
+ });
408
+ }
409
+ }
410
+
411
+ // Check input schema changes
412
+ if (baseline.input.properties && current.input.properties) {
413
+ for (const [name, prop] of Object.entries(baseline.input.properties)) {
414
+ const currentProp = current.input.properties[name];
415
+ if (currentProp) {
416
+ // Check type changes
417
+ if (prop.type !== currentProp.type) {
418
+ diffs.push({
419
+ type: 'changed',
420
+ path: `${baseline.name}.input.${name}.type`,
421
+ description: `Type of '${name}' changed from '${prop.type}' to '${currentProp.type}'`,
422
+ breaking: true,
423
+ oldValue: prop.type,
424
+ newValue: currentProp.type,
425
+ });
426
+ }
427
+
428
+ // Check enum changes
429
+ if (prop.enum && currentProp.enum) {
430
+ const removedValues = prop.enum.filter((v) => !currentProp.enum!.includes(v));
431
+ if (removedValues.length > 0) {
432
+ diffs.push({
433
+ type: 'changed',
434
+ path: `${baseline.name}.input.${name}.enum`,
435
+ description: `Enum values removed from '${name}': ${removedValues.join(', ')}`,
436
+ breaking: true,
437
+ oldValue: prop.enum,
438
+ newValue: currentProp.enum,
439
+ });
440
+ }
441
+ }
442
+ }
443
+ }
444
+ }
445
+
446
+ return diffs;
447
+ }
448
+
449
+ /**
450
+ * Load contracts from file
451
+ */
452
+ private async loadContracts(): Promise<StoredContracts | null> {
453
+ if (this.cachedContracts) {
454
+ return this.cachedContracts;
455
+ }
456
+
457
+ try {
458
+ const content = await readFile(this.contractPath, 'utf-8');
459
+ this.cachedContracts = JSON.parse(content);
460
+ return this.cachedContracts;
461
+ } catch {
462
+ return null;
463
+ }
464
+ }
465
+
466
+ /**
467
+ * Save contracts to file
468
+ */
469
+ private async saveContracts(contracts: StoredContracts): Promise<void> {
470
+ await mkdir(dirname(this.contractPath), { recursive: true });
471
+ await writeFile(this.contractPath, JSON.stringify(contracts, null, 2));
472
+ }
473
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Regression Testing System
3
+ *
4
+ * Comprehensive regression detection to prevent capability degradation
5
+ * as new features are added. Includes:
6
+ * - Performance baselines
7
+ * - Security vulnerability checks
8
+ * - API contract validation
9
+ * - Integration verification
10
+ *
11
+ * @module v3/testing/regression
12
+ */
13
+
14
+ export {
15
+ RegressionTestRunner,
16
+ type RegressionConfig,
17
+ type RegressionResult,
18
+ type RegressionReport,
19
+ } from './regression-runner.js';
20
+
21
+ export {
22
+ PerformanceBaseline,
23
+ type BaselineMetric,
24
+ type BaselineComparison,
25
+ type BaselineConfig,
26
+ } from './performance-baseline.js';
27
+
28
+ export {
29
+ SecurityRegressionChecker,
30
+ type SecurityCheck,
31
+ type SecurityVulnerability,
32
+ type SecurityReport,
33
+ } from './security-regression.js';
34
+
35
+ export {
36
+ APIContractValidator,
37
+ type ContractDefinition,
38
+ type ContractValidation,
39
+ type ContractDiff,
40
+ } from './api-contract.js';
41
+
42
+ export {
43
+ IntegrationRegressionSuite,
44
+ type IntegrationTest,
45
+ type IntegrationResult,
46
+ } from './integration-regression.js';