@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.
- package/README.md +547 -0
- package/__tests__/framework.test.ts +21 -0
- package/package.json +61 -0
- package/src/fixtures/agent-fixtures.ts +793 -0
- package/src/fixtures/agents.ts +212 -0
- package/src/fixtures/configurations.ts +491 -0
- package/src/fixtures/index.ts +21 -0
- package/src/fixtures/mcp-fixtures.ts +1030 -0
- package/src/fixtures/memory-entries.ts +328 -0
- package/src/fixtures/memory-fixtures.ts +750 -0
- package/src/fixtures/swarm-fixtures.ts +837 -0
- package/src/fixtures/tasks.ts +309 -0
- package/src/helpers/assertion-helpers.ts +616 -0
- package/src/helpers/assertions.ts +286 -0
- package/src/helpers/create-mock.ts +200 -0
- package/src/helpers/index.ts +182 -0
- package/src/helpers/mock-factory.ts +711 -0
- package/src/helpers/setup-teardown.ts +678 -0
- package/src/helpers/swarm-instance.ts +326 -0
- package/src/helpers/test-application.ts +310 -0
- package/src/helpers/test-utils.ts +670 -0
- package/src/index.ts +232 -0
- package/src/mocks/index.ts +29 -0
- package/src/mocks/mock-mcp-client.ts +723 -0
- package/src/mocks/mock-services.ts +793 -0
- package/src/regression/api-contract.ts +473 -0
- package/src/regression/index.ts +46 -0
- package/src/regression/integration-regression.ts +416 -0
- package/src/regression/performance-baseline.ts +356 -0
- package/src/regression/regression-runner.ts +339 -0
- package/src/regression/security-regression.ts +331 -0
- package/src/setup.ts +127 -0
- package/src/v2-compat/api-compat.test.ts +590 -0
- package/src/v2-compat/cli-compat.test.ts +484 -0
- package/src/v2-compat/compatibility-validator.ts +1072 -0
- package/src/v2-compat/hooks-compat.test.ts +602 -0
- package/src/v2-compat/index.ts +58 -0
- package/src/v2-compat/mcp-compat.test.ts +557 -0
- package/src/v2-compat/report-generator.ts +441 -0
- package/tmp.json +0 -0
- package/tsconfig.json +20 -0
- 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;
|