/** * ๐Ÿ”ฅ ๋ฒ„ํŠผ ์ œ์–ด๊ด€๋ฆฌ ์„ฑ๋Šฅ ๊ฒ€์ฆ ์Šคํฌ๋ฆฝํŠธ * * ์‹ค์ œ ํ™˜๊ฒฝ์—์„œ ์„ฑ๋Šฅ ๋ชฉํ‘œ ๋‹ฌ์„ฑ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. * * ์‚ฌ์šฉ๋ฒ•: * npm run performance-test */ import { optimizedButtonDataflowService } from "../lib/services/optimizedButtonDataflowService"; import { dataflowConfigCache } from "../lib/services/dataflowCache"; import { dataflowJobQueue } from "../lib/services/dataflowJobQueue"; import { PerformanceBenchmark } from "../lib/services/__tests__/buttonDataflowPerformance.test"; import { ButtonActionType, ButtonTypeConfig } from "../types/screen"; // ๐Ÿ”ฅ ์„ฑ๋Šฅ ๋ชฉํ‘œ ์ƒ์ˆ˜ const PERFORMANCE_TARGETS = { IMMEDIATE_RESPONSE: 200, // ms CACHE_HIT: 10, // ms SIMPLE_VALIDATION: 50, // ms QUEUE_ENQUEUE: 5, // ms CACHE_HIT_RATE: 80, // % } as const; /** * ๐Ÿ”ฅ ๋ฉ”์ธ ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ ์‹คํ–‰ */ async function runPerformanceTests() { console.log("๐Ÿ”ฅ Button Dataflow Performance Verification"); console.log("==========================================\n"); const benchmark = new PerformanceBenchmark(); let totalTests = 0; let passedTests = 0; try { // 1. ์บ์‹œ ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ console.log("๐Ÿ“Š Testing Cache Performance..."); const cacheResults = await testCachePerformance(benchmark); totalTests += cacheResults.total; passedTests += cacheResults.passed; // 2. ๋ฒ„ํŠผ ์‹คํ–‰ ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ console.log("\nโšก Testing Button Execution Performance..."); const buttonResults = await testButtonExecutionPerformance(benchmark); totalTests += buttonResults.total; passedTests += buttonResults.passed; // 3. ํ ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ console.log("\n๐Ÿš€ Testing Job Queue Performance..."); const queueResults = await testJobQueuePerformance(benchmark); totalTests += queueResults.total; passedTests += queueResults.passed; // 4. ํ†ตํ•ฉ ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ console.log("\n๐Ÿ”ง Testing Integration Performance..."); const integrationResults = await testIntegrationPerformance(benchmark); totalTests += integrationResults.total; passedTests += integrationResults.passed; // ์ตœ์ข… ๊ฒฐ๊ณผ ์ถœ๋ ฅ console.log("\n" + "=".repeat(50)); console.log("๐ŸŽฏ PERFORMANCE TEST SUMMARY"); console.log("=".repeat(50)); console.log(`Total Tests: ${totalTests}`); console.log(`Passed: ${passedTests} (${((passedTests / totalTests) * 100).toFixed(1)}%)`); console.log(`Failed: ${totalTests - passedTests}`); // ๋ฒค์น˜๋งˆํฌ ๋ฆฌํฌํŠธ benchmark.printReport(); // ์„ฑ๊ณต/์‹คํŒจ ํŒ์ • const successRate = (passedTests / totalTests) * 100; if (successRate >= 90) { console.log("\n๐ŸŽ‰ PERFORMANCE VERIFICATION PASSED!"); console.log("All performance targets have been met."); process.exit(0); } else { console.log("\nโš ๏ธ PERFORMANCE VERIFICATION FAILED!"); console.log("Some performance targets were not met."); process.exit(1); } } catch (error) { console.error("\nโŒ Performance test failed:", error); process.exit(1); } } /** * ์บ์‹œ ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ */ async function testCachePerformance(benchmark: PerformanceBenchmark) { let total = 0; let passed = 0; // ์บ์‹œ ์ดˆ๊ธฐํ™” dataflowConfigCache.clearAllCache(); // 1. ์ฒซ ๋ฒˆ์งธ ๋กœ๋“œ ์„ฑ๋Šฅ (์„œ๋ฒ„ ํ˜ธ์ถœ) total++; try { const time = await benchmark.measure("Cache First Load", async () => { return await dataflowConfigCache.getConfig("perf-test-1"); }); // ์ฒซ ๋กœ๋“œ๋Š” 1์ดˆ ์ด๋‚ด๋ฉด ํ†ต๊ณผ if (benchmark.getResults().details.slice(-1)[0].time < 1000) { passed++; console.log(" โœ… First load performance: PASSED"); } else { console.log(" โŒ First load performance: FAILED"); } } catch (error) { console.log(" โŒ First load test: ERROR -", error.message); } // 2. ์บ์‹œ ํžˆํŠธ ์„ฑ๋Šฅ total++; try { await benchmark.measure("Cache Hit Performance", async () => { return await dataflowConfigCache.getConfig("perf-test-1"); }); const hitTime = benchmark.getResults().details.slice(-1)[0].time; if (hitTime < PERFORMANCE_TARGETS.CACHE_HIT) { passed++; console.log(` โœ… Cache hit performance: PASSED (${hitTime.toFixed(2)}ms < ${PERFORMANCE_TARGETS.CACHE_HIT}ms)`); } else { console.log(` โŒ Cache hit performance: FAILED (${hitTime.toFixed(2)}ms >= ${PERFORMANCE_TARGETS.CACHE_HIT}ms)`); } } catch (error) { console.log(" โŒ Cache hit test: ERROR -", error.message); } // 3. ์บ์‹œ ํžˆํŠธ์œจ ํ…Œ์ŠคํŠธ total++; try { // ์—ฌ๋Ÿฌ ๋ฒ„ํŠผ์— ๋Œ€ํ•ด ์บ์‹œ ๋กœ๋“œ ๋ฐ ํžˆํŠธ ํ…Œ์ŠคํŠธ const buttonIds = Array.from({ length: 10 }, (_, i) => `perf-test-${i}`); // ์ฒซ ๋ฒˆ์งธ ๋กœ๋“œ (์บ์‹œ ์ฑ„์šฐ๊ธฐ) await Promise.all(buttonIds.map((id) => dataflowConfigCache.getConfig(id))); // ๋‘ ๋ฒˆ์งธ ๋กœ๋“œ (์บ์‹œ ํžˆํŠธ) await Promise.all(buttonIds.map((id) => dataflowConfigCache.getConfig(id))); const metrics = dataflowConfigCache.getMetrics(); if (metrics.hitRate >= PERFORMANCE_TARGETS.CACHE_HIT_RATE) { passed++; console.log( ` โœ… Cache hit rate: PASSED (${metrics.hitRate.toFixed(1)}% >= ${PERFORMANCE_TARGETS.CACHE_HIT_RATE}%)`, ); } else { console.log( ` โŒ Cache hit rate: FAILED (${metrics.hitRate.toFixed(1)}% < ${PERFORMANCE_TARGETS.CACHE_HIT_RATE}%)`, ); } } catch (error) { console.log(" โŒ Cache hit rate test: ERROR -", error.message); } return { total, passed }; } /** * ๋ฒ„ํŠผ ์‹คํ–‰ ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ */ async function testButtonExecutionPerformance(benchmark: PerformanceBenchmark) { let total = 0; let passed = 0; const mockConfig: ButtonTypeConfig = { actionType: "save" as ButtonActionType, enableDataflowControl: true, dataflowTiming: "after", dataflowConfig: { controlMode: "simple", selectedDiagramId: 1, selectedRelationshipId: "rel-123", }, }; // 1. After ํƒ€์ด๋ฐ ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ total++; try { await benchmark.measure("Button Execution (After)", async () => { return await optimizedButtonDataflowService.executeButtonWithDataflow( "perf-button-1", "save", mockConfig, { testData: "value" }, "DEFAULT", ); }); const execTime = benchmark.getResults().details.slice(-1)[0].time; if (execTime < PERFORMANCE_TARGETS.IMMEDIATE_RESPONSE) { passed++; console.log( ` โœ… After timing execution: PASSED (${execTime.toFixed(2)}ms < ${PERFORMANCE_TARGETS.IMMEDIATE_RESPONSE}ms)`, ); } else { console.log( ` โŒ After timing execution: FAILED (${execTime.toFixed(2)}ms >= ${PERFORMANCE_TARGETS.IMMEDIATE_RESPONSE}ms)`, ); } } catch (error) { console.log(" โŒ After timing test: ERROR -", error.message); } // 2. Before ํƒ€์ด๋ฐ (๊ฐ„๋‹จํ•œ ๊ฒ€์ฆ) ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ total++; try { const beforeConfig = { ...mockConfig, 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: [], }, }, }; await benchmark.measure("Button Execution (Before Simple)", async () => { return await optimizedButtonDataflowService.executeButtonWithDataflow( "perf-button-2", "save", beforeConfig, { status: "active" }, "DEFAULT", ); }); const execTime = benchmark.getResults().details.slice(-1)[0].time; if (execTime < PERFORMANCE_TARGETS.SIMPLE_VALIDATION) { passed++; console.log( ` โœ… Before simple validation: PASSED (${execTime.toFixed(2)}ms < ${PERFORMANCE_TARGETS.SIMPLE_VALIDATION}ms)`, ); } else { console.log( ` โŒ Before simple validation: FAILED (${execTime.toFixed(2)}ms >= ${PERFORMANCE_TARGETS.SIMPLE_VALIDATION}ms)`, ); } } catch (error) { console.log(" โŒ Before timing test: ERROR -", error.message); } // 3. ์ œ์–ด๊ด€๋ฆฌ ์—†๋Š” ์‹คํ–‰ ์„ฑ๋Šฅ total++; try { const noDataflowConfig = { ...mockConfig, enableDataflowControl: false, }; await benchmark.measure("Button Execution (No Dataflow)", async () => { return await optimizedButtonDataflowService.executeButtonWithDataflow( "perf-button-3", "save", noDataflowConfig, { testData: "value" }, "DEFAULT", ); }); const execTime = benchmark.getResults().details.slice(-1)[0].time; if (execTime < 100) { // ์ œ์–ด๊ด€๋ฆฌ ์—†์œผ๋ฉด ๋” ๋นจ๋ผ์•ผ ํ•จ passed++; console.log(` โœ… No dataflow execution: PASSED (${execTime.toFixed(2)}ms < 100ms)`); } else { console.log(` โŒ No dataflow execution: FAILED (${execTime.toFixed(2)}ms >= 100ms)`); } } catch (error) { console.log(" โŒ No dataflow test: ERROR -", error.message); } return { total, passed }; } /** * ์ž‘์—… ํ ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ */ async function testJobQueuePerformance(benchmark: PerformanceBenchmark) { let total = 0; let passed = 0; const mockConfig: ButtonTypeConfig = { actionType: "save" as ButtonActionType, enableDataflowControl: true, dataflowTiming: "after", dataflowConfig: { controlMode: "simple", selectedDiagramId: 1, selectedRelationshipId: "rel-123", }, }; // ํ ์ดˆ๊ธฐํ™” dataflowJobQueue.clearQueue(); // 1. ๋‹จ์ผ ์ž‘์—… ํ์ž‰ ์„ฑ๋Šฅ total++; try { await benchmark.measure("Job Queue Enqueue (Single)", async () => { return dataflowJobQueue.enqueue("queue-perf-1", "save", mockConfig, {}, "DEFAULT", "normal"); }); const queueTime = benchmark.getResults().details.slice(-1)[0].time; if (queueTime < PERFORMANCE_TARGETS.QUEUE_ENQUEUE) { passed++; console.log( ` โœ… Single job enqueue: PASSED (${queueTime.toFixed(2)}ms < ${PERFORMANCE_TARGETS.QUEUE_ENQUEUE}ms)`, ); } else { console.log( ` โŒ Single job enqueue: FAILED (${queueTime.toFixed(2)}ms >= ${PERFORMANCE_TARGETS.QUEUE_ENQUEUE}ms)`, ); } } catch (error) { console.log(" โŒ Single enqueue test: ERROR -", error.message); } // 2. ๋Œ€๋Ÿ‰ ์ž‘์—… ํ์ž‰ ์„ฑ๋Šฅ total++; try { const jobCount = 50; await benchmark.measure("Job Queue Enqueue (Batch)", async () => { const promises = Array.from({ length: jobCount }, (_, i) => dataflowJobQueue.enqueue(`queue-perf-batch-${i}`, "save", mockConfig, {}, "DEFAULT", "normal"), ); return Promise.resolve(promises); }); const batchTime = benchmark.getResults().details.slice(-1)[0].time; const averageTime = batchTime / jobCount; if (averageTime < PERFORMANCE_TARGETS.QUEUE_ENQUEUE) { passed++; console.log( ` โœ… Batch job enqueue: PASSED (avg ${averageTime.toFixed(2)}ms < ${PERFORMANCE_TARGETS.QUEUE_ENQUEUE}ms)`, ); } else { console.log( ` โŒ Batch job enqueue: FAILED (avg ${averageTime.toFixed(2)}ms >= ${PERFORMANCE_TARGETS.QUEUE_ENQUEUE}ms)`, ); } } catch (error) { console.log(" โŒ Batch enqueue test: ERROR -", error.message); } // 3. ์šฐ์„ ์ˆœ์œ„ ์ฒ˜๋ฆฌ ํ™•์ธ total++; try { // ์ผ๋ฐ˜ ์šฐ์„ ์ˆœ์œ„ ์ž‘์—…๋“ค const normalJobs = Array.from({ length: 5 }, (_, i) => dataflowJobQueue.enqueue(`normal-${i}`, "save", mockConfig, {}, "DEFAULT", "normal"), ); // ๋†’์€ ์šฐ์„ ์ˆœ์œ„ ์ž‘์—… const highJob = dataflowJobQueue.enqueue("high-priority", "save", mockConfig, {}, "DEFAULT", "high"); const queueInfo = dataflowJobQueue.getQueueInfo(); // ๋†’์€ ์šฐ์„ ์ˆœ์œ„ ์ž‘์—…์ด ๋งจ ์•ž์— ์žˆ๋Š”์ง€ ํ™•์ธ if (queueInfo.pending[0].id === highJob && queueInfo.pending[0].priority === "high") { passed++; console.log(" โœ… Priority handling: PASSED"); } else { console.log(" โŒ Priority handling: FAILED"); } } catch (error) { console.log(" โŒ Priority test: ERROR -", error.message); } return { total, passed }; } /** * ํ†ตํ•ฉ ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ */ async function testIntegrationPerformance(benchmark: PerformanceBenchmark) { let total = 0; let passed = 0; // ์‹ค์ œ ์‚ฌ์šฉ ์‹œ๋‚˜๋ฆฌ์˜ค ์‹œ๋ฎฌ๋ ˆ์ด์…˜ total++; try { const scenarios = [ { timing: "after", count: 10, actionType: "save" }, { timing: "before", count: 5, actionType: "delete" }, { timing: "replace", count: 3, actionType: "submit" }, ]; await benchmark.measure("Integration Load Test", async () => { for (const scenario of scenarios) { const promises = Array.from({ length: scenario.count }, async (_, i) => { const config: ButtonTypeConfig = { actionType: scenario.actionType as ButtonActionType, enableDataflowControl: true, dataflowTiming: scenario.timing as any, dataflowConfig: { controlMode: "simple", selectedDiagramId: 1, selectedRelationshipId: `rel-${i}`, }, }; return await optimizedButtonDataflowService.executeButtonWithDataflow( `integration-${scenario.timing}-${i}`, scenario.actionType as ButtonActionType, config, { testData: `value-${i}` }, "DEFAULT", ); }); await Promise.all(promises); } }); const totalTime = benchmark.getResults().details.slice(-1)[0].time; const totalRequests = scenarios.reduce((sum, s) => sum + s.count, 0); const averageTime = totalTime / totalRequests; // ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ์—์„œ๋Š” ํ‰๊ท  300ms ์ด๋‚ด๋ฉด ํ†ต๊ณผ if (averageTime < 300) { passed++; console.log(` โœ… Integration load test: PASSED (avg ${averageTime.toFixed(2)}ms < 300ms)`); } else { console.log(` โŒ Integration load test: FAILED (avg ${averageTime.toFixed(2)}ms >= 300ms)`); } } catch (error) { console.log(" โŒ Integration test: ERROR -", error.message); } return { total, passed }; } // ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์ง์ ‘ ์‹คํ–‰๋  ๋•Œ๋งŒ ํ…Œ์ŠคํŠธ ์‹คํ–‰ if (require.main === module) { runPerformanceTests(); } export { runPerformanceTests };