test(#229): Add performance test suite for orchestrator
Add 14 performance benchmarks across 3 test files: - Spawner throughput: single/sequential/concurrent spawn latency, session lookup, list performance, memory efficiency - Queue service: backoff calculation throughput, validation perf - Secret scanner: content scanning throughput, pattern scalability Adds test:perf script to package.json. Fixes #229 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
/**
|
||||
* Performance Test: Secret Scanner Throughput
|
||||
*
|
||||
* Benchmarks the secret scanner's ability to scan content
|
||||
* at scale without degrading performance.
|
||||
*
|
||||
* Covers issue #229 (ORCH-128)
|
||||
*/
|
||||
import { describe, it, expect, beforeEach, vi } from "vitest";
|
||||
import { SecretScannerService } from "../../src/git/secret-scanner.service";
|
||||
import { ConfigService } from "@nestjs/config";
|
||||
|
||||
describe("Performance: Secret Scanner", () => {
|
||||
let scanner: SecretScannerService;
|
||||
|
||||
const mockConfigService = {
|
||||
get: vi.fn((_key: string, defaultValue?: unknown) => defaultValue),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
scanner = new SecretScannerService(mockConfigService as unknown as ConfigService);
|
||||
});
|
||||
|
||||
describe("Content scanning throughput", () => {
|
||||
it("should scan 1000 lines of clean code in under 50ms", () => {
|
||||
const lines = Array.from(
|
||||
{ length: 1000 },
|
||||
(_, i) => `const value${String(i)} = computeResult(${String(i)}, "param-${String(i)}");`
|
||||
);
|
||||
const content = lines.join("\n");
|
||||
|
||||
const start = performance.now();
|
||||
const result = scanner.scanContent(content, "test-file.ts");
|
||||
const duration = performance.now() - start;
|
||||
|
||||
expect(duration).toBeLessThan(50);
|
||||
expect(result.matches).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("should scan 100 files worth of content in under 500ms", () => {
|
||||
const fileContent = Array.from(
|
||||
{ length: 100 },
|
||||
(_, i) => `export function handler${String(i)}(): string { return "result-${String(i)}"; }`
|
||||
).join("\n");
|
||||
|
||||
const start = performance.now();
|
||||
|
||||
for (let i = 0; i < 100; i++) {
|
||||
scanner.scanContent(fileContent, `file-${String(i)}.ts`);
|
||||
}
|
||||
|
||||
const duration = performance.now() - start;
|
||||
expect(duration).toBeLessThan(500);
|
||||
});
|
||||
|
||||
it("should detect secrets in large content without performance regression", () => {
|
||||
// Mix clean code with embedded secrets
|
||||
const lines: string[] = [];
|
||||
for (let i = 0; i < 500; i++) {
|
||||
lines.push(`const config${String(i)} = { host: "localhost", port: ${String(3000 + i)} };`);
|
||||
}
|
||||
// Insert a secret at line 250
|
||||
lines[250] = 'const apiKey = "AKIA1234567890ABCDEF"; // AWS access key';
|
||||
|
||||
const content = lines.join("\n");
|
||||
|
||||
const start = performance.now();
|
||||
const result = scanner.scanContent(content, "config.ts");
|
||||
const duration = performance.now() - start;
|
||||
|
||||
expect(duration).toBeLessThan(100);
|
||||
expect(result.matches.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("should handle content with many false-positive patterns efficiently", () => {
|
||||
// Content with many patterns that look like secrets but are placeholders
|
||||
const lines = Array.from(
|
||||
{ length: 200 },
|
||||
(_, i) => `const example_key_${String(i)} = "test-xxxx-example-${String(i)}";`
|
||||
);
|
||||
const content = lines.join("\n");
|
||||
|
||||
const start = performance.now();
|
||||
const result = scanner.scanContent(content, "examples.ts");
|
||||
const duration = performance.now() - start;
|
||||
|
||||
expect(duration).toBeLessThan(100);
|
||||
// Placeholders should be whitelisted
|
||||
expect(result.matches).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Pattern matching scalability", () => {
|
||||
it("should maintain consistent scan time regardless of content position", () => {
|
||||
const baseContent = Array.from(
|
||||
{ length: 1000 },
|
||||
(_, i) => `const x${String(i)} = ${String(i)};`
|
||||
);
|
||||
|
||||
// Secret at start
|
||||
const contentStart = ['const key = "AKIA1234567890ABCDEF";', ...baseContent].join("\n");
|
||||
|
||||
// Secret at end
|
||||
const contentEnd = [...baseContent, 'const key = "AKIA1234567890ABCDEF";'].join("\n");
|
||||
|
||||
const startTime1 = performance.now();
|
||||
scanner.scanContent(contentStart, "start.ts");
|
||||
const duration1 = performance.now() - startTime1;
|
||||
|
||||
const startTime2 = performance.now();
|
||||
scanner.scanContent(contentEnd, "end.ts");
|
||||
const duration2 = performance.now() - startTime2;
|
||||
|
||||
// Both should complete quickly
|
||||
expect(duration1).toBeLessThan(100);
|
||||
expect(duration2).toBeLessThan(100);
|
||||
|
||||
// And be within 5x of each other (no pathological behavior)
|
||||
const ratio = Math.max(duration1, duration2) / Math.max(0.01, Math.min(duration1, duration2));
|
||||
expect(ratio).toBeLessThan(5);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user