aiwf 0.3.3 → 0.3.5

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.
Files changed (208) hide show
  1. package/.aiwf/config/language.json +1 -1
  2. package/.aiwf/metrics/metrics.json +18 -0
  3. package/.aiwf/personas/current.json +5 -0
  4. package/.aiwf/sessions/session_1752877859489_1t7n2fhdb.json +7 -0
  5. package/.aiwf/sessions/session_1752877859515_cwdv211ry.json +7 -0
  6. package/.aiwf/sessions/session_1752877860664_huziadmbo.json +7 -0
  7. package/.aiwf/sessions/session_1752877860666_kyr5u1np6.json +7 -0
  8. package/.aiwf/sessions/session_1752877861848_9yinepw3t.json +7 -0
  9. package/.aiwf/sessions/session_1752877861874_y32sputuc.json +7 -0
  10. package/.aiwf/sessions/session_1752877862685_ivxbl5rhz.json +7 -0
  11. package/.aiwf/sessions/session_1752877862820_wnq4glvxi.json +7 -0
  12. package/.aiwf/sessions/session_1752877864632_3ebnpaelb.json +7 -0
  13. package/.aiwf/sessions/session_1752877864879_b9zgjcb7d.json +7 -0
  14. package/.aiwf/sessions/session_1752877865533_x14m1vodi.json +7 -0
  15. package/.aiwf/sessions/session_1752877865562_gkoh19n7d.json +7 -0
  16. package/.aiwf/sessions/session_1752877866333_wp8qpdcoh.json +7 -0
  17. package/.aiwf/sessions/session_1752877866509_dawntfpab.json +7 -0
  18. package/.aiwf/sessions/session_1752877866890_plc5vfucl.json +7 -0
  19. package/.aiwf/sessions/session_1752877866895_ga8hmq3oi.json +7 -0
  20. package/.aiwf/sessions/session_1752877867761_ayjp4v5f5.json +7 -0
  21. package/.aiwf/sessions/session_1752877867892_lfz6jy252.json +7 -0
  22. package/.aiwf/sessions/session_1752877868139_7bqd9ubli.json +7 -0
  23. package/.aiwf/sessions/session_1752877868162_zq3ofjbsh.json +7 -0
  24. package/.aiwf/sessions/session_1752877869054_7rlgm9jt6.json +7 -0
  25. package/.aiwf/sessions/session_1752877869477_ax3nhbxzr.json +7 -0
  26. package/.aiwf/sessions/session_1752877870095_6hgp4vl1e.json +7 -0
  27. package/.aiwf/sessions/session_1752877870381_qfcrdg4li.json +7 -0
  28. package/.aiwf/sessions/session_1752877871895_pgd5vllhz.json +7 -0
  29. package/.aiwf/sessions/session_1752877872199_jbhv2g2da.json +7 -0
  30. package/.aiwf/sessions/session_1752877872680_x04wpffu6.json +7 -0
  31. package/.aiwf/sessions/session_1752877872895_3y4vbeauo.json +7 -0
  32. package/.aiwf/sessions/session_1752877873178_3maolzc6b.json +7 -0
  33. package/.aiwf/sessions/session_1752877873180_ytm83j07f.json +7 -0
  34. package/.aiwf/sessions/session_1752877874217_dp56c9t42.json +7 -0
  35. package/.aiwf/sessions/session_1752877874415_epwp1g1og.json +7 -0
  36. package/.aiwf/sessions/session_1752877874454_heicyuryb.json +7 -0
  37. package/.aiwf/sessions/session_1752877874869_1c2illeu5.json +7 -0
  38. package/.aiwf/sessions/session_1752877874879_k1vo4vxyx.json +7 -0
  39. package/.aiwf/sessions/session_1752877874913_bwgyiz9bp.json +7 -0
  40. package/.aiwf/sessions/session_1752877876218_44807kztz.json +7 -0
  41. package/.aiwf/sessions/session_1752877876515_qkhjv5kpw.json +7 -0
  42. package/.aiwf/sessions/session_1752877876543_p605aud1m.json +7 -0
  43. package/.aiwf/sessions/session_1752877876819_l65w7ifck.json +7 -0
  44. package/.aiwf/sessions/session_1752877876867_xrhzss4we.json +7 -0
  45. package/.aiwf/sessions/session_1752877876872_u7aso54qb.json +7 -0
  46. package/.aiwf/sessions/session_1752877877705_telnfg4o6.json +7 -0
  47. package/.aiwf/sessions/session_1752877878285_6s45im2ib.json +7 -0
  48. package/.aiwf/sessions/session_1752877878506_v0dvguqhz.json +7 -0
  49. package/.aiwf/sessions/session_1752877878581_3ri3yc66a.json +7 -0
  50. package/.aiwf/sessions/session_1752877878756_a82x9s2pd.json +7 -0
  51. package/.aiwf/sessions/session_1752877878886_20pjsfour.json +7 -0
  52. package/.aiwf/sessions/session_1752877880266_fm1ydjw0g.json +7 -0
  53. package/.aiwf/sessions/session_1752877881149_xbat4z03d.json +7 -0
  54. package/.aiwf/sessions/session_1752877881551_qy5sic9qs.json +7 -0
  55. package/.aiwf/sessions/session_1752877881554_24ugd96wu.json +7 -0
  56. package/.aiwf/sessions/session_1752877881797_e9mm25uh0.json +7 -0
  57. package/.aiwf/sessions/session_1752877881822_bxvjj8c9g.json +7 -0
  58. package/.aiwf/sessions/session_1752877881890_jv7l6jve3.json +7 -0
  59. package/.aiwf/sessions/session_1752877881991_f0qugdtcq.json +7 -0
  60. package/.aiwf/sessions/session_1752877883387_0bvw9rvnx.json +7 -0
  61. package/.aiwf/sessions/session_1752877883820_q6g2md2up.json +7 -0
  62. package/.aiwf/sessions/session_1752877884393_sqr7trvjl.json +7 -0
  63. package/.aiwf/sessions/session_1752877884500_whtypmzlf.json +7 -0
  64. package/.aiwf/sessions/session_1752877884645_y9mvvxli1.json +7 -0
  65. package/.aiwf/sessions/session_1752877884733_hk46eenel.json +7 -0
  66. package/.aiwf/sessions/session_1752877884738_c62j6214r.json +7 -0
  67. package/.aiwf/sessions/session_1752877884771_temhxehoz.json +7 -0
  68. package/.aiwf/sessions/session_1752877885643_6v5fcktc7.json +7 -0
  69. package/.aiwf/sessions/session_1752877886426_ee8di2fqh.json +7 -0
  70. package/.aiwf/sessions/session_1752877886549_3xj6tdoew.json +7 -0
  71. package/.aiwf/sessions/session_1752877886604_aj80yyfco.json +7 -0
  72. package/.aiwf/sessions/session_1752877886702_2b8841vyg.json +7 -0
  73. package/.aiwf/sessions/session_1752877886712_ntqc8b8bz.json +7 -0
  74. package/.aiwf/sessions/session_1752877886968_946m8tbw6.json +7 -0
  75. package/.aiwf/sessions/session_1752877887008_wg6zfg8dn.json +7 -0
  76. package/.aiwf/sessions/session_1752877888479_ciiu2qiqs.json +7 -0
  77. package/.aiwf/sessions/session_1752877889330_ravg8l66k.json +7 -0
  78. package/.aiwf/sessions/session_1752877889542_7zepkhjkc.json +7 -0
  79. package/.aiwf/sessions/session_1752877889638_ev4htqiu0.json +7 -0
  80. package/.aiwf/sessions/session_1752877889956_szphy2qma.json +7 -0
  81. package/.aiwf/sessions/session_1752877890220_o6aaa57tt.json +7 -0
  82. package/.aiwf/sessions/session_1752877890527_v2loy5vu1.json +7 -0
  83. package/.aiwf/sessions/session_1752877890572_cwq8fiuma.json +7 -0
  84. package/.aiwf/sessions/session_1752877892200_sgcwq1alt.json +7 -0
  85. package/.aiwf/sessions/session_1752877892268_v8kexpbc5.json +7 -0
  86. package/.aiwf/sessions/session_1752877892384_1gbbgrp4y.json +7 -0
  87. package/.aiwf/sessions/session_1752877893568_9p2n5p31a.json +7 -0
  88. package/.aiwf/sessions/session_1752877893727_un34a5tvp.json +7 -0
  89. package/.aiwf/sessions/session_1752877893778_01j79sw9i.json +7 -0
  90. package/.aiwf/sessions/session_1752877894053_jrhba6wko.json +7 -0
  91. package/.aiwf/sessions/session_1752877894082_x3bghawox.json +7 -0
  92. package/.aiwf/sessions/session_1752877894500_iytiav2ku.json +7 -0
  93. package/.aiwf/sessions/session_1752877894667_ucn0gc2ge.json +7 -0
  94. package/.aiwf/test-results-s02.json +4 -4
  95. package/.claude/commands/aiwf/aiwf_changelog.md +82 -0
  96. package/.claude/commands/aiwf/aiwf_code_review.md +88 -0
  97. package/.claude/commands/aiwf/aiwf_commit.md +160 -0
  98. package/.claude/commands/aiwf/aiwf_compress_aggressive_persona.md +53 -0
  99. package/.claude/commands/aiwf/aiwf_compress_context.md +116 -0
  100. package/.claude/commands/aiwf/aiwf_compress_minimal_persona.md +48 -0
  101. package/.claude/commands/aiwf/aiwf_compress_stats.md +79 -0
  102. package/.claude/commands/aiwf/aiwf_compress_with_persona.md +51 -0
  103. package/.claude/commands/aiwf/aiwf_create_feature_ledger.md +54 -0
  104. package/.claude/commands/aiwf/aiwf_create_general_task.md +147 -0
  105. package/.claude/commands/aiwf/aiwf_create_milestone_plan.md +194 -0
  106. package/.claude/commands/aiwf/aiwf_create_prd.md +280 -0
  107. package/.claude/commands/aiwf/aiwf_create_sprint_tasks.md +189 -0
  108. package/.claude/commands/aiwf/aiwf_create_sprints_from_milestone.md +121 -0
  109. package/.claude/commands/aiwf/aiwf_default_mode.md +44 -0
  110. package/.claude/commands/aiwf/aiwf_discuss_review.md +29 -0
  111. package/.claude/commands/aiwf/aiwf_do_task.md +109 -0
  112. package/.claude/commands/aiwf/aiwf_docs.md +225 -0
  113. package/.claude/commands/aiwf/aiwf_evaluate.md +92 -0
  114. package/.claude/commands/aiwf/aiwf_get_feature_details.md +78 -0
  115. package/.claude/commands/aiwf/aiwf_infinite.md +202 -0
  116. package/.claude/commands/aiwf/aiwf_initialize.md +134 -0
  117. package/.claude/commands/aiwf/aiwf_issue_create.md +65 -0
  118. package/.claude/commands/aiwf/aiwf_link_feature_to_milestone.md +57 -0
  119. package/.claude/commands/aiwf/aiwf_list_features.md +54 -0
  120. package/.claude/commands/aiwf/aiwf_mermaid.md +272 -0
  121. package/.claude/commands/aiwf/aiwf_persona_architect.md +48 -0
  122. package/.claude/commands/aiwf/aiwf_persona_backend.md +37 -0
  123. package/.claude/commands/aiwf/aiwf_persona_data_analyst.md +37 -0
  124. package/.claude/commands/aiwf/aiwf_persona_frontend.md +37 -0
  125. package/.claude/commands/aiwf/aiwf_persona_security.md +48 -0
  126. package/.claude/commands/aiwf/aiwf_persona_status.md +50 -0
  127. package/.claude/commands/aiwf/aiwf_pr_create.md +76 -0
  128. package/.claude/commands/aiwf/aiwf_prime.md +9 -0
  129. package/.claude/commands/aiwf/aiwf_project_review.md +261 -0
  130. package/.claude/commands/aiwf/aiwf_switch_language.md +160 -0
  131. package/.claude/commands/aiwf/aiwf_test.md +124 -0
  132. package/.claude/commands/aiwf/aiwf_testing_review.md +198 -0
  133. package/.claude/commands/aiwf/aiwf_tm-run-all-subtask.md +210 -0
  134. package/.claude/commands/aiwf/aiwf_ultrathink_code_advanced.md +460 -0
  135. package/.claude/commands/aiwf/aiwf_ultrathink_code_basic.md +165 -0
  136. package/.claude/commands/aiwf/aiwf_ultrathink_general.md +305 -0
  137. package/.claude/commands/aiwf/aiwf_update_docs_kr.md +25 -0
  138. package/.claude/commands/aiwf/aiwf_update_feature_status.md +53 -0
  139. package/.claude/commands/aiwf/aiwf_yolo.md +300 -0
  140. package/.eslintrc.json +26 -0
  141. package/.prettierrc +10 -0
  142. package/CHANGELOG.md +54 -5
  143. package/CLAUDE.md +154 -54
  144. package/README.ko.md +25 -0
  145. package/README.md +25 -0
  146. package/TEST_RECOVERY_PLAN.md +68 -0
  147. package/claude-code/aiwf/ko/commands/ai-persona.js +1 -1
  148. package/docs/CLEANUP_RECOMMENDATIONS.md +86 -0
  149. package/docs/CLI_USAGE_GUIDE.md +479 -0
  150. package/package.json +9 -9
  151. package/scripts/update-feature-status.js +70 -0
  152. package/{lib → src/cli}/cache-cli.js +4 -4
  153. package/src/cli/index.js +206 -0
  154. package/{language-cli.js → src/cli/language-cli.js} +11 -4
  155. package/src/commands/cache-templates.js +209 -0
  156. package/src/commands/create-offline.js +86 -0
  157. package/src/commands/create-project.js +151 -0
  158. package/src/commands/persona.js +288 -0
  159. package/src/lib/backup-manager.js +270 -0
  160. package/src/lib/file-downloader.js +208 -0
  161. package/src/lib/installer.js +676 -0
  162. package/src/lib/rollback-manager.js +418 -0
  163. package/src/lib/validator.js +561 -0
  164. package/src/utils/git-integration.js +173 -0
  165. package/{language-utils.js → src/utils/language-utils.js} +2 -1
  166. package/src/utils/messages.js +136 -0
  167. package/src/utils/paths.js +130 -0
  168. package/tests/application.test.js +2 -2
  169. package/tests/installer.test.js +2 -2
  170. package/tests/integration/ai-persona-system.test.js +4 -4
  171. package/tests/integration/test-persona-system/docs/README.md +1 -0
  172. package/tests/integration/test-persona-system/package.json +1 -0
  173. package/tests/integration/test-persona-system/src/auth.js +1 -0
  174. package/tests/integration/test-persona-system/src/index.js +1 -0
  175. package/tests/integration/test-persona-system/test/test.js +1 -0
  176. package/tests/language-utils.test.js +1 -1
  177. package/tests/temp_invalid_json/invalid.json +1 -0
  178. package/tests/unit/ai-persona-manager.test.js +7 -7
  179. package/.aiwf/05_DOCUMENTATION/performance-report.md +0 -33
  180. package/.aiwf/test-cache/86c7f9e80251abb8bedd58953ab0d1779e46b4f43ea33b63bffdb778106373b0.json +0 -6
  181. package/.aiwf/test-cache/9e252a90b80fce0585d7ed0646e7a9b4343e109f661f42a3ae678ba1ec5e22c7.json +0 -7
  182. package/.aiwf/token-data/alerts.json +0 -293
  183. package/.aiwf/token-data/monitor-config.json +0 -12
  184. package/CHANGELOG_M02.md +0 -52
  185. package/claude-code/aiwf/ko/templates/web-app/template/package-lock.json +0 -10022
  186. package/claude-code/aiwf/ko/token-data/alerts.json +0 -293
  187. package/claude-code/aiwf/ko/token-data/monitor-config.json +0 -12
  188. package/docs/m02-dod-checklist.md +0 -125
  189. package/docs/m02-gap-analysis.md +0 -214
  190. package/docs/m02-milestone-completion-report.md +0 -186
  191. package/docs/m02-milestone-executive-summary.md +0 -68
  192. package/index.js +0 -1951
  193. package/lib/cache-integration.js +0 -401
  194. package/lib/file-batch-processor.js +0 -401
  195. package/lib/performance-benchmark.js +0 -621
  196. package/lib/template-manager.js +0 -310
  197. package/scripts/validate-commands.cjs +0 -338
  198. /package/{lib → src/lib}/ai-persona-manager.js +0 -0
  199. /package/{lib → src/lib}/cache-system.js +0 -0
  200. /package/{lib → src/lib}/context-engine.js +0 -0
  201. /package/{lib → src/lib}/memory-profiler.js +0 -0
  202. /package/{lib → src/lib}/metrics-collector.js +0 -0
  203. /package/{lib → src/lib}/offline-detector.js +0 -0
  204. /package/{lib → src/lib}/task-analyzer.js +0 -0
  205. /package/{lib → src/lib}/template-cache-system.js +0 -0
  206. /package/{lib → src/lib}/template-downloader.js +0 -0
  207. /package/{lib → src/lib}/template-version-manager.js +0 -0
  208. /package/{lib → src/lib}/token-optimizer.js +0 -0
