@frontmcp/testing 0.7.2 → 0.8.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/auth/index.d.ts +2 -0
- package/auth/index.d.ts.map +1 -1
- package/auth/mock-cimd-server.d.ts +174 -0
- package/auth/mock-cimd-server.d.ts.map +1 -0
- package/auth/mock-oauth-server.d.ts +136 -6
- package/auth/mock-oauth-server.d.ts.map +1 -1
- package/auth/token-factory.d.ts.map +1 -1
- package/client/index.d.ts +1 -1
- package/client/index.d.ts.map +1 -1
- package/client/mcp-test-client.builder.d.ts +12 -0
- package/client/mcp-test-client.builder.d.ts.map +1 -1
- package/client/mcp-test-client.d.ts +48 -2
- package/client/mcp-test-client.d.ts.map +1 -1
- package/client/mcp-test-client.types.d.ts +60 -0
- package/client/mcp-test-client.types.d.ts.map +1 -1
- package/esm/fixtures/index.mjs +661 -83
- package/esm/index.mjs +3245 -219
- package/esm/package.json +5 -4
- package/esm/perf/index.mjs +4334 -0
- package/esm/perf/perf-setup.mjs +31 -0
- package/fixtures/fixture-types.d.ts +10 -1
- package/fixtures/fixture-types.d.ts.map +1 -1
- package/fixtures/index.js +661 -93
- package/fixtures/test-fixture.d.ts +1 -1
- package/fixtures/test-fixture.d.ts.map +1 -1
- package/index.d.ts +5 -1
- package/index.d.ts.map +1 -1
- package/index.js +3271 -219
- package/interceptor/interceptor-chain.d.ts +1 -0
- package/interceptor/interceptor-chain.d.ts.map +1 -1
- package/package.json +5 -4
- package/perf/baseline-store.d.ts +67 -0
- package/perf/baseline-store.d.ts.map +1 -0
- package/perf/index.d.ts +44 -0
- package/perf/index.d.ts.map +1 -0
- package/perf/index.js +4404 -0
- package/perf/jest-perf-reporter.d.ts +6 -0
- package/perf/jest-perf-reporter.d.ts.map +1 -0
- package/perf/leak-detector.d.ts +81 -0
- package/perf/leak-detector.d.ts.map +1 -0
- package/perf/metrics-collector.d.ts +83 -0
- package/perf/metrics-collector.d.ts.map +1 -0
- package/perf/perf-fixtures.d.ts +107 -0
- package/perf/perf-fixtures.d.ts.map +1 -0
- package/perf/perf-setup.d.ts +9 -0
- package/perf/perf-setup.d.ts.map +1 -0
- package/perf/perf-setup.js +50 -0
- package/perf/perf-test.d.ts +69 -0
- package/perf/perf-test.d.ts.map +1 -0
- package/perf/regression-detector.d.ts +55 -0
- package/perf/regression-detector.d.ts.map +1 -0
- package/perf/report-generator.d.ts +66 -0
- package/perf/report-generator.d.ts.map +1 -0
- package/perf/types.d.ts +439 -0
- package/perf/types.d.ts.map +1 -0
- package/platform/platform-client-info.d.ts +18 -0
- package/platform/platform-client-info.d.ts.map +1 -1
- package/server/index.d.ts +2 -0
- package/server/index.d.ts.map +1 -1
- package/server/port-registry.d.ts +179 -0
- package/server/port-registry.d.ts.map +1 -0
- package/server/test-server.d.ts +9 -5
- package/server/test-server.d.ts.map +1 -1
- package/transport/streamable-http.transport.d.ts +26 -0
- package/transport/streamable-http.transport.d.ts.map +1 -1
- package/transport/transport.interface.d.ts +9 -1
- package/transport/transport.interface.d.ts.map +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jest-perf-reporter.d.ts","sourceRoot":"","sources":["../../src/perf/jest-perf-reporter.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file leak-detector.ts
|
|
3
|
+
* @description Memory leak detection using iterative measurement and linear regression
|
|
4
|
+
*/
|
|
5
|
+
import type { LeakDetectionOptions, LeakDetectionResult, ParallelLeakDetectionOptions, ParallelLeakDetectionResult, ParallelTestClient } from './types';
|
|
6
|
+
/**
|
|
7
|
+
* Detects memory leaks by running an operation multiple times
|
|
8
|
+
* and analyzing heap growth using linear regression.
|
|
9
|
+
*/
|
|
10
|
+
export declare class LeakDetector {
|
|
11
|
+
/**
|
|
12
|
+
* Run leak detection on an async operation.
|
|
13
|
+
*
|
|
14
|
+
* @param operation - The operation to test for leaks
|
|
15
|
+
* @param options - Detection options
|
|
16
|
+
* @returns Leak detection result
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* const detector = new LeakDetector();
|
|
21
|
+
* const result = await detector.detectLeak(
|
|
22
|
+
* async () => mcp.tools.call('my-tool', {}),
|
|
23
|
+
* { iterations: 50, threshold: 5 * 1024 * 1024 }
|
|
24
|
+
* );
|
|
25
|
+
* expect(result.hasLeak).toBe(false);
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
detectLeak(operation: () => Promise<void>, options?: LeakDetectionOptions): Promise<LeakDetectionResult>;
|
|
29
|
+
/**
|
|
30
|
+
* Run leak detection on a sync operation.
|
|
31
|
+
*/
|
|
32
|
+
detectLeakSync(operation: () => void, options?: LeakDetectionOptions): LeakDetectionResult;
|
|
33
|
+
/**
|
|
34
|
+
* Run parallel leak detection using multiple clients.
|
|
35
|
+
* Each worker gets its own client for true parallel HTTP requests.
|
|
36
|
+
*
|
|
37
|
+
* @param operationFactory - Factory that receives a client and worker index, returns an operation function
|
|
38
|
+
* @param options - Detection options including worker count and client factory
|
|
39
|
+
* @returns Combined leak detection result with per-worker statistics
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```typescript
|
|
43
|
+
* const detector = new LeakDetector();
|
|
44
|
+
* const result = await detector.detectLeakParallel(
|
|
45
|
+
* (client, workerId) => async () => client.tools.call('my-tool', {}),
|
|
46
|
+
* {
|
|
47
|
+
* iterations: 1000,
|
|
48
|
+
* workers: 5,
|
|
49
|
+
* clientFactory: () => server.createClient(),
|
|
50
|
+
* }
|
|
51
|
+
* );
|
|
52
|
+
* // 5 workers × ~80 req/s = ~400 req/s total
|
|
53
|
+
* console.log(result.totalRequestsPerSecond);
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
detectLeakParallel(operationFactory: (client: ParallelTestClient, workerId: number) => () => Promise<void>, options: ParallelLeakDetectionOptions & {
|
|
57
|
+
clientFactory: () => Promise<ParallelTestClient>;
|
|
58
|
+
}): Promise<ParallelLeakDetectionResult>;
|
|
59
|
+
/**
|
|
60
|
+
* Analyze heap samples for leak patterns using linear regression.
|
|
61
|
+
*/
|
|
62
|
+
private analyzeLeakPattern;
|
|
63
|
+
/**
|
|
64
|
+
* Generate interval-based measurements for detailed analysis.
|
|
65
|
+
*/
|
|
66
|
+
private generateIntervals;
|
|
67
|
+
/**
|
|
68
|
+
* Generate graph data points for visualization.
|
|
69
|
+
*/
|
|
70
|
+
private generateGraphData;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Quick leak check helper function.
|
|
74
|
+
* Returns true if no leak detected, throws if leak detected.
|
|
75
|
+
*/
|
|
76
|
+
export declare function assertNoLeak(operation: () => Promise<void>, options?: LeakDetectionOptions): Promise<LeakDetectionResult>;
|
|
77
|
+
/**
|
|
78
|
+
* Create a LeakDetector instance.
|
|
79
|
+
*/
|
|
80
|
+
export declare function createLeakDetector(): LeakDetector;
|
|
81
|
+
//# sourceMappingURL=leak-detector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"leak-detector.d.ts","sourceRoot":"","sources":["../../src/perf/leak-detector.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,oBAAoB,EACpB,mBAAmB,EAGnB,4BAA4B,EAC5B,2BAA2B,EAE3B,kBAAkB,EACnB,MAAM,SAAS,CAAC;AAkFjB;;;GAGG;AACH,qBAAa,YAAY;IACvB;;;;;;;;;;;;;;;;OAgBG;IACG,UAAU,CAAC,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,EAAE,oBAAoB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IA6C9G;;OAEG;IACH,cAAc,CAAC,SAAS,EAAE,MAAM,IAAI,EAAE,OAAO,CAAC,EAAE,oBAAoB,GAAG,mBAAmB;IAsC1F;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACG,kBAAkB,CACtB,gBAAgB,EAAE,CAAC,MAAM,EAAE,kBAAkB,EAAE,QAAQ,EAAE,MAAM,KAAK,MAAM,OAAO,CAAC,IAAI,CAAC,EACvF,OAAO,EAAE,4BAA4B,GAAG;QAAE,aAAa,EAAE,MAAM,OAAO,CAAC,kBAAkB,CAAC,CAAA;KAAE,GAC3F,OAAO,CAAC,2BAA2B,CAAC;IAiIvC;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAwF1B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAgCzB;;OAEG;IACH,OAAO,CAAC,iBAAiB;CAa1B;AAaD;;;GAGG;AACH,wBAAsB,YAAY,CAChC,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EAC9B,OAAO,CAAC,EAAE,oBAAoB,GAC7B,OAAO,CAAC,mBAAmB,CAAC,CAS9B;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,YAAY,CAEjD"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file metrics-collector.ts
|
|
3
|
+
* @description Memory and CPU metrics collection utilities
|
|
4
|
+
*/
|
|
5
|
+
import type { MemoryMetrics, CpuMetrics, PerfSnapshot } from './types';
|
|
6
|
+
/**
|
|
7
|
+
* Check if manual GC is available (requires --expose-gc flag)
|
|
8
|
+
*/
|
|
9
|
+
export declare function isGcAvailable(): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Force garbage collection if available.
|
|
12
|
+
* Requires Node.js to be started with --expose-gc flag.
|
|
13
|
+
*/
|
|
14
|
+
export declare function forceGc(): void;
|
|
15
|
+
/**
|
|
16
|
+
* Force multiple GC cycles to ensure thorough cleanup.
|
|
17
|
+
* V8 may need multiple passes to collect all garbage.
|
|
18
|
+
*/
|
|
19
|
+
export declare function forceFullGc(cycles?: number, delayMs?: number): Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Collects memory and CPU metrics for performance testing
|
|
22
|
+
*/
|
|
23
|
+
export declare class MetricsCollector {
|
|
24
|
+
private cpuStartUsage;
|
|
25
|
+
private baseline;
|
|
26
|
+
private measurements;
|
|
27
|
+
/**
|
|
28
|
+
* Capture the baseline snapshot.
|
|
29
|
+
* Forces GC to get a clean memory state.
|
|
30
|
+
*/
|
|
31
|
+
captureBaseline(forceGcCycles?: number): Promise<PerfSnapshot>;
|
|
32
|
+
/**
|
|
33
|
+
* Capture a snapshot of current memory and CPU state.
|
|
34
|
+
*/
|
|
35
|
+
captureSnapshot(label?: string): PerfSnapshot;
|
|
36
|
+
/**
|
|
37
|
+
* Start CPU tracking from this point.
|
|
38
|
+
*/
|
|
39
|
+
startCpuTracking(): void;
|
|
40
|
+
/**
|
|
41
|
+
* Get CPU usage since tracking started.
|
|
42
|
+
*/
|
|
43
|
+
getCpuUsage(): CpuMetrics;
|
|
44
|
+
/**
|
|
45
|
+
* Get current memory metrics.
|
|
46
|
+
*/
|
|
47
|
+
getMemoryMetrics(): MemoryMetrics;
|
|
48
|
+
/**
|
|
49
|
+
* Get the baseline snapshot if captured.
|
|
50
|
+
*/
|
|
51
|
+
getBaseline(): PerfSnapshot | null;
|
|
52
|
+
/**
|
|
53
|
+
* Get all measurement snapshots.
|
|
54
|
+
*/
|
|
55
|
+
getMeasurements(): PerfSnapshot[];
|
|
56
|
+
/**
|
|
57
|
+
* Calculate memory delta from baseline.
|
|
58
|
+
*/
|
|
59
|
+
calculateMemoryDelta(current: MemoryMetrics): {
|
|
60
|
+
heapUsed: number;
|
|
61
|
+
heapTotal: number;
|
|
62
|
+
rss: number;
|
|
63
|
+
} | null;
|
|
64
|
+
/**
|
|
65
|
+
* Reset the collector state.
|
|
66
|
+
*/
|
|
67
|
+
reset(): void;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Format bytes to human readable string.
|
|
71
|
+
*/
|
|
72
|
+
export declare function formatBytes(bytes: number): string;
|
|
73
|
+
/**
|
|
74
|
+
* Format microseconds to human readable string.
|
|
75
|
+
*/
|
|
76
|
+
export declare function formatMicroseconds(us: number): string;
|
|
77
|
+
/**
|
|
78
|
+
* Format milliseconds to human readable string.
|
|
79
|
+
*/
|
|
80
|
+
export declare function formatDuration(ms: number): string;
|
|
81
|
+
export declare function getGlobalCollector(): MetricsCollector;
|
|
82
|
+
export declare function resetGlobalCollector(): void;
|
|
83
|
+
//# sourceMappingURL=metrics-collector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics-collector.d.ts","sourceRoot":"","sources":["../../src/perf/metrics-collector.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAMvE;;GAEG;AACH,wBAAgB,aAAa,IAAI,OAAO,CAEvC;AAED;;;GAGG;AACH,wBAAgB,OAAO,IAAI,IAAI,CAI9B;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,MAAM,SAAI,EAAE,OAAO,SAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAWzE;AAMD;;GAEG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,aAAa,CAAgC;IACrD,OAAO,CAAC,QAAQ,CAA6B;IAC7C,OAAO,CAAC,YAAY,CAAsB;IAE1C;;;OAGG;IACG,eAAe,CAAC,aAAa,SAAI,GAAG,OAAO,CAAC,YAAY,CAAC;IAc/D;;OAEG;IACH,eAAe,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,YAAY;IA4B7C;;OAEG;IACH,gBAAgB,IAAI,IAAI;IAIxB;;OAEG;IACH,WAAW,IAAI,UAAU;IAUzB;;OAEG;IACH,gBAAgB,IAAI,aAAa;IAWjC;;OAEG;IACH,WAAW,IAAI,YAAY,GAAG,IAAI;IAIlC;;OAEG;IACH,eAAe,IAAI,YAAY,EAAE;IAIjC;;OAEG;IACH,oBAAoB,CAAC,OAAO,EAAE,aAAa,GAAG;QAC5C,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,GAAG,EAAE,MAAM,CAAC;KACb,GAAG,IAAI;IAYR;;OAEG;IACH,KAAK,IAAI,IAAI;CAKd;AAMD;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAcjD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAQrD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAQjD;AAcD,wBAAgB,kBAAkB,IAAI,gBAAgB,CAKrD;AAED,wBAAgB,oBAAoB,IAAI,IAAI,CAK3C"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file perf-fixtures.ts
|
|
3
|
+
* @description Performance testing fixtures implementation
|
|
4
|
+
*/
|
|
5
|
+
import type { PerfFixtures, PerfSnapshot, PerfThresholds, LeakDetectionOptions, LeakDetectionResult, PerfMeasurement, PerfIssue, ParallelLeakDetectionOptions, ParallelLeakDetectionResult, ParallelTestClient } from './types';
|
|
6
|
+
/**
|
|
7
|
+
* Creates a PerfFixtures instance for a test.
|
|
8
|
+
*/
|
|
9
|
+
export declare function createPerfFixtures(testName: string, project: string): PerfFixturesImpl;
|
|
10
|
+
/**
|
|
11
|
+
* Implementation of PerfFixtures interface.
|
|
12
|
+
*/
|
|
13
|
+
export declare class PerfFixturesImpl implements PerfFixtures {
|
|
14
|
+
private readonly testName;
|
|
15
|
+
private readonly project;
|
|
16
|
+
private collector;
|
|
17
|
+
private leakDetector;
|
|
18
|
+
private baselineSnapshot;
|
|
19
|
+
private startTime;
|
|
20
|
+
private issues;
|
|
21
|
+
private leakResults;
|
|
22
|
+
constructor(testName: string, project: string);
|
|
23
|
+
/**
|
|
24
|
+
* Capture baseline snapshot with GC.
|
|
25
|
+
*/
|
|
26
|
+
baseline(): Promise<PerfSnapshot>;
|
|
27
|
+
/**
|
|
28
|
+
* Capture a measurement snapshot.
|
|
29
|
+
*/
|
|
30
|
+
measure(label?: string): PerfSnapshot;
|
|
31
|
+
/**
|
|
32
|
+
* Run leak detection on an operation.
|
|
33
|
+
*/
|
|
34
|
+
checkLeak(operation: () => Promise<void>, options?: LeakDetectionOptions): Promise<LeakDetectionResult>;
|
|
35
|
+
/**
|
|
36
|
+
* Run parallel leak detection using multiple clients.
|
|
37
|
+
* Each worker gets its own client for true parallel HTTP requests.
|
|
38
|
+
*
|
|
39
|
+
* @param operationFactory - Factory that receives a client and worker index, returns an operation function
|
|
40
|
+
* @param options - Detection options including worker count and client factory
|
|
41
|
+
* @returns Combined leak detection result with per-worker statistics
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```typescript
|
|
45
|
+
* const result = await perf.checkLeakParallel(
|
|
46
|
+
* (client, workerId) => async () => {
|
|
47
|
+
* await client.tools.call('loadSkills', { skillIds: ['review-pr'] });
|
|
48
|
+
* },
|
|
49
|
+
* {
|
|
50
|
+
* iterations: 1000,
|
|
51
|
+
* workers: 5,
|
|
52
|
+
* clientFactory: () => server.createClient(),
|
|
53
|
+
* }
|
|
54
|
+
* );
|
|
55
|
+
* // 5 workers × ~80 req/s = ~400 req/s total
|
|
56
|
+
* expect(result.totalRequestsPerSecond).toBeGreaterThan(300);
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
checkLeakParallel(operationFactory: (client: ParallelTestClient, workerId: number) => () => Promise<void>, options: ParallelLeakDetectionOptions & {
|
|
60
|
+
clientFactory: () => Promise<ParallelTestClient>;
|
|
61
|
+
}): Promise<ParallelLeakDetectionResult>;
|
|
62
|
+
/**
|
|
63
|
+
* Assert that metrics are within thresholds.
|
|
64
|
+
*/
|
|
65
|
+
assertThresholds(thresholds: PerfThresholds): void;
|
|
66
|
+
/**
|
|
67
|
+
* Get all measurements so far.
|
|
68
|
+
*/
|
|
69
|
+
getMeasurements(): PerfSnapshot[];
|
|
70
|
+
/**
|
|
71
|
+
* Get the current test name.
|
|
72
|
+
*/
|
|
73
|
+
getTestName(): string;
|
|
74
|
+
/**
|
|
75
|
+
* Get the project name.
|
|
76
|
+
*/
|
|
77
|
+
getProject(): string;
|
|
78
|
+
/**
|
|
79
|
+
* Get all detected issues.
|
|
80
|
+
*/
|
|
81
|
+
getIssues(): PerfIssue[];
|
|
82
|
+
/**
|
|
83
|
+
* Build a complete PerfMeasurement for this test.
|
|
84
|
+
*/
|
|
85
|
+
buildMeasurement(): PerfMeasurement;
|
|
86
|
+
/**
|
|
87
|
+
* Reset the fixture state.
|
|
88
|
+
*/
|
|
89
|
+
reset(): void;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Add a measurement to the global collection.
|
|
93
|
+
*/
|
|
94
|
+
export declare function addGlobalMeasurement(measurement: PerfMeasurement): void;
|
|
95
|
+
/**
|
|
96
|
+
* Get all global measurements.
|
|
97
|
+
*/
|
|
98
|
+
export declare function getGlobalMeasurements(): PerfMeasurement[];
|
|
99
|
+
/**
|
|
100
|
+
* Clear all global measurements.
|
|
101
|
+
*/
|
|
102
|
+
export declare function clearGlobalMeasurements(): void;
|
|
103
|
+
/**
|
|
104
|
+
* Get measurements for a specific project.
|
|
105
|
+
*/
|
|
106
|
+
export declare function getMeasurementsForProject(project: string): PerfMeasurement[];
|
|
107
|
+
//# sourceMappingURL=perf-fixtures.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"perf-fixtures.d.ts","sourceRoot":"","sources":["../../src/perf/perf-fixtures.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,YAAY,EACZ,YAAY,EACZ,cAAc,EACd,oBAAoB,EACpB,mBAAmB,EACnB,eAAe,EACf,SAAS,EACT,4BAA4B,EAC5B,2BAA2B,EAC3B,kBAAkB,EACnB,MAAM,SAAS,CAAC;AAQjB;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,gBAAgB,CAEtF;AAED;;GAEG;AACH,qBAAa,gBAAiB,YAAW,YAAY;IASjD,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAT1B,OAAO,CAAC,SAAS,CAAmB;IACpC,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,gBAAgB,CAA6B;IACrD,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,WAAW,CAA6B;gBAG7B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM;IAMlC;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,YAAY,CAAC;IAMvC;;OAEG;IACH,OAAO,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,YAAY;IAIrC;;OAEG;IACG,SAAS,CAAC,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,EAAE,oBAAoB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAoB7G;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACG,iBAAiB,CACrB,gBAAgB,EAAE,CAAC,MAAM,EAAE,kBAAkB,EAAE,QAAQ,EAAE,MAAM,KAAK,MAAM,OAAO,CAAC,IAAI,CAAC,EACvF,OAAO,EAAE,4BAA4B,GAAG;QAAE,aAAa,EAAE,MAAM,OAAO,CAAC,kBAAkB,CAAC,CAAA;KAAE,GAC3F,OAAO,CAAC,2BAA2B,CAAC;IAoBvC;;OAEG;IACH,gBAAgB,CAAC,UAAU,EAAE,cAAc,GAAG,IAAI;IA6ElD;;OAEG;IACH,eAAe,IAAI,YAAY,EAAE;IAIjC;;OAEG;IACH,WAAW,IAAI,MAAM;IAIrB;;OAEG;IACH,UAAU,IAAI,MAAM;IAIpB;;OAEG;IACH,SAAS,IAAI,SAAS,EAAE;IAIxB;;OAEG;IACH,gBAAgB,IAAI,eAAe;IAuBnC;;OAEG;IACH,KAAK,IAAI,IAAI;CAOd;AAiBD;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,eAAe,GAAG,IAAI,CAEvE;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,eAAe,EAAE,CAEzD;AAED;;GAEG;AACH,wBAAgB,uBAAuB,IAAI,IAAI,CAG9C;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,EAAE,CAE5E"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file perf-setup.ts
|
|
3
|
+
* @description Jest setup file for performance tests
|
|
4
|
+
*
|
|
5
|
+
* This file is loaded via setupFilesAfterEnv in jest.perf.config.ts.
|
|
6
|
+
* It sets up the environment for performance testing.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=perf-setup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"perf-setup.d.ts","sourceRoot":"","sources":["../../src/perf/perf-setup.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA0DH,OAAO,EAAE,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __copyProps = (to, from, except, desc) => {
|
|
7
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
8
|
+
for (let key of __getOwnPropNames(from))
|
|
9
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
10
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
11
|
+
}
|
|
12
|
+
return to;
|
|
13
|
+
};
|
|
14
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
15
|
+
|
|
16
|
+
// libs/testing/src/perf/perf-setup.ts
|
|
17
|
+
var perf_setup_exports = {};
|
|
18
|
+
module.exports = __toCommonJS(perf_setup_exports);
|
|
19
|
+
|
|
20
|
+
// libs/testing/src/perf/metrics-collector.ts
|
|
21
|
+
function isGcAvailable() {
|
|
22
|
+
return typeof global.gc === "function";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// libs/testing/src/perf/perf-setup.ts
|
|
26
|
+
if (!isGcAvailable()) {
|
|
27
|
+
console.warn(
|
|
28
|
+
'\n[PerfSetup] WARNING: Manual garbage collection is not available.\nFor accurate memory measurements, run Node.js with --expose-gc flag:\n node --expose-gc ./node_modules/.bin/jest\nor add to jest.perf.config.ts:\n "testEnvironmentOptions": { "execArgv": ["--expose-gc"] }\n'
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
jest.setTimeout(12e4);
|
|
32
|
+
if (process.env["DEBUG"] !== "1" && process.env["PERF_VERBOSE"] !== "1") {
|
|
33
|
+
const originalLog = console.log;
|
|
34
|
+
const originalInfo = console.info;
|
|
35
|
+
const originalDebug = console.debug;
|
|
36
|
+
const originalWarn = console.warn;
|
|
37
|
+
const originalError = console.error;
|
|
38
|
+
beforeAll(() => {
|
|
39
|
+
console.log = jest.fn();
|
|
40
|
+
console.info = jest.fn();
|
|
41
|
+
console.debug = jest.fn();
|
|
42
|
+
console.warn = originalWarn;
|
|
43
|
+
console.error = originalError;
|
|
44
|
+
});
|
|
45
|
+
afterAll(() => {
|
|
46
|
+
console.log = originalLog;
|
|
47
|
+
console.info = originalInfo;
|
|
48
|
+
console.debug = originalDebug;
|
|
49
|
+
});
|
|
50
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file perf-test.ts
|
|
3
|
+
* @description Performance test fixture extending the standard test fixture
|
|
4
|
+
*
|
|
5
|
+
* Provides a Playwright-like fixture API for performance testing:
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { perfTest, expect } from '@frontmcp/testing';
|
|
10
|
+
*
|
|
11
|
+
* perfTest.describe('Cache Performance', () => {
|
|
12
|
+
* perfTest.use({
|
|
13
|
+
* server: 'apps/e2e/demo-e2e-cache/src/main.ts',
|
|
14
|
+
* project: 'demo-e2e-cache',
|
|
15
|
+
* publicMode: true,
|
|
16
|
+
* });
|
|
17
|
+
*
|
|
18
|
+
* perfTest('cache operations memory overhead', async ({ mcp, perf }) => {
|
|
19
|
+
* await perf.baseline();
|
|
20
|
+
*
|
|
21
|
+
* for (let i = 0; i < 100; i++) {
|
|
22
|
+
* await mcp.tools.call('expensive-operation', { operationId: `test-${i}` });
|
|
23
|
+
* }
|
|
24
|
+
*
|
|
25
|
+
* perf.assertThresholds({ maxHeapDelta: 10 * 1024 * 1024 });
|
|
26
|
+
* });
|
|
27
|
+
* });
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
import type { TestConfig, TestFixtures } from '../fixtures/fixture-types';
|
|
31
|
+
import type { PerfFixtures, PerfTestConfig } from './types';
|
|
32
|
+
/**
|
|
33
|
+
* Extended fixtures including performance testing
|
|
34
|
+
*/
|
|
35
|
+
export interface PerfTestFixtures extends TestFixtures {
|
|
36
|
+
/** Performance fixture */
|
|
37
|
+
perf: PerfFixtures;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Test function that receives extended fixtures
|
|
41
|
+
*/
|
|
42
|
+
export type PerfTestFn = (fixtures: PerfTestFixtures) => Promise<void> | void;
|
|
43
|
+
/**
|
|
44
|
+
* Enhanced test function with perf fixture support
|
|
45
|
+
*/
|
|
46
|
+
export interface PerfTestWithFixtures {
|
|
47
|
+
(name: string, fn: PerfTestFn): void;
|
|
48
|
+
/** Configure fixtures for this test file/suite */
|
|
49
|
+
use(config: TestConfig & PerfTestConfig): void;
|
|
50
|
+
/** Create a describe block */
|
|
51
|
+
describe: typeof describe;
|
|
52
|
+
/** Run before all tests in the file */
|
|
53
|
+
beforeAll: typeof beforeAll;
|
|
54
|
+
/** Run before each test */
|
|
55
|
+
beforeEach: typeof beforeEach;
|
|
56
|
+
/** Run after each test */
|
|
57
|
+
afterEach: typeof afterEach;
|
|
58
|
+
/** Run after all tests in the file */
|
|
59
|
+
afterAll: typeof afterAll;
|
|
60
|
+
/** Skip a test */
|
|
61
|
+
skip(name: string, fn: PerfTestFn): void;
|
|
62
|
+
/** Run only this test */
|
|
63
|
+
only(name: string, fn: PerfTestFn): void;
|
|
64
|
+
/** Mark test as todo (not implemented) */
|
|
65
|
+
todo(name: string): void;
|
|
66
|
+
}
|
|
67
|
+
declare const perfTest: PerfTestWithFixtures;
|
|
68
|
+
export { perfTest };
|
|
69
|
+
//# sourceMappingURL=perf-test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"perf-test.d.ts","sourceRoot":"","sources":["../../src/perf/perf-test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAMH,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAwC,MAAM,2BAA2B,CAAC;AAChH,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAO5D;;GAEG;AACH,MAAM,WAAW,gBAAiB,SAAQ,YAAY;IACpD,0BAA0B;IAC1B,IAAI,EAAE,YAAY,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,QAAQ,EAAE,gBAAgB,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AAE9E;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,GAAG,IAAI,CAAC;IAErC,kDAAkD;IAClD,GAAG,CAAC,MAAM,EAAE,UAAU,GAAG,cAAc,GAAG,IAAI,CAAC;IAE/C,8BAA8B;IAC9B,QAAQ,EAAE,OAAO,QAAQ,CAAC;IAE1B,uCAAuC;IACvC,SAAS,EAAE,OAAO,SAAS,CAAC;IAE5B,2BAA2B;IAC3B,UAAU,EAAE,OAAO,UAAU,CAAC;IAE9B,0BAA0B;IAC1B,SAAS,EAAE,OAAO,SAAS,CAAC;IAE5B,sCAAsC;IACtC,QAAQ,EAAE,OAAO,QAAQ,CAAC;IAE1B,kBAAkB;IAClB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,GAAG,IAAI,CAAC;IAEzC,yBAAyB;IACzB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,GAAG,IAAI,CAAC;IAEzC,0CAA0C;IAC1C,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAgTD,QAAA,MAAM,QAAQ,EAA2B,oBAAoB,CAAC;AAgB9D,OAAO,EAAE,QAAQ,EAAE,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file regression-detector.ts
|
|
3
|
+
* @description Performance regression detection by comparing current metrics to baseline
|
|
4
|
+
*/
|
|
5
|
+
import type { RegressionConfig, RegressionResult, MetricRegression, PerfMeasurement, TestBaseline, PerfBaseline } from './types';
|
|
6
|
+
/**
|
|
7
|
+
* Detects performance regressions by comparing current metrics to baseline.
|
|
8
|
+
*/
|
|
9
|
+
export declare class RegressionDetector {
|
|
10
|
+
private readonly config;
|
|
11
|
+
constructor(config?: RegressionConfig);
|
|
12
|
+
/**
|
|
13
|
+
* Detect regressions in a measurement compared to baseline.
|
|
14
|
+
*/
|
|
15
|
+
detectRegression(measurement: PerfMeasurement, baseline: TestBaseline): RegressionResult;
|
|
16
|
+
/**
|
|
17
|
+
* Detect regressions for multiple measurements.
|
|
18
|
+
*/
|
|
19
|
+
detectRegressions(measurements: PerfMeasurement[], baselines: PerfBaseline): RegressionResult[];
|
|
20
|
+
/**
|
|
21
|
+
* Check a single metric for regression.
|
|
22
|
+
*/
|
|
23
|
+
private checkMetric;
|
|
24
|
+
/**
|
|
25
|
+
* Build a human-readable message for regression result.
|
|
26
|
+
*/
|
|
27
|
+
private buildMessage;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Summarize regression results.
|
|
31
|
+
*/
|
|
32
|
+
export declare function summarizeRegressions(results: RegressionResult[]): {
|
|
33
|
+
total: number;
|
|
34
|
+
ok: number;
|
|
35
|
+
warnings: number;
|
|
36
|
+
regressions: number;
|
|
37
|
+
summary: string;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Filter regressions by status.
|
|
41
|
+
*/
|
|
42
|
+
export declare function filterByStatus(results: RegressionResult[], status: 'ok' | 'warning' | 'regression'): RegressionResult[];
|
|
43
|
+
/**
|
|
44
|
+
* Get the most severe metric regression from a result.
|
|
45
|
+
*/
|
|
46
|
+
export declare function getMostSevereMetric(result: RegressionResult): MetricRegression | null;
|
|
47
|
+
/**
|
|
48
|
+
* Get the global regression detector instance.
|
|
49
|
+
*/
|
|
50
|
+
export declare function getRegressionDetector(config?: RegressionConfig): RegressionDetector;
|
|
51
|
+
/**
|
|
52
|
+
* Reset the global regression detector.
|
|
53
|
+
*/
|
|
54
|
+
export declare function resetRegressionDetector(): void;
|
|
55
|
+
//# sourceMappingURL=regression-detector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"regression-detector.d.ts","sourceRoot":"","sources":["../../src/perf/regression-detector.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,YAAY,EACZ,YAAY,EACb,MAAM,SAAS,CAAC;AAiBjB;;GAEG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA6B;gBAExC,MAAM,CAAC,EAAE,gBAAgB;IAIrC;;OAEG;IACH,gBAAgB,CAAC,WAAW,EAAE,eAAe,EAAE,QAAQ,EAAE,YAAY,GAAG,gBAAgB;IAuCxF;;OAEG;IACH,iBAAiB,CAAC,YAAY,EAAE,eAAe,EAAE,EAAE,SAAS,EAAE,YAAY,GAAG,gBAAgB,EAAE;IAe/F;;OAEG;IACH,OAAO,CAAC,WAAW;IA8BnB;;OAEG;IACH,OAAO,CAAC,YAAY;CAerB;AAMD;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,gBAAgB,EAAE,GAAG;IACjE,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB,CAgBA;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,gBAAgB,EAAE,EAC3B,MAAM,EAAE,IAAI,GAAG,SAAS,GAAG,YAAY,GACtC,gBAAgB,EAAE,CAEpB;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,gBAAgB,GAAG,gBAAgB,GAAG,IAAI,CAYrF;AAQD;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,CAAC,EAAE,gBAAgB,GAAG,kBAAkB,CAKnF;AAED;;GAEG;AACH,wBAAgB,uBAAuB,IAAI,IAAI,CAE9C"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file report-generator.ts
|
|
3
|
+
* @description Generate JSON and Markdown reports from performance measurements
|
|
4
|
+
*/
|
|
5
|
+
import type { PerfReport, PerfMeasurement, PerfBaseline } from './types';
|
|
6
|
+
/**
|
|
7
|
+
* Generates performance reports in JSON and Markdown formats.
|
|
8
|
+
*/
|
|
9
|
+
export declare class ReportGenerator {
|
|
10
|
+
private readonly detector;
|
|
11
|
+
constructor();
|
|
12
|
+
/**
|
|
13
|
+
* Generate a complete performance report.
|
|
14
|
+
*/
|
|
15
|
+
generateReport(measurements: PerfMeasurement[], baseline?: PerfBaseline, gitInfo?: {
|
|
16
|
+
commitHash?: string;
|
|
17
|
+
branch?: string;
|
|
18
|
+
}): PerfReport;
|
|
19
|
+
/**
|
|
20
|
+
* Generate Markdown report for PR comments.
|
|
21
|
+
*/
|
|
22
|
+
generateMarkdownReport(report: PerfReport): string;
|
|
23
|
+
/**
|
|
24
|
+
* Generate JSON report.
|
|
25
|
+
*/
|
|
26
|
+
generateJsonReport(report: PerfReport): string;
|
|
27
|
+
/**
|
|
28
|
+
* Calculate summary statistics for measurements.
|
|
29
|
+
*/
|
|
30
|
+
private calculateSummary;
|
|
31
|
+
/**
|
|
32
|
+
* Get status emoji based on summary.
|
|
33
|
+
*/
|
|
34
|
+
private getStatusEmoji;
|
|
35
|
+
/**
|
|
36
|
+
* Get summary text.
|
|
37
|
+
*/
|
|
38
|
+
private getSummaryText;
|
|
39
|
+
/**
|
|
40
|
+
* Generate markdown table for project measurements.
|
|
41
|
+
*/
|
|
42
|
+
private generateProjectTable;
|
|
43
|
+
/**
|
|
44
|
+
* Get test status indicator.
|
|
45
|
+
*/
|
|
46
|
+
private getTestStatus;
|
|
47
|
+
/**
|
|
48
|
+
* Check if a leak detection result is a parallel result.
|
|
49
|
+
*/
|
|
50
|
+
private isParallelResult;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Generate and save reports to files.
|
|
54
|
+
*/
|
|
55
|
+
export declare function saveReports(measurements: PerfMeasurement[], outputDir: string, baseline?: PerfBaseline, gitInfo?: {
|
|
56
|
+
commitHash?: string;
|
|
57
|
+
branch?: string;
|
|
58
|
+
}): Promise<{
|
|
59
|
+
jsonPath: string;
|
|
60
|
+
markdownPath: string;
|
|
61
|
+
}>;
|
|
62
|
+
/**
|
|
63
|
+
* Create a report generator instance.
|
|
64
|
+
*/
|
|
65
|
+
export declare function createReportGenerator(): ReportGenerator;
|
|
66
|
+
//# sourceMappingURL=report-generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report-generator.d.ts","sourceRoot":"","sources":["../../src/perf/report-generator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,UAAU,EAGV,eAAe,EACf,YAAY,EAEb,MAAM,SAAS,CAAC;AAQjB;;GAEG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqB;;IAM9C;;OAEG;IACH,cAAc,CACZ,YAAY,EAAE,eAAe,EAAE,EAC/B,QAAQ,CAAC,EAAE,YAAY,EACvB,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GACjD,UAAU;IAwCb;;OAEG;IACH,sBAAsB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM;IAuIlD;;OAEG;IACH,kBAAkB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM;IAI9C;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAiCxB;;OAEG;IACH,OAAO,CAAC,cAAc;IAUtB;;OAEG;IACH,OAAO,CAAC,cAAc;IAatB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAiB5B;;OAEG;IACH,OAAO,CAAC,aAAa;IAiBrB;;OAEG;IACH,OAAO,CAAC,gBAAgB;CASzB;AAMD;;GAEG;AACH,wBAAsB,WAAW,CAC/B,YAAY,EAAE,eAAe,EAAE,EAC/B,SAAS,EAAE,MAAM,EACjB,QAAQ,CAAC,EAAE,YAAY,EACvB,OAAO,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GACjD,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC,CAerD;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,eAAe,CAEvD"}
|