@sparkleideas/testing 3.0.0-alpha.10

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,331 @@
1
+ /**
2
+ * Security Regression Checker
3
+ *
4
+ * Detects new security vulnerabilities and regressions.
5
+ *
6
+ * @module v3/testing/regression/security-regression
7
+ */
8
+
9
+ import { readFile, readdir, stat } from 'fs/promises';
10
+ import { join, extname } from 'path';
11
+
12
+ /**
13
+ * Security check definition
14
+ */
15
+ export interface SecurityCheck {
16
+ id: string;
17
+ name: string;
18
+ description: string;
19
+ severity: 'critical' | 'high' | 'medium' | 'low' | 'info';
20
+ passed: boolean;
21
+ message: string;
22
+ location?: string;
23
+ line?: number;
24
+ }
25
+
26
+ /**
27
+ * Security vulnerability
28
+ */
29
+ export interface SecurityVulnerability {
30
+ id: string;
31
+ type: string;
32
+ severity: 'critical' | 'high' | 'medium' | 'low';
33
+ file: string;
34
+ line: number;
35
+ description: string;
36
+ recommendation: string;
37
+ cwe?: string;
38
+ }
39
+
40
+ /**
41
+ * Security report
42
+ */
43
+ export interface SecurityReport {
44
+ timestamp: Date;
45
+ duration: number;
46
+ passed: boolean;
47
+ checks: SecurityCheck[];
48
+ vulnerabilities: SecurityVulnerability[];
49
+ newIssues: string[];
50
+ resolvedIssues: string[];
51
+ summary: {
52
+ critical: number;
53
+ high: number;
54
+ medium: number;
55
+ low: number;
56
+ };
57
+ }
58
+
59
+ /**
60
+ * Security patterns to check
61
+ */
62
+ const SECURITY_PATTERNS = [
63
+ {
64
+ id: 'sql-injection',
65
+ name: 'SQL Injection',
66
+ pattern: /\$\{[^}]*\}.*(?:SELECT|INSERT|UPDATE|DELETE|FROM|WHERE)/gi,
67
+ altPattern: /['"`]\s*\+\s*\w+.*(?:SELECT|INSERT|UPDATE|DELETE|FROM|WHERE)/gi,
68
+ severity: 'critical' as const,
69
+ cwe: 'CWE-89',
70
+ description: 'Potential SQL injection vulnerability',
71
+ recommendation: 'Use parameterized queries instead of string concatenation',
72
+ },
73
+ {
74
+ id: 'command-injection',
75
+ name: 'Command Injection',
76
+ pattern: /exec\s*\(\s*['"`].*\$\{|exec\s*\(\s*\w+\s*\+/gi,
77
+ altPattern: /child_process.*exec.*\+|spawn.*shell:\s*true/gi,
78
+ severity: 'critical' as const,
79
+ cwe: 'CWE-78',
80
+ description: 'Potential command injection vulnerability',
81
+ recommendation: 'Avoid shell execution with user input; use parameterized commands',
82
+ },
83
+ {
84
+ id: 'path-traversal',
85
+ name: 'Path Traversal',
86
+ pattern: /\.\.\//g,
87
+ altPattern: /path\.join.*req\.|path\.resolve.*req\./gi,
88
+ severity: 'high' as const,
89
+ cwe: 'CWE-22',
90
+ description: 'Potential path traversal vulnerability',
91
+ recommendation: 'Validate and sanitize file paths; use path.normalize()',
92
+ },
93
+ {
94
+ id: 'weak-random',
95
+ name: 'Weak Random',
96
+ pattern: /Math\.random\(\)/g,
97
+ severity: 'medium' as const,
98
+ cwe: 'CWE-330',
99
+ description: 'Use of weak random number generator',
100
+ recommendation: 'Use crypto.randomBytes() or crypto.randomUUID() for security-sensitive operations',
101
+ },
102
+ {
103
+ id: 'hardcoded-secret',
104
+ name: 'Hardcoded Secret',
105
+ pattern: /(?:password|secret|api_key|apikey|token)\s*[=:]\s*['"][^'"]{8,}/gi,
106
+ severity: 'high' as const,
107
+ cwe: 'CWE-798',
108
+ description: 'Potential hardcoded secret or credential',
109
+ recommendation: 'Use environment variables or secure vault for secrets',
110
+ },
111
+ {
112
+ id: 'cors-wildcard',
113
+ name: 'CORS Wildcard',
114
+ pattern: /origin:\s*['"]?\*/gi,
115
+ altPattern: /Access-Control-Allow-Origin.*\*/gi,
116
+ severity: 'medium' as const,
117
+ cwe: 'CWE-942',
118
+ description: 'Permissive CORS configuration',
119
+ recommendation: 'Specify explicit allowed origins instead of wildcard',
120
+ },
121
+ {
122
+ id: 'eval-usage',
123
+ name: 'Eval Usage',
124
+ pattern: /\beval\s*\(/gi,
125
+ altPattern: /new\s+Function\s*\(/gi,
126
+ severity: 'high' as const,
127
+ cwe: 'CWE-95',
128
+ description: 'Use of eval() or dynamic code execution',
129
+ recommendation: 'Avoid eval(); use safer alternatives like JSON.parse()',
130
+ },
131
+ {
132
+ id: 'unsafe-regex',
133
+ name: 'Unsafe Regex',
134
+ pattern: /new\s+RegExp\s*\(\s*\w+\s*\)/gi,
135
+ severity: 'medium' as const,
136
+ cwe: 'CWE-1333',
137
+ description: 'Potentially unsafe dynamic regex construction',
138
+ recommendation: 'Validate and escape regex input; consider using regex-safe libraries',
139
+ },
140
+ {
141
+ id: 'missing-csrf',
142
+ name: 'Missing CSRF Protection',
143
+ pattern: /router\.(?:post|put|patch|delete)\s*\([^)]*\)/gi,
144
+ severity: 'medium' as const,
145
+ cwe: 'CWE-352',
146
+ description: 'Endpoint may lack CSRF protection',
147
+ recommendation: 'Implement CSRF tokens for state-changing operations',
148
+ },
149
+ {
150
+ id: 'insecure-http',
151
+ name: 'Insecure HTTP',
152
+ pattern: /http:\/\/(?!localhost|127\.0\.0\.1)/gi,
153
+ severity: 'low' as const,
154
+ cwe: 'CWE-319',
155
+ description: 'Use of insecure HTTP protocol',
156
+ recommendation: 'Use HTTPS for all external communications',
157
+ },
158
+ ];
159
+
160
+ /**
161
+ * File extensions to scan
162
+ */
163
+ const SCANNABLE_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'];
164
+
165
+ /**
166
+ * Directories to exclude
167
+ */
168
+ const EXCLUDED_DIRS = ['node_modules', 'dist', 'build', '.git', 'coverage', '__tests__'];
169
+
170
+ /**
171
+ * Security Regression Checker
172
+ *
173
+ * Scans codebase for security vulnerabilities.
174
+ */
175
+ export class SecurityRegressionChecker {
176
+ private readonly basePath: string;
177
+
178
+ constructor(basePath: string = process.cwd()) {
179
+ this.basePath = basePath;
180
+ }
181
+
182
+ /**
183
+ * Run full security check
184
+ */
185
+ async check(): Promise<SecurityReport> {
186
+ const startTime = Date.now();
187
+ const vulnerabilities: SecurityVulnerability[] = [];
188
+ const checks: SecurityCheck[] = [];
189
+
190
+ // Scan all TypeScript/JavaScript files
191
+ const files = await this.findFiles(join(this.basePath, 'v3'));
192
+
193
+ for (const file of files) {
194
+ const fileVulns = await this.scanFile(file);
195
+ vulnerabilities.push(...fileVulns);
196
+ }
197
+
198
+ // Create checks from patterns
199
+ for (const pattern of SECURITY_PATTERNS) {
200
+ const patternVulns = vulnerabilities.filter((v) => v.type === pattern.id);
201
+ checks.push({
202
+ id: pattern.id,
203
+ name: pattern.name,
204
+ description: pattern.description,
205
+ severity: pattern.severity,
206
+ passed: patternVulns.length === 0,
207
+ message: patternVulns.length === 0
208
+ ? `No ${pattern.name} vulnerabilities found`
209
+ : `Found ${patternVulns.length} potential ${pattern.name} issues`,
210
+ });
211
+ }
212
+
213
+ // Calculate summary
214
+ const summary = {
215
+ critical: vulnerabilities.filter((v) => v.severity === 'critical').length,
216
+ high: vulnerabilities.filter((v) => v.severity === 'high').length,
217
+ medium: vulnerabilities.filter((v) => v.severity === 'medium').length,
218
+ low: vulnerabilities.filter((v) => v.severity === 'low').length,
219
+ };
220
+
221
+ const report: SecurityReport = {
222
+ timestamp: new Date(),
223
+ duration: Date.now() - startTime,
224
+ passed: summary.critical === 0 && summary.high === 0,
225
+ checks,
226
+ vulnerabilities,
227
+ newIssues: vulnerabilities.map((v) => `${v.severity.toUpperCase()}: ${v.type} in ${v.file}:${v.line}`),
228
+ resolvedIssues: [],
229
+ summary,
230
+ };
231
+
232
+ return report;
233
+ }
234
+
235
+ /**
236
+ * Find all scannable files
237
+ */
238
+ private async findFiles(dir: string): Promise<string[]> {
239
+ const files: string[] = [];
240
+
241
+ try {
242
+ const entries = await readdir(dir, { withFileTypes: true });
243
+
244
+ for (const entry of entries) {
245
+ const fullPath = join(dir, entry.name);
246
+
247
+ if (entry.isDirectory()) {
248
+ if (!EXCLUDED_DIRS.includes(entry.name)) {
249
+ const subFiles = await this.findFiles(fullPath);
250
+ files.push(...subFiles);
251
+ }
252
+ } else if (entry.isFile()) {
253
+ if (SCANNABLE_EXTENSIONS.includes(extname(entry.name))) {
254
+ files.push(fullPath);
255
+ }
256
+ }
257
+ }
258
+ } catch {
259
+ // Directory doesn't exist or not accessible
260
+ }
261
+
262
+ return files;
263
+ }
264
+
265
+ /**
266
+ * Scan a single file for vulnerabilities
267
+ */
268
+ private async scanFile(filePath: string): Promise<SecurityVulnerability[]> {
269
+ const vulnerabilities: SecurityVulnerability[] = [];
270
+
271
+ try {
272
+ const content = await readFile(filePath, 'utf-8');
273
+ const lines = content.split('\n');
274
+
275
+ for (const pattern of SECURITY_PATTERNS) {
276
+ // Skip weak-random checks in test files
277
+ if (pattern.id === 'weak-random' && filePath.includes('test')) {
278
+ continue;
279
+ }
280
+
281
+ for (let i = 0; i < lines.length; i++) {
282
+ const line = lines[i]!;
283
+
284
+ // Check main pattern
285
+ if (pattern.pattern.test(line)) {
286
+ // Reset lastIndex for global patterns
287
+ pattern.pattern.lastIndex = 0;
288
+
289
+ vulnerabilities.push({
290
+ id: `${pattern.id}-${filePath}-${i + 1}`,
291
+ type: pattern.id,
292
+ severity: pattern.severity,
293
+ file: filePath.replace(this.basePath + '/', ''),
294
+ line: i + 1,
295
+ description: pattern.description,
296
+ recommendation: pattern.recommendation,
297
+ cwe: pattern.cwe,
298
+ });
299
+ }
300
+
301
+ // Check alternate pattern if exists
302
+ if (pattern.altPattern && pattern.altPattern.test(line)) {
303
+ pattern.altPattern.lastIndex = 0;
304
+
305
+ // Avoid duplicates
306
+ const existingVuln = vulnerabilities.find(
307
+ (v) => v.type === pattern.id && v.line === i + 1 && v.file === filePath.replace(this.basePath + '/', '')
308
+ );
309
+
310
+ if (!existingVuln) {
311
+ vulnerabilities.push({
312
+ id: `${pattern.id}-${filePath}-${i + 1}`,
313
+ type: pattern.id,
314
+ severity: pattern.severity,
315
+ file: filePath.replace(this.basePath + '/', ''),
316
+ line: i + 1,
317
+ description: pattern.description,
318
+ recommendation: pattern.recommendation,
319
+ cwe: pattern.cwe,
320
+ });
321
+ }
322
+ }
323
+ }
324
+ }
325
+ } catch {
326
+ // File not readable
327
+ }
328
+
329
+ return vulnerabilities;
330
+ }
331
+ }
package/src/setup.ts ADDED
@@ -0,0 +1,127 @@
1
+ /**
2
+ * V3 Claude-Flow Test Setup
3
+ *
4
+ * London School TDD Global Configuration
5
+ * - Initializes mock infrastructure
6
+ * - Sets up global test utilities
7
+ * - Configures behavior verification helpers
8
+ */
9
+ import { vi, beforeEach, afterEach, expect } from 'vitest';
10
+
11
+ // Re-export commonly used testing utilities
12
+ export { vi, expect } from 'vitest';
13
+ export { createMock, createDeepMock, createSpyMock } from './helpers/create-mock.js';
14
+ export { createTestApplication } from './helpers/test-application.js';
15
+ export { createSwarmTestInstance } from './helpers/swarm-instance.js';
16
+
17
+ // Custom matchers for London School testing
18
+ expect.extend({
19
+ /**
20
+ * Verify a mock was called with specific interaction pattern
21
+ */
22
+ toHaveBeenCalledWithInteraction(received, expected) {
23
+ const pass = received.mock.calls.some((call: unknown[]) =>
24
+ JSON.stringify(call) === JSON.stringify(expected)
25
+ );
26
+
27
+ return {
28
+ pass,
29
+ message: () => pass
30
+ ? `Expected mock not to have been called with ${JSON.stringify(expected)}`
31
+ : `Expected mock to have been called with ${JSON.stringify(expected)}, but was called with ${JSON.stringify(received.mock.calls)}`,
32
+ };
33
+ },
34
+
35
+ /**
36
+ * Verify mock call order for behavior testing
37
+ */
38
+ toHaveBeenCalledBefore(received, other) {
39
+ const receivedCalls = received.mock.invocationCallOrder;
40
+ const otherCalls = other.mock.invocationCallOrder;
41
+
42
+ if (receivedCalls.length === 0 || otherCalls.length === 0) {
43
+ return {
44
+ pass: false,
45
+ message: () => 'Expected both mocks to have been called',
46
+ };
47
+ }
48
+
49
+ const pass = Math.min(...receivedCalls) < Math.min(...otherCalls);
50
+
51
+ return {
52
+ pass,
53
+ message: () => pass
54
+ ? `Expected first mock not to have been called before second mock`
55
+ : `Expected first mock to have been called before second mock`,
56
+ };
57
+ },
58
+
59
+ /**
60
+ * Verify interaction count for behavior verification
61
+ */
62
+ toHaveInteractionCount(received, expected) {
63
+ const actual = received.mock.calls.length;
64
+ const pass = actual === expected;
65
+
66
+ return {
67
+ pass,
68
+ message: () => pass
69
+ ? `Expected mock not to have ${expected} interactions`
70
+ : `Expected mock to have ${expected} interactions, but had ${actual}`,
71
+ };
72
+ },
73
+ });
74
+
75
+ // Global setup - runs once before all tests
76
+ beforeEach(() => {
77
+ // Reset all mocks before each test (London School principle)
78
+ vi.clearAllMocks();
79
+
80
+ // Reset any timers
81
+ vi.useRealTimers();
82
+ });
83
+
84
+ afterEach(() => {
85
+ // Verify no unhandled mock calls (strict verification)
86
+ vi.restoreAllMocks();
87
+ });
88
+
89
+ // Type declarations for custom matchers
90
+ // Note: Custom matcher types are available via expect.extend() at runtime
91
+ // For TypeScript, we use interface merging with CustomMatchers
92
+ interface CustomMatchers<R = unknown> {
93
+ toHaveBeenCalledWithInteraction(expected: unknown[]): R;
94
+ toHaveBeenCalledBefore(other: ReturnType<typeof vi.fn>): R;
95
+ toHaveInteractionCount(expected: number): R;
96
+ // Additional matchers from assertion-helpers
97
+ toHaveBeenCalledWithPattern(pattern: Record<string, unknown>): R;
98
+ toHaveEventType(eventType: string): R;
99
+ toMeetV3PerformanceTargets(): R;
100
+ toBeValidTransition(from: string, allowedTransitions: Record<string, string[]>): R;
101
+ }
102
+
103
+ declare module 'vitest' {
104
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
105
+ interface Assertion<T> extends CustomMatchers<T> {}
106
+ interface AsymmetricMatchersContaining extends CustomMatchers {}
107
+ }
108
+
109
+ /**
110
+ * Test configuration constants
111
+ */
112
+ export const TEST_CONFIG = {
113
+ // Security test thresholds
114
+ SECURITY_COVERAGE_TARGET: 0.95,
115
+
116
+ // Performance test thresholds
117
+ FLASH_ATTENTION_SPEEDUP_MIN: 2.49,
118
+ FLASH_ATTENTION_SPEEDUP_MAX: 7.47,
119
+ AGENTDB_SEARCH_IMPROVEMENT_MIN: 150,
120
+ AGENTDB_SEARCH_IMPROVEMENT_MAX: 12500,
121
+ MEMORY_REDUCTION_TARGET: 0.50,
122
+
123
+ // Timeouts
124
+ ASYNC_TIMEOUT: 5000,
125
+ INTEGRATION_TIMEOUT: 10000,
126
+ ACCEPTANCE_TIMEOUT: 30000,
127
+ } as const;