@@ -1,621 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { performance } from 'perf_hooks';
4
- import { Worker } from 'worker_threads';
5
- import fs from 'fs/promises';
6
- import path from 'path';
7
- import { MemoryProfiler } from './memory-profiler.js';
8
- import { GitHubAPICache } from './cache-system.js';
9
-
10
- /**
11
- * 성능 벤치마크 도구
12
- * - 다양한 성능 메트릭 측정
13
- * - 부하 테스트 실행
14
- * - 성능 회귀 감지
15
- */
16
- class PerformanceBenchmark {
17
- constructor(options = {}) {
18
- this.options = {
19
- iterations: options.iterations || 100,
20
- warmupIterations: options.warmupIterations || 10,
21
- memoryProfiling: options.memoryProfiling || false,
22
- concurrency: options.concurrency || 1,
23
- timeout: options.timeout || 30000,
24
- ...options
25
- };
26
-
27
- this.results = [];
28
- this.memoryProfiler = new MemoryProfiler();
29
- this.benchmarkHistory = new Map();
30
- }
31
-
32
- // 벤치마크 실행
33
- async run(testSuite) {
34
- console.log(`Starting benchmark suite: ${testSuite.name}`);
35
-
36
- if (this.options.memoryProfiling) {
37
- this.memoryProfiler.startProfiling();
38
- }
39
-
40
- const suiteResults = {
41
- name: testSuite.name,
42
- timestamp: Date.now(),
43
- tests: [],
44
- summary: {}
45
- };
46
-
47
- // 각 테스트 실행
48
- for (const test of testSuite.tests) {
49
- console.log(`Running test: ${test.name}`);
50
-
51
- try {
52
- const testResult = await this.runTest(test);
53
- suiteResults.tests.push(testResult);
54
- } catch (error) {
55
- suiteResults.tests.push({
56
- name: test.name,
57
- error: error.message,
58
- status: 'failed'
59
- });
60
- }
61
- }
62
-
63
- // 요약 생성
64
- suiteResults.summary = this.generateSummary(suiteResults.tests);
65
-
66
- if (this.options.memoryProfiling) {
67
- this.memoryProfiler.stopProfiling();
68
- suiteResults.memoryReport = this.memoryProfiler.generateReport();
69
- }
70
-
71
- this.results.push(suiteResults);
72
- this.benchmarkHistory.set(testSuite.name, suiteResults);
73
-
74
- return suiteResults;
75
- }
76
-
77
- // 개별 테스트 실행
78
- async runTest(test) {
79
- const testResult = {
80
- name: test.name,
81
- status: 'running',
82
- iterations: this.options.iterations,
83
- warmupIterations: this.options.warmupIterations,
84
- metrics: {
85
- duration: [],
86
- memory: [],
87
- throughput: [],
88
- errors: 0
89
- },
90
- statistics: {}
91
- };
92
-
93
- // 워밍업 실행
94
- await this.warmup(test);
95
-
96
- // 메인 벤치마크 실행
97
- const startTime = performance.now();
98
-
99
- for (let i = 0; i < this.options.iterations; i++) {
100
- const iterationStartTime = performance.now();
101
- const memoryBefore = process.memoryUsage();
102
-
103
- try {
104
- const result = await Promise.race([
105
- this.executeTest(test),
106
- this.createTimeout(this.options.timeout)
107
- ]);
108
-
109
- const iterationEndTime = performance.now();
110
- const memoryAfter = process.memoryUsage();
111
-
112
- // 메트릭 수집
113
- testResult.metrics.duration.push(iterationEndTime - iterationStartTime);
114
- testResult.metrics.memory.push(memoryAfter.heapUsed - memoryBefore.heapUsed);
115
-
116
- if (result && result.throughput) {
117
- testResult.metrics.throughput.push(result.throughput);
118
- }
119
-
120
- } catch (error) {
121
- testResult.metrics.errors++;
122
- console.warn(`Test iteration ${i} failed:`, error.message);
123
- }
124
- }
125
-
126
- const endTime = performance.now();
127
- testResult.totalTime = endTime - startTime;
128
- testResult.status = 'completed';
129
-
130
- // 통계 계산
131
- testResult.statistics = this.calculateStatistics(testResult.metrics);
132
-
133
- return testResult;
134
- }
135
-
136
- // 워밍업 실행
137
- async warmup(test) {
138
- console.log(`Warming up: ${test.name}`);
139
-
140
- for (let i = 0; i < this.options.warmupIterations; i++) {
141
- try {
142
- await this.executeTest(test);
143
- } catch (error) {
144
- // 워밍업 실패는 무시
145
- }
146
- }
147
- }
148
-
149
- // 테스트 실행
150
- async executeTest(test) {
151
- if (this.options.concurrency > 1) {
152
- return await this.executeConcurrentTest(test);
153
- }
154
-
155
- return await test.fn();
156
- }
157
-
158
- // 동시성 테스트 실행
159
- async executeConcurrentTest(test) {
160
- const promises = [];
161
-
162
- for (let i = 0; i < this.options.concurrency; i++) {
163
- promises.push(test.fn());
164
- }
165
-
166
- const results = await Promise.allSettled(promises);
167
- const successfulResults = results.filter(r => r.status === 'fulfilled');
168
-
169
- return {
170
- totalRequests: this.options.concurrency,
171
- successfulRequests: successfulResults.length,
172
- failedRequests: results.length - successfulResults.length,
173
- throughput: successfulResults.length / (Date.now() / 1000)
174
- };
175
- }
176
-
177
- // 타임아웃 생성
178
- createTimeout(timeout) {
179
- return new Promise((_, reject) => {
180
- setTimeout(() => reject(new Error('Test timeout')), timeout);
181
- });
182
- }
183
-
184
- // 통계 계산
185
- calculateStatistics(metrics) {
186
- const stats = {};
187
-
188
- ['duration', 'memory', 'throughput'].forEach(metric => {
189
- if (metrics[metric].length > 0) {
190
- const sorted = metrics[metric].slice().sort((a, b) => a - b);
191
-
192
- stats[metric] = {
193
- min: sorted[0],
194
- max: sorted[sorted.length - 1],
195
- avg: sorted.reduce((a, b) => a + b, 0) / sorted.length,
196
- median: this.calculateMedian(sorted),
197
- p95: this.calculatePercentile(sorted, 95),
198
- p99: this.calculatePercentile(sorted, 99),
199
- stddev: this.calculateStandardDeviation(sorted)
200
- };
201
- }
202
- });
203
-
204
- stats.errorRate = metrics.errors / (metrics.duration.length + metrics.errors);
205
-
206
- return stats;
207
- }
208
-
209
- // 중간값 계산
210
- calculateMedian(sorted) {
211
- const mid = Math.floor(sorted.length / 2);
212
- return sorted.length % 2 === 0 ?
213
- (sorted[mid - 1] + sorted[mid]) / 2 :
214
- sorted[mid];
215
- }
216
-
217
- // 백분위수 계산
218
- calculatePercentile(sorted, percentile) {
219
- const index = Math.ceil((percentile / 100) * sorted.length) - 1;
220
- return sorted[Math.max(0, Math.min(index, sorted.length - 1))];
221
- }
222
-
223
- // 표준편차 계산
224
- calculateStandardDeviation(values) {
225
- const avg = values.reduce((a, b) => a + b, 0) / values.length;
226
- const variance = values.reduce((a, b) => a + Math.pow(b - avg, 2), 0) / values.length;
227
- return Math.sqrt(variance);
228
- }
229
-
230
- // 요약 생성
231
- generateSummary(tests) {
232
- const summary = {
233
- totalTests: tests.length,
234
- passedTests: tests.filter(t => t.status === 'completed').length,
235
- failedTests: tests.filter(t => t.status === 'failed').length,
236
- averageDuration: 0,
237
- totalDuration: 0,
238
- memoryUsage: {
239
- min: Infinity,
240
- max: 0,
241
- avg: 0
242
- }
243
- };
244
-
245
- const completedTests = tests.filter(t => t.status === 'completed');
246
-
247
- if (completedTests.length > 0) {
248
- summary.totalDuration = completedTests.reduce((sum, test) => sum + test.totalTime, 0);
249
- summary.averageDuration = summary.totalDuration / completedTests.length;
250
-
251
- // 메모리 사용량 요약
252
- const memoryStats = completedTests.map(t => t.statistics.memory).filter(Boolean);
253
- if (memoryStats.length > 0) {
254
- summary.memoryUsage.min = Math.min(...memoryStats.map(s => s.min));
255
- summary.memoryUsage.max = Math.max(...memoryStats.map(s => s.max));
256
- summary.memoryUsage.avg = memoryStats.reduce((sum, s) => sum + s.avg, 0) / memoryStats.length;
257
- }
258
- }
259
-
260
- return summary;
261
- }
262
-
263
- // 부하 테스트 실행
264
- async runLoadTest(testConfig) {
265
- const {
266
- name,
267
- target,
268
- duration = 60000, // 1분
269
- concurrency = 10,
270
- rampUp = 5000 // 5초
271
- } = testConfig;
272
-
273
- console.log(`Starting load test: ${name}`);
274
- console.log(`Target: ${target}, Duration: ${duration}ms, Concurrency: ${concurrency}`);
275
-
276
- const loadTestResult = {
277
- name,
278
- config: testConfig,
279
- timestamp: Date.now(),
280
- metrics: {
281
- totalRequests: 0,
282
- successfulRequests: 0,
283
- failedRequests: 0,
284
- averageResponseTime: 0,
285
- responseTimePercentiles: {},
286
- throughput: 0,
287
- errorRate: 0
288
- },
289
- timeline: []
290
- };
291
-
292
- const startTime = Date.now();
293
- const workers = [];
294
- const results = [];
295
-
296
- // 점진적 부하 증가
297
- for (let i = 0; i < concurrency; i++) {
298
- setTimeout(() => {
299
- const worker = this.createLoadTestWorker(target, duration, results);
300
- workers.push(worker);
301
- }, (i / concurrency) * rampUp);
302
- }
303
-
304
- // 테스트 완료 대기
305
- await new Promise(resolve => setTimeout(resolve, duration + rampUp));
306
-
307
- // 워커 정리
308
- workers.forEach(worker => worker.terminate());
309
-
310
- // 결과 분석
311
- loadTestResult.metrics = this.analyzeLoadTestResults(results);
312
- loadTestResult.duration = Date.now() - startTime;
313
-
314
- return loadTestResult;
315
- }
316
-
317
- // 부하 테스트 워커 생성
318
- createLoadTestWorker(target, duration, results) {
319
- const worker = new Worker(`
320
- const { parentPort } = require('worker_threads');
321
- const startTime = Date.now();
322
-
323
- async function runRequests() {
324
- while (Date.now() - startTime < ${duration}) {
325
- const requestStartTime = Date.now();
326
-
327
- try {
328
- // 실제 요청 실행 (예: HTTP 요청)
329
- await new Promise(resolve => setTimeout(resolve, Math.random() * 100));
330
-
331
- const responseTime = Date.now() - requestStartTime;
332
- parentPort.postMessage({
333
- type: 'success',
334
- responseTime,
335
- timestamp: Date.now()
336
- });
337
- } catch (error) {
338
- parentPort.postMessage({
339
- type: 'error',
340
- error: error.message,
341
- timestamp: Date.now()
342
- });
343
- }
344
- }
345
- }
346
-
347
- runRequests();
348
- `, { eval: true });
349
-
350
- worker.on('message', (message) => {
351
- results.push(message);
352
- });
353
-
354
- return worker;
355
- }
356
-
357
- // 부하 테스트 결과 분석
358
- analyzeLoadTestResults(results) {
359
- const successful = results.filter(r => r.type === 'success');
360
- const failed = results.filter(r => r.type === 'error');
361
-
362
- const responseTimes = successful.map(r => r.responseTime);
363
- const sorted = responseTimes.slice().sort((a, b) => a - b);
364
-
365
- return {
366
- totalRequests: results.length,
367
- successfulRequests: successful.length,
368
- failedRequests: failed.length,
369
- averageResponseTime: responseTimes.reduce((a, b) => a + b, 0) / responseTimes.length,
370
- responseTimePercentiles: {
371
- p50: this.calculatePercentile(sorted, 50),
372
- p95: this.calculatePercentile(sorted, 95),
373
- p99: this.calculatePercentile(sorted, 99)
374
- },
375
- throughput: successful.length / (results.length > 0 ?
376
- (Math.max(...results.map(r => r.timestamp)) - Math.min(...results.map(r => r.timestamp))) / 1000 : 1),
377
- errorRate: failed.length / results.length
378
- };
379
- }
380
-
381
- // 성능 회귀 감지
382
- detectPerformanceRegression(currentResult, baselineResult) {
383
- const regression = {
384
- detected: false,
385
- issues: [],
386
- improvements: []
387
- };
388
-
389
- const thresholds = {
390
- duration: 0.2, // 20% 증가
391
- memory: 0.3, // 30% 증가
392
- throughput: -0.1 // 10% 감소
393
- };
394
-
395
- // 지속 시간 회귀 검사
396
- const durationChange = (currentResult.statistics.duration.avg - baselineResult.statistics.duration.avg) / baselineResult.statistics.duration.avg;
397
-
398
- if (durationChange > thresholds.duration) {
399
- regression.detected = true;
400
- regression.issues.push({
401
- metric: 'duration',
402
- change: durationChange,
403
- threshold: thresholds.duration
404
- });
405
- } else if (durationChange < -thresholds.duration) {
406
- regression.improvements.push({
407
- metric: 'duration',
408
- change: durationChange
409
- });
410
- }
411
-
412
- // 메모리 회귀 검사
413
- if (currentResult.statistics.memory && baselineResult.statistics.memory) {
414
- const memoryChange = (currentResult.statistics.memory.avg - baselineResult.statistics.memory.avg) / baselineResult.statistics.memory.avg;
415
-
416
- if (memoryChange > thresholds.memory) {
417
- regression.detected = true;
418
- regression.issues.push({
419
- metric: 'memory',
420
- change: memoryChange,
421
- threshold: thresholds.memory
422
- });
423
- }
424
- }
425
-
426
- return regression;
427
- }
428
-
429
- // 보고서 생성
430
- generateReport() {
431
- const report = {
432
- timestamp: Date.now(),
433
- summary: {
434
- totalSuites: this.results.length,
435
- totalTests: this.results.reduce((sum, suite) => sum + suite.tests.length, 0),
436
- overallStatus: this.results.every(suite => suite.tests.every(test => test.status === 'completed')) ? 'passed' : 'failed'
437
- },
438
- suites: this.results,
439
- configuration: this.options,
440
- systemInfo: {
441
- nodeVersion: process.version,
442
- platform: process.platform,
443
- arch: process.arch,
444
- memory: process.memoryUsage()
445
- }
446
- };
447
-
448
- return report;
449
- }
450
-
451
- // 보고서 저장
452
- async saveReport(filePath) {
453
- const report = this.generateReport();
454
- await fs.writeFile(filePath, JSON.stringify(report, null, 2));
455
- return filePath;
456
- }
457
-
458
- // HTML 보고서 생성
459
- async generateHTMLReport(filePath) {
460
- const report = this.generateReport();
461
- const html = this.generateHTMLTemplate(report);
462
- await fs.writeFile(filePath, html);
463
- return filePath;
464
- }
465
-
466
- // HTML 템플릿 생성
467
- generateHTMLTemplate(report) {
468
- return `
469
- <!DOCTYPE html>
470
- <html>
471
- <head>
472
- <title>Performance Benchmark Report</title>
473
- <style>
474
- body { font-family: Arial, sans-serif; margin: 20px; }
475
- .summary { background: #f5f5f5; padding: 15px; border-radius: 5px; }
476
- .test-suite { margin: 20px 0; border: 1px solid #ddd; padding: 15px; border-radius: 5px; }
477
- .test { margin: 10px 0; padding: 10px; background: #fafafa; border-radius: 3px; }
478
- .passed { border-left: 4px solid #4CAF50; }
479
- .failed { border-left: 4px solid #f44336; }
480
- .metric { display: inline-block; margin: 5px 10px; }
481
- .chart { width: 100%; height: 200px; margin: 10px 0; }
482
- table { width: 100%; border-collapse: collapse; }
483
- th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
484
- th { background-color: #f2f2f2; }
485
- </style>
486
- </head>
487
- <body>
488
- <h1>Performance Benchmark Report</h1>
489
-
490
- <div class="summary">
491
- <h2>Summary</h2>
492
- <p>Generated: ${new Date(report.timestamp).toLocaleString()}</p>
493
- <p>Total Suites: ${report.summary.totalSuites}</p>
494
- <p>Total Tests: ${report.summary.totalTests}</p>
495
- <p>Overall Status: ${report.summary.overallStatus}</p>
496
- </div>
497
-
498
- ${report.suites.map(suite => `
499
- <div class="test-suite">
500
- <h3>${suite.name}</h3>
501
- <p>Tests: ${suite.tests.length}</p>
502
- <p>Duration: ${suite.summary.totalDuration.toFixed(2)}ms</p>
503
-
504
- ${suite.tests.map(test => `
505
- <div class="test ${test.status}">
506
- <h4>${test.name}</h4>
507
- <div class="metric">Duration: ${test.statistics.duration?.avg.toFixed(2)}ms</div>
508
- <div class="metric">Memory: ${test.statistics.memory?.avg.toFixed(2)} bytes</div>
509
- <div class="metric">Error Rate: ${(test.statistics.errorRate * 100).toFixed(2)}%</div>
510
- </div>
511
- `).join('')}
512
- </div>
513
- `).join('')}
514
-
515
- <div class="summary">
516
- <h2>System Information</h2>
517
- <table>
518
- <tr><th>Property</th><th>Value</th></tr>
519
- <tr><td>Node Version</td><td>${report.systemInfo.nodeVersion}</td></tr>
520
- <tr><td>Platform</td><td>${report.systemInfo.platform}</td></tr>
521
- <tr><td>Architecture</td><td>${report.systemInfo.arch}</td></tr>
522
- <tr><td>Memory (RSS)</td><td>${(report.systemInfo.memory.rss / 1024 / 1024).toFixed(2)} MB</td></tr>
523
- </table>
524
- </div>
525
- </body>
526
- </html>
527
- `;
528
- }
529
- }
530
-
531
- // 사전 정의된 벤치마크 테스트 스위트
532
- class BenchmarkSuites {
533
- static fileOperations() {
534
- return {
535
- name: 'File Operations',
536
- tests: [
537
- {
538
- name: 'File Read Performance',
539
- fn: async () => {
540
- const filePath = '/tmp/test-file.txt';
541
- await fs.writeFile(filePath, 'test content'.repeat(1000));
542
- const startTime = performance.now();
543
- await fs.readFile(filePath, 'utf8');
544
- const endTime = performance.now();
545
- await fs.unlink(filePath);
546
- return { duration: endTime - startTime };
547
- }
548
- },
549
- {
550
- name: 'File Write Performance',
551
- fn: async () => {
552
- const filePath = '/tmp/test-write.txt';
553
- const content = 'test content'.repeat(1000);
554
- const startTime = performance.now();
555
- await fs.writeFile(filePath, content);
556
- const endTime = performance.now();
557
- await fs.unlink(filePath);
558
- return { duration: endTime - startTime };
559
- }
560
- }
561
- ]
562
- };
563
- }
564
-
565
- static memoryOperations() {
566
- return {
567
- name: 'Memory Operations',
568
- tests: [
569
- {
570
- name: 'Array Creation',
571
- fn: async () => {
572
- const startTime = performance.now();
573
- const arr = new Array(100000).fill(0);
574
- const endTime = performance.now();
575
- return { duration: endTime - startTime };
576
- }
577
- },
578
- {
579
- name: 'Object Creation',
580
- fn: async () => {
581
- const startTime = performance.now();
582
- const obj = {};
583
- for (let i = 0; i < 10000; i++) {
584
- obj[`key${i}`] = `value${i}`;
585
- }
586
- const endTime = performance.now();
587
- return { duration: endTime - startTime };
588
- }
589
- }
590
- ]
591
- };
592
- }
593
-
594
- static githubAPICache() {
595
- return {
596
- name: 'GitHub API Cache',
597
- tests: [
598
- {
599
- name: 'Cache Hit Performance',
600
- fn: async () => {
601
- const cache = new GitHubAPICache();
602
- await cache.init();
603
-
604
- const url = 'https://api.github.com/repos/test/test';
605
- const data = { test: 'data' };
606
-
607
- await cache.set(url, data);
608
-
609
- const startTime = performance.now();
610
- const result = await cache.get(url);
611
- const endTime = performance.now();
612
-
613
- return { duration: endTime - startTime, throughput: 1 };
614
- }
615
- }
616
- ]
617
- };
618
- }
619
- }
620
-
621
- export { PerformanceBenchmark, BenchmarkSuites };