ERP-node/frontend/lib/services/__tests__/buttonDataflowPerformance.t...

396 lines
12 KiB
TypeScript

/**
* 🔥 버튼 제어관리 성능 테스트
*
* 목표 성능:
* - 즉시 응답: 50-200ms
* - 캐시 히트: 1-10ms
* - 백그라운드 작업: 사용자 체감 없음
*/
import { optimizedButtonDataflowService } from "../optimizedButtonDataflowService";
import { dataflowConfigCache } from "../dataflowCache";
import { dataflowJobQueue } from "../dataflowJobQueue";
import { ButtonActionType, ButtonTypeConfig } from "@/types/screen";
// Mock API client
jest.mock("@/lib/api/client", () => ({
apiClient: {
get: jest.fn(),
post: jest.fn(),
put: jest.fn(),
},
}));
describe("🔥 Button Dataflow Performance Tests", () => {
beforeEach(() => {
// 캐시 초기화
dataflowConfigCache.clearAllCache();
dataflowJobQueue.clearQueue();
});
describe("📊 Cache Performance", () => {
it("should load config from server on first request", async () => {
const startTime = performance.now();
const config = await dataflowConfigCache.getConfig("test-button-1");
const endTime = performance.now();
const responseTime = endTime - startTime;
// 첫 번째 요청은 서버 로딩으로 인해 더 오래 걸릴 수 있음
expect(responseTime).toBeLessThan(1000); // 1초 이내
const metrics = dataflowConfigCache.getMetrics();
expect(metrics.totalRequests).toBe(1);
expect(metrics.cacheMisses).toBe(1);
});
it("should return cached config in under 10ms", async () => {
// 먼저 캐시에 로드
await dataflowConfigCache.getConfig("test-button-2");
// 두 번째 요청 시간 측정
const startTime = performance.now();
const config = await dataflowConfigCache.getConfig("test-button-2");
const endTime = performance.now();
const responseTime = endTime - startTime;
// 🔥 캐시 히트는 10ms 이내여야 함
expect(responseTime).toBeLessThan(10);
const metrics = dataflowConfigCache.getMetrics();
expect(metrics.cacheHits).toBeGreaterThan(0);
expect(metrics.hitRate).toBeGreaterThan(0);
});
it("should maintain cache performance under load", async () => {
const buttonIds = Array.from({ length: 50 }, (_, i) => `test-button-${i}`);
// 첫 번째 로드 (캐시 채우기)
await Promise.all(buttonIds.map((id) => dataflowConfigCache.getConfig(id)));
// 캐시된 데이터 성능 테스트
const startTime = performance.now();
await Promise.all(buttonIds.map((id) => dataflowConfigCache.getConfig(id)));
const endTime = performance.now();
const totalTime = endTime - startTime;
const averageTime = totalTime / buttonIds.length;
// 🔥 평균 캐시 응답 시간 5ms 이내
expect(averageTime).toBeLessThan(5);
const metrics = dataflowConfigCache.getMetrics();
expect(metrics.hitRate).toBeGreaterThan(80); // 80% 이상 히트율
});
});
describe("⚡ Button Execution Performance", () => {
const mockButtonConfig: ButtonTypeConfig = {
actionType: "save" as ButtonActionType,
enableDataflowControl: true,
dataflowTiming: "after",
dataflowConfig: {
controlMode: "simple",
selectedDiagramId: 1,
selectedRelationshipId: "rel-123",
},
};
it("should execute button action in under 200ms", async () => {
const startTime = performance.now();
const result = await optimizedButtonDataflowService.executeButtonWithDataflow(
"test-button-3",
"save",
mockButtonConfig,
{ testData: "value" },
"DEFAULT",
);
const endTime = performance.now();
const responseTime = endTime - startTime;
// 🔥 즉시 응답 목표: 200ms 이내
expect(responseTime).toBeLessThan(200);
expect(result.jobId).toBeDefined();
});
it("should handle after timing with immediate response", async () => {
const config = { ...mockButtonConfig, dataflowTiming: "after" as const };
const startTime = performance.now();
const result = await optimizedButtonDataflowService.executeButtonWithDataflow(
"test-button-4",
"save",
config,
{ testData: "value" },
"DEFAULT",
);
const endTime = performance.now();
const responseTime = endTime - startTime;
// After 타이밍은 기존 액션 즉시 실행이므로 빠름
expect(responseTime).toBeLessThan(150);
expect(result.immediateResult).toBeDefined();
expect(result.timing).toBe("after");
});
it("should handle simple validation quickly", async () => {
const config = {
...mockButtonConfig,
dataflowTiming: "before" as const,
dataflowConfig: {
controlMode: "advanced" as const,
directControl: {
sourceTable: "test_table",
triggerType: "insert" as const,
conditions: [
{
id: "cond1",
type: "condition" as const,
field: "status",
operator: "=" as const,
value: "active",
},
],
actions: [],
},
},
};
const startTime = performance.now();
const result = await optimizedButtonDataflowService.executeButtonWithDataflow(
"test-button-5",
"save",
config,
{ status: "active" },
"DEFAULT",
);
const endTime = performance.now();
const responseTime = endTime - startTime;
// 🔥 간단한 검증은 50ms 이내
expect(responseTime).toBeLessThan(50);
expect(result.immediateResult).toBeDefined();
});
});
describe("🚀 Job Queue Performance", () => {
it("should enqueue jobs instantly", () => {
const startTime = performance.now();
const jobId = dataflowJobQueue.enqueue(
"test-button-6",
"save",
mockButtonConfig,
{ testData: "value" },
"DEFAULT",
"normal",
);
const endTime = performance.now();
const responseTime = endTime - startTime;
// 🔥 큐잉은 즉시 (5ms 이내)
expect(responseTime).toBeLessThan(5);
expect(jobId).toBeDefined();
expect(jobId).toMatch(/^job_/);
});
it("should handle multiple concurrent jobs", () => {
const jobCount = 20;
const startTime = performance.now();
const jobIds = Array.from({ length: jobCount }, (_, i) =>
dataflowJobQueue.enqueue(
`test-button-${i}`,
"save",
mockButtonConfig,
{ testData: `value-${i}` },
"DEFAULT",
"normal",
),
);
const endTime = performance.now();
const totalTime = endTime - startTime;
const averageTime = totalTime / jobCount;
// 🔥 평균 큐잉 시간 1ms 이내
expect(averageTime).toBeLessThan(1);
expect(jobIds).toHaveLength(jobCount);
const metrics = dataflowJobQueue.getMetrics();
expect(metrics.totalJobs).toBe(jobCount);
});
it("should prioritize high priority jobs", () => {
// 일반 우선순위 작업들 추가
const normalJobs = Array.from({ length: 5 }, (_, i) =>
dataflowJobQueue.enqueue(`normal-button-${i}`, "save", mockButtonConfig, {}, "DEFAULT", "normal"),
);
// 높은 우선순위 작업 추가
const highJob = dataflowJobQueue.enqueue("high-priority-button", "save", mockButtonConfig, {}, "DEFAULT", "high");
const queueInfo = dataflowJobQueue.getQueueInfo();
// 높은 우선순위 작업이 큐의 맨 앞에 있어야 함
expect(queueInfo.pending[0].id).toBe(highJob);
expect(queueInfo.pending[0].priority).toBe("high");
});
});
describe("📈 Performance Metrics", () => {
it("should track cache metrics accurately", async () => {
// 캐시 미스 발생
await dataflowConfigCache.getConfig("metrics-test-1");
await dataflowConfigCache.getConfig("metrics-test-2");
// 캐시 히트 발생
await dataflowConfigCache.getConfig("metrics-test-1");
await dataflowConfigCache.getConfig("metrics-test-1");
const metrics = dataflowConfigCache.getMetrics();
expect(metrics.totalRequests).toBe(4);
expect(metrics.cacheHits).toBe(2);
expect(metrics.cacheMisses).toBe(2);
expect(metrics.hitRate).toBe(50);
expect(metrics.averageResponseTime).toBeGreaterThan(0);
});
it("should track queue metrics accurately", () => {
// 작업 추가
dataflowJobQueue.enqueue("metrics-button-1", "save", mockButtonConfig, {}, "DEFAULT");
dataflowJobQueue.enqueue("metrics-button-2", "delete", mockButtonConfig, {}, "DEFAULT");
const metrics = dataflowJobQueue.getMetrics();
expect(metrics.totalJobs).toBe(2);
expect(metrics.pendingJobs).toBe(2);
expect(metrics.processingJobs).toBe(0);
});
it("should provide performance recommendations", () => {
// 느린 응답 시뮬레이션
const slowCache = dataflowConfigCache as any;
slowCache.metrics.averageResponseTime = 500; // 500ms
const metrics = dataflowConfigCache.getMetrics();
expect(metrics.averageResponseTime).toBe(500);
// 성능 개선 권장사항 확인 (실제 구현에서)
// expect(recommendations).toContain('캐싱 설정을 확인하세요');
});
});
describe("🔧 Integration Performance", () => {
it("should maintain performance under realistic load", async () => {
const testScenarios = [
{ timing: "after", count: 10 },
{ timing: "before", count: 5 },
{ timing: "replace", count: 3 },
];
const startTime = performance.now();
for (const scenario of testScenarios) {
const promises = Array.from({ length: scenario.count }, (_, i) =>
optimizedButtonDataflowService.executeButtonWithDataflow(
`load-test-${scenario.timing}-${i}`,
"save",
{ ...mockButtonConfig, dataflowTiming: scenario.timing as any },
{ testData: `value-${i}` },
"DEFAULT",
),
);
await Promise.all(promises);
}
const endTime = performance.now();
const totalTime = endTime - startTime;
const totalRequests = testScenarios.reduce((sum, s) => sum + s.count, 0);
const averageTime = totalTime / totalRequests;
// 🔥 실제 환경에서 평균 응답 시간 300ms 이내
expect(averageTime).toBeLessThan(300);
console.log(`Performance Test Results:`);
console.log(` Total requests: ${totalRequests}`);
console.log(` Total time: ${totalTime.toFixed(2)}ms`);
console.log(` Average time: ${averageTime.toFixed(2)}ms`);
});
});
});
// 🔥 성능 벤치마크 유틸리티
export class PerformanceBenchmark {
private results: Array<{
name: string;
time: number;
success: boolean;
}> = [];
async measure<T>(name: string, fn: () => Promise<T>): Promise<T> {
const startTime = performance.now();
let success = true;
let result: T;
try {
result = await fn();
} catch (error) {
success = false;
throw error;
} finally {
const endTime = performance.now();
this.results.push({
name,
time: endTime - startTime,
success,
});
}
return result!;
}
getResults() {
return {
total: this.results.length,
successful: this.results.filter((r) => r.success).length,
failed: this.results.filter((r) => r.success === false).length,
averageTime: this.results.reduce((sum, r) => sum + r.time, 0) / this.results.length,
fastest: Math.min(...this.results.map((r) => r.time)),
slowest: Math.max(...this.results.map((r) => r.time)),
details: this.results,
};
}
printReport() {
const results = this.getResults();
console.log("\n🔥 Performance Benchmark Report");
console.log("================================");
console.log(`Total tests: ${results.total}`);
console.log(`Successful: ${results.successful} (${((results.successful / results.total) * 100).toFixed(1)}%)`);
console.log(`Failed: ${results.failed}`);
console.log(`Average time: ${results.averageTime.toFixed(2)}ms`);
console.log(`Fastest: ${results.fastest.toFixed(2)}ms`);
console.log(`Slowest: ${results.slowest.toFixed(2)}ms`);
console.log("\nDetailed Results:");
results.details.forEach((r) => {
const status = r.success ? "✅" : "❌";
console.log(` ${status} ${r.name}: ${r.time.toFixed(2)}ms`);
});
}
}