From 205af1535638f5410880d3156662432429f51058 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=EC=A1=B0=EB=8C=80=ED=9D=AC?= <joedaehui@ajou.ac.kr>
Date: Fri, 6 Dec 2024 10:10:31 +0900
Subject: [PATCH] =?UTF-8?q?test:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94?=
 =?UTF-8?q?=EB=93=9C=20=EC=88=98=EC=A0=95=20(#23)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 services/performance.test.js | 273 ++++++++++++++++++++++++-----------
 1 file changed, 189 insertions(+), 84 deletions(-)

diff --git a/services/performance.test.js b/services/performance.test.js
index 9104525..e987e86 100644
--- a/services/performance.test.js
+++ b/services/performance.test.js
@@ -1,98 +1,203 @@
-const axios = require('axios');
-const { performance } = require('perf_hooks');
-
-async function runPerformanceTest() {
-    const baseURL = 'http://localhost:3000/api';
-    const iterations = 100;
-    
-    // 테스트 데이터
-    const testSchedule = {
-        title: 'Test Schedule',
-        is_fixed: true,
-        time_indices: [36, 37, 38]
-    };
-
-    console.log(`Starting performance test with ${iterations} iterations`);
-
-    // 테스트 결과 저장용 객체
-    const results = {
-        create: [],
-        update: [],
-        getAll: [],
-        getByTimeIdx: [],
-        delete: []
-    };
-
-    for (let i = 0; i < iterations; i++) {
+// services/performance.test.js
+require('dotenv').config();
+const { Op } = require('sequelize');
+const ScheduleService = require('./scheduleService');
+const sequelize = require('../config/sequelize');
+const Schedule = require('../models/schedule');
+
+class PerformanceTester {
+    constructor() {
+        this.testUserIds = [1, 2, 3, 4, 5];  // 5명의 테스트 유저만 사용
+        this.results = {
+            operations: {
+                createSchedules: [],
+                getAllSchedules: [],
+                updateSchedules: [],
+                deleteSchedules: []
+            },
+            summary: {}
+        };
+    }
+
+    async setup() {
         try {
-            console.log(`Running iteration ${i + 1}/${iterations}`);
-
-            // Create
-            const createStart = performance.now();
-            await axios.post(`${baseURL}/schedule`, testSchedule);
-            results.create.push(performance.now() - createStart);
-            
-            // Get All Schedules
-            const getAllStart = performance.now();
-            await axios.get(`${baseURL}/schedule/all`);
-            results.getAll.push(performance.now() - getAllStart);
-
-            // Get Schedule by Time Index
-            const getByTimeIdxStart = performance.now();
-            await axios.get(`${baseURL}/schedule/36`);
-            results.getByTimeIdx.push(performance.now() - getByTimeIdxStart);
-            
-            // Update
-            const updateStart = performance.now();
-            await axios.put(`${baseURL}/schedule`, {
-                originalTitle: 'Test Schedule',
-                title: 'Updated Schedule',
-                is_fixed: true,
-                time_indices: [39, 40, 41]
-            });
-            results.update.push(performance.now() - updateStart);
-            
-            // Delete
-            const deleteStart = performance.now();
-            await axios.delete(`${baseURL}/schedule`, {
-                data: { title: 'Updated Schedule' }
+            await sequelize.authenticate();
+            console.log('Database connection established successfully.');
+            await Schedule.destroy({ where: {}, force: true });
+            console.log('Test data cleaned successfully.');
+            console.log('Using existing user IDs:', this.testUserIds);
+        } catch (error) {
+            console.error('Setup failed:', error);
+            throw error;
+        }
+    }
+
+    async runLoadTest() {
+        console.log('Starting simplified test...');
+
+        const testSchedules = this.testUserIds.map((userId, i) => ({
+            userId,
+            title: `Test Schedule ${i}`,
+            is_fixed: true,
+            time_indices: [i * 2, i * 2 + 1]
+        }));
+
+        console.log('Test schedules:', testSchedules);
+
+        const transaction = await sequelize.transaction();
+        try {
+            // Create 테스트
+            console.log('\nTesting createSchedules...');
+            const createdSchedules = [];
+            for (const schedule of testSchedules) {
+                const result = await this.measureOperation('createSchedules', async () => {
+                    const created = await ScheduleService.createSchedules(schedule, transaction);
+                    console.log(`Created schedule for user ${schedule.userId}`);
+                    return created;
+                });
+                if (result) createdSchedules.push(result);
+            }
+            await transaction.commit();
+
+            // 생성된 스케줄 확인
+            const verifySchedules = await Schedule.findAll({
+                where: {
+                    user_id: { [Op.in]: this.testUserIds }
+                },
+                raw: true
             });
-            results.delete.push(performance.now() - deleteStart);
+            console.log('\nVerified schedules:', verifySchedules);
+
+            // GetAll 테스트
+            console.log('\nTesting getAllSchedules...');
+            for (const userId of this.testUserIds) {
+                await this.measureOperation('getAllSchedules', async () => {
+                    return await ScheduleService.getAllSchedules(userId);
+                });
+            }
+
+            // Update 테스트
+            console.log('\nTesting updateSchedules...');
+            for (const schedule of createdSchedules) {
+                await this.measureOperation('updateSchedules', async () => {
+                    return await ScheduleService.updateSchedules(schedule.user_id, {
+                        originalTitle: schedule.title,
+                        title: `Updated ${schedule.title}`,
+                        is_fixed: schedule.is_fixed,
+                        time_indices: schedule.time_indices
+                    });
+                });
+            }
+
+            // Delete 테스트
+            console.log('\nTesting deleteSchedules...');
+            const deleteTransaction = await sequelize.transaction();
+            try {
+                for (const schedule of createdSchedules) {
+                    await this.measureOperation('deleteSchedules', async () => {
+                        return await ScheduleService.deleteSchedules(
+                            schedule.user_id,
+                            `Updated ${schedule.title}`,
+                            deleteTransaction
+                        );
+                    });
+                }
+                await deleteTransaction.commit();
+            } catch (error) {
+                await deleteTransaction.rollback();
+                throw error;
+            }
+        } catch (error) {
+            await transaction.rollback();
+            throw error;
+        }
 
+        this.analyzePerfResults();
+    }
+
+    async measureOperation(name, operation) {
+        const start = process.hrtime.bigint();
+        try {
+            const result = await operation();
+            const end = process.hrtime.bigint();
+            const duration = Number(end - start) / 1000000;
+            this.results.operations[name].push({ success: true, duration });
+            return result;
         } catch (error) {
-            console.error(`Iteration ${i} failed:`, error.message);
+            const end = process.hrtime.bigint();
+            const duration = Number(end - start) / 1000000;
+            this.results.operations[name].push({
+                success: false,
+                duration,
+                error: error.message
+            });
+            console.error(`Error in ${name}:`, error.message);
+            return null;
         }
     }
 
-    // 결과 분석
-    const analyzeResults = (times) => {
-        const avg = times.reduce((a, b) => a + b, 0) / times.length;
-        const min = Math.min(...times);
-        const max = Math.max(...times);
-        return {
-            average: avg.toFixed(2),
-            min: min.toFixed(2),
-            max: max.toFixed(2),
-            count: times.length
-        };
-    };
+    analyzePerfResults() {
+        Object.entries(this.results.operations).forEach(([operation, results]) => {
+            const successful = results.filter(r => r.success);
+            const failed = results.filter(r => !r.success);
+            if (successful.length > 0) {
+                const durations = successful.map(r => r.duration);
+                this.results.summary[operation] = {
+                    totalRequests: results.length,
+                    successCount: successful.length,
+                    failCount: failed.length,
+                    avgDuration: durations.reduce((a, b) => a + b, 0) / successful.length,
+                    minDuration: Math.min(...durations),
+                    maxDuration: Math.max(...durations),
+                    p95: this.calculatePercentile(durations, 95),
+                    p99: this.calculatePercentile(durations, 99)
+                };
+            }
+        });
+        this.printResults();
+    }
 
-    // 성능 통계 출력
-    console.log('\nPerformance Results (ms):');
-    console.log('Create Schedule:', analyzeResults(results.create));
-    console.log('Get All Schedules:', analyzeResults(results.getAll));
-    console.log('Get Schedule by Time Index:', analyzeResults(results.getByTimeIdx));
-    console.log('Update Schedule:', analyzeResults(results.update));
-    console.log('Delete Schedule:', analyzeResults(results.delete));
+    calculatePercentile(array, percentile) {
+        const sorted = array.sort((a, b) => a - b);
+        const index = Math.ceil((percentile / 100) * sorted.length) - 1;
+        return sorted[index];
+    }
+
+    printResults() {
+        console.log('\n=== Performance Test Results ===');
+        Object.entries(this.results.summary).forEach(([operation, stats]) => {
+            console.log(`\n${operation}:`);
+            console.log(`Total Requests: ${stats.totalRequests}`);
+            console.log(`Success Rate: ${((stats.successCount / stats.totalRequests) * 100).toFixed(2)}%`);
+            console.log(`Average Duration: ${stats.avgDuration.toFixed(2)}ms`);
+            console.log(`Min Duration: ${stats.minDuration.toFixed(2)}ms`);
+            console.log(`Max Duration: ${stats.maxDuration.toFixed(2)}ms`);
+            console.log(`95th Percentile: ${stats.p95.toFixed(2)}ms`);
+            console.log(`99th Percentile: ${stats.p99.toFixed(2)}ms`);
+        });
+    }
+
+    async cleanup() {
+        try {
+            await Schedule.destroy({ where: {}, force: true });
+            console.log('Cleanup completed successfully.');
+        } catch (error) {
+            console.error('Cleanup failed:', error);
+        }
+    }
+}
 
-    // 성능 통계 API 호출
+async function runTests() {
+    const tester = new PerformanceTester();
     try {
-        const stats = await axios.get(`${baseURL}/performance/stats`);
-        console.log('\nDetailed Performance Statistics:', JSON.stringify(stats.data, null, 2));
+        await tester.setup();
+        console.log('Starting performance tests...');
+        await tester.runLoadTest();
     } catch (error) {
-        console.error('Failed to fetch performance stats:', error.message);
+        console.error('Test failed:', error);
+    } finally {
+        await sequelize.close();
     }
 }
 
-// 테스트 실행
-runPerformanceTest().catch(console.error);
\ No newline at end of file
+runTests();
\ No newline at end of file
-- 
GitLab