koa-classic-server 1.1.0 ā 2.0.0
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.
- package/BENCHMARKS.md +317 -0
- package/CHANGELOG.md +181 -0
- package/CREATE_RELEASE.sh +53 -0
- package/DEBUG_REPORT.md +593 -0
- package/DOCUMENTATION.md +1585 -0
- package/EXAMPLES_INDEX_OPTION.md +395 -0
- package/INDEX_OPTION_PRIORITY.md +527 -0
- package/LICENSE +21 -0
- package/OPTIMIZATION_HTTP_CACHING.md +687 -0
- package/PERFORMANCE_ANALYSIS.md +839 -0
- package/PERFORMANCE_COMPARISON.md +388 -0
- package/README.md +278 -103
- package/__tests__/index-option.test.js +447 -0
- package/__tests__/index.test.js +15 -11
- package/__tests__/performance.test.js +301 -0
- package/__tests__/publicWwwTest/cartella vuota con spazi nel nome/file con spazio nel nome .txt +1 -0
- package/__tests__/security.test.js +336 -0
- package/benchmark-results-baseline-v1.2.0.txt +354 -0
- package/benchmark-results-optimized-v2.0.0.txt +354 -0
- package/benchmark.js +239 -0
- package/demo-regex-index.js +140 -0
- package/index.cjs +386 -156
- package/jest.config.js +18 -0
- package/package.json +18 -5
- package/publish-to-npm.sh +65 -0
- package/scripts/setup-benchmark.js +178 -0
- package/test-regex-quick.js +158 -0
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Performance Benchmark Tests
|
|
3
|
+
*
|
|
4
|
+
* These tests measure current performance to establish a baseline.
|
|
5
|
+
* After optimizations, run these tests again to measure improvements.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* npm run test:performance
|
|
9
|
+
*
|
|
10
|
+
* To save results:
|
|
11
|
+
* npm run test:performance > benchmark-results-v1.2.0.txt
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const Koa = require('koa');
|
|
15
|
+
const supertest = require('supertest');
|
|
16
|
+
const koaClassicServer = require('../index.cjs');
|
|
17
|
+
const path = require('path');
|
|
18
|
+
const fs = require('fs');
|
|
19
|
+
|
|
20
|
+
const BENCHMARK_DIR = path.join(__dirname, '../benchmark-data');
|
|
21
|
+
|
|
22
|
+
// Check if benchmark data exists
|
|
23
|
+
if (!fs.existsSync(BENCHMARK_DIR)) {
|
|
24
|
+
console.error('\nā Benchmark data not found!');
|
|
25
|
+
console.error('Please run: node scripts/setup-benchmark.js\n');
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Helper to measure execution time
|
|
30
|
+
function measureTime(fn) {
|
|
31
|
+
const start = process.hrtime.bigint();
|
|
32
|
+
const result = fn();
|
|
33
|
+
const end = process.hrtime.bigint();
|
|
34
|
+
const durationMs = Number(end - start) / 1000000; // Convert to ms
|
|
35
|
+
return { result, durationMs };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function measureTimeAsync(fn) {
|
|
39
|
+
const start = process.hrtime.bigint();
|
|
40
|
+
const result = await fn();
|
|
41
|
+
const end = process.hrtime.bigint();
|
|
42
|
+
const durationMs = Number(end - start) / 1000000;
|
|
43
|
+
return { result, durationMs };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Helper to run multiple iterations and get statistics
|
|
47
|
+
async function benchmark(name, fn, iterations = 10) {
|
|
48
|
+
const times = [];
|
|
49
|
+
|
|
50
|
+
for (let i = 0; i < iterations; i++) {
|
|
51
|
+
const { durationMs } = await measureTimeAsync(fn);
|
|
52
|
+
times.push(durationMs);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const avg = times.reduce((a, b) => a + b, 0) / times.length;
|
|
56
|
+
const min = Math.min(...times);
|
|
57
|
+
const max = Math.max(...times);
|
|
58
|
+
const median = times.sort((a, b) => a - b)[Math.floor(times.length / 2)];
|
|
59
|
+
|
|
60
|
+
return { name, avg, min, max, median, times };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
describe('Performance Benchmarks - BASELINE (v1.2.0)', () => {
|
|
64
|
+
let app;
|
|
65
|
+
let server;
|
|
66
|
+
let request;
|
|
67
|
+
|
|
68
|
+
beforeAll(() => {
|
|
69
|
+
app = new Koa();
|
|
70
|
+
app.use(koaClassicServer(BENCHMARK_DIR));
|
|
71
|
+
server = app.listen();
|
|
72
|
+
request = supertest(server);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
afterAll(() => {
|
|
76
|
+
server.close();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
describe('File Serving Performance', () => {
|
|
80
|
+
test('Benchmark: Small file (1KB) - 100 iterations', async () => {
|
|
81
|
+
const stats = await benchmark(
|
|
82
|
+
'Small file (1KB)',
|
|
83
|
+
async () => {
|
|
84
|
+
const res = await request.get('/small-files/file-1.txt');
|
|
85
|
+
expect(res.status).toBe(200);
|
|
86
|
+
},
|
|
87
|
+
100
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
console.log('\nš Small File (1KB) Benchmark:');
|
|
91
|
+
console.log(` Average: ${stats.avg.toFixed(2)}ms`);
|
|
92
|
+
console.log(` Median: ${stats.median.toFixed(2)}ms`);
|
|
93
|
+
console.log(` Min: ${stats.min.toFixed(2)}ms`);
|
|
94
|
+
console.log(` Max: ${stats.max.toFixed(2)}ms`);
|
|
95
|
+
|
|
96
|
+
expect(stats.avg).toBeLessThan(50); // Should be fast
|
|
97
|
+
}, 30000);
|
|
98
|
+
|
|
99
|
+
test('Benchmark: Medium file (100KB) - 50 iterations', async () => {
|
|
100
|
+
const stats = await benchmark(
|
|
101
|
+
'Medium file (100KB)',
|
|
102
|
+
async () => {
|
|
103
|
+
const res = await request.get('/medium-files/file-1.txt');
|
|
104
|
+
expect(res.status).toBe(200);
|
|
105
|
+
},
|
|
106
|
+
50
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
console.log('\nš Medium File (100KB) Benchmark:');
|
|
110
|
+
console.log(` Average: ${stats.avg.toFixed(2)}ms`);
|
|
111
|
+
console.log(` Median: ${stats.median.toFixed(2)}ms`);
|
|
112
|
+
console.log(` Min: ${stats.min.toFixed(2)}ms`);
|
|
113
|
+
console.log(` Max: ${stats.max.toFixed(2)}ms`);
|
|
114
|
+
|
|
115
|
+
expect(stats.avg).toBeLessThan(100);
|
|
116
|
+
}, 30000);
|
|
117
|
+
|
|
118
|
+
test('Benchmark: Large file (1MB) - 20 iterations', async () => {
|
|
119
|
+
const stats = await benchmark(
|
|
120
|
+
'Large file (1MB)',
|
|
121
|
+
async () => {
|
|
122
|
+
const res = await request.get('/large-files/file-1.txt');
|
|
123
|
+
expect(res.status).toBe(200);
|
|
124
|
+
},
|
|
125
|
+
20
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
console.log('\nš Large File (1MB) Benchmark:');
|
|
129
|
+
console.log(` Average: ${stats.avg.toFixed(2)}ms`);
|
|
130
|
+
console.log(` Median: ${stats.median.toFixed(2)}ms`);
|
|
131
|
+
console.log(` Min: ${stats.min.toFixed(2)}ms`);
|
|
132
|
+
console.log(` Max: ${stats.max.toFixed(2)}ms`);
|
|
133
|
+
|
|
134
|
+
expect(stats.avg).toBeLessThan(500);
|
|
135
|
+
}, 30000);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
describe('Directory Listing Performance', () => {
|
|
139
|
+
test('Benchmark: Small directory (100 files) - 50 iterations', async () => {
|
|
140
|
+
const stats = await benchmark(
|
|
141
|
+
'Directory listing (100 files)',
|
|
142
|
+
async () => {
|
|
143
|
+
const res = await request.get('/small-files/');
|
|
144
|
+
expect(res.status).toBe(200);
|
|
145
|
+
expect(res.text).toContain('file-1.txt');
|
|
146
|
+
},
|
|
147
|
+
50
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
console.log('\nš Small Directory (100 files) Benchmark:');
|
|
151
|
+
console.log(` Average: ${stats.avg.toFixed(2)}ms`);
|
|
152
|
+
console.log(` Median: ${stats.median.toFixed(2)}ms`);
|
|
153
|
+
console.log(` Min: ${stats.min.toFixed(2)}ms`);
|
|
154
|
+
console.log(` Max: ${stats.max.toFixed(2)}ms`);
|
|
155
|
+
}, 30000);
|
|
156
|
+
|
|
157
|
+
test('Benchmark: Large directory (1,000 files) - 20 iterations', async () => {
|
|
158
|
+
const stats = await benchmark(
|
|
159
|
+
'Directory listing (1,000 files)',
|
|
160
|
+
async () => {
|
|
161
|
+
const res = await request.get('/large-directory/');
|
|
162
|
+
expect(res.status).toBe(200);
|
|
163
|
+
expect(res.text).toContain('item-0001.txt');
|
|
164
|
+
},
|
|
165
|
+
20
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
console.log('\nš Large Directory (1,000 files) Benchmark:');
|
|
169
|
+
console.log(` Average: ${stats.avg.toFixed(2)}ms`);
|
|
170
|
+
console.log(` Median: ${stats.median.toFixed(2)}ms`);
|
|
171
|
+
console.log(` Min: ${stats.min.toFixed(2)}ms`);
|
|
172
|
+
console.log(` Max: ${stats.max.toFixed(2)}ms`);
|
|
173
|
+
|
|
174
|
+
// This is expected to be slow with current sync implementation
|
|
175
|
+
console.log(` ā ļø WARNING: This will be MUCH faster after async optimization`);
|
|
176
|
+
}, 60000);
|
|
177
|
+
|
|
178
|
+
test('Benchmark: Very large directory (10,000 files) - 5 iterations', async () => {
|
|
179
|
+
const stats = await benchmark(
|
|
180
|
+
'Directory listing (10,000 files)',
|
|
181
|
+
async () => {
|
|
182
|
+
const res = await request.get('/very-large-directory/');
|
|
183
|
+
expect(res.status).toBe(200);
|
|
184
|
+
expect(res.text).toContain('item-00001.txt');
|
|
185
|
+
},
|
|
186
|
+
5
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
console.log('\nš Very Large Directory (10,000 files) Benchmark:');
|
|
190
|
+
console.log(` Average: ${stats.avg.toFixed(2)}ms`);
|
|
191
|
+
console.log(` Median: ${stats.median.toFixed(2)}ms`);
|
|
192
|
+
console.log(` Min: ${stats.min.toFixed(2)}ms`);
|
|
193
|
+
console.log(` Max: ${stats.max.toFixed(2)}ms`);
|
|
194
|
+
console.log(` ā ļø WARNING: Event loop BLOCKED during this operation!`);
|
|
195
|
+
console.log(` ā ļø Expected to drop to ~${(stats.avg * 0.3).toFixed(2)}ms after optimization`);
|
|
196
|
+
}, 120000);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
describe('Concurrent Request Performance', () => {
|
|
200
|
+
test('Benchmark: 10 concurrent small file requests', async () => {
|
|
201
|
+
const start = process.hrtime.bigint();
|
|
202
|
+
|
|
203
|
+
const promises = Array.from({ length: 10 }, (_, i) =>
|
|
204
|
+
request.get(`/small-files/file-${i + 1}.txt`)
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
const results = await Promise.all(promises);
|
|
208
|
+
|
|
209
|
+
const end = process.hrtime.bigint();
|
|
210
|
+
const totalTime = Number(end - start) / 1000000;
|
|
211
|
+
|
|
212
|
+
console.log('\nš 10 Concurrent Small Files:');
|
|
213
|
+
console.log(` Total time: ${totalTime.toFixed(2)}ms`);
|
|
214
|
+
console.log(` Avg per request: ${(totalTime / 10).toFixed(2)}ms`);
|
|
215
|
+
|
|
216
|
+
results.forEach(res => expect(res.status).toBe(200));
|
|
217
|
+
}, 10000);
|
|
218
|
+
|
|
219
|
+
test('Benchmark: 5 concurrent directory listings (100 files each)', async () => {
|
|
220
|
+
const start = process.hrtime.bigint();
|
|
221
|
+
|
|
222
|
+
const promises = Array.from({ length: 5 }, () =>
|
|
223
|
+
request.get('/small-files/')
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
const results = await Promise.all(promises);
|
|
227
|
+
|
|
228
|
+
const end = process.hrtime.bigint();
|
|
229
|
+
const totalTime = Number(end - start) / 1000000;
|
|
230
|
+
|
|
231
|
+
console.log('\nš 5 Concurrent Directory Listings (100 files):');
|
|
232
|
+
console.log(` Total time: ${totalTime.toFixed(2)}ms`);
|
|
233
|
+
console.log(` Avg per request: ${(totalTime / 5).toFixed(2)}ms`);
|
|
234
|
+
console.log(` ā ļø With current sync code, these run SEQUENTIALLY`);
|
|
235
|
+
console.log(` ā ļø After async optimization, will run in PARALLEL`);
|
|
236
|
+
|
|
237
|
+
results.forEach(res => expect(res.status).toBe(200));
|
|
238
|
+
}, 30000);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
describe('404 Not Found Performance', () => {
|
|
242
|
+
test('Benchmark: Non-existent file - 50 iterations', async () => {
|
|
243
|
+
const stats = await benchmark(
|
|
244
|
+
'404 Not Found',
|
|
245
|
+
async () => {
|
|
246
|
+
const res = await request.get('/does-not-exist-12345.txt');
|
|
247
|
+
expect(res.status).toBe(404);
|
|
248
|
+
},
|
|
249
|
+
50
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
console.log('\nš 404 Not Found Benchmark:');
|
|
253
|
+
console.log(` Average: ${stats.avg.toFixed(2)}ms`);
|
|
254
|
+
console.log(` Median: ${stats.median.toFixed(2)}ms`);
|
|
255
|
+
console.log(` Min: ${stats.min.toFixed(2)}ms`);
|
|
256
|
+
console.log(` Max: ${stats.max.toFixed(2)}ms`);
|
|
257
|
+
}, 10000);
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
describe('Memory Usage (Informational)', () => {
|
|
261
|
+
test('Memory usage during large directory listing', async () => {
|
|
262
|
+
// Force garbage collection if available
|
|
263
|
+
if (global.gc) {
|
|
264
|
+
global.gc();
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const memBefore = process.memoryUsage();
|
|
268
|
+
|
|
269
|
+
// Request large directory
|
|
270
|
+
const res = await request.get('/very-large-directory/');
|
|
271
|
+
expect(res.status).toBe(200);
|
|
272
|
+
|
|
273
|
+
const memAfter = process.memoryUsage();
|
|
274
|
+
|
|
275
|
+
const heapUsedDiff = (memAfter.heapUsed - memBefore.heapUsed) / 1024 / 1024;
|
|
276
|
+
const externalDiff = (memAfter.external - memBefore.external) / 1024 / 1024;
|
|
277
|
+
|
|
278
|
+
console.log('\nš Memory Usage (10,000 files directory):');
|
|
279
|
+
console.log(` Heap used increase: ${heapUsedDiff.toFixed(2)} MB`);
|
|
280
|
+
console.log(` External increase: ${externalDiff.toFixed(2)} MB`);
|
|
281
|
+
console.log(` Response size: ${(res.text.length / 1024 / 1024).toFixed(2)} MB`);
|
|
282
|
+
console.log(` ā ļø Expected to reduce by ~30-40% after optimization`);
|
|
283
|
+
}, 30000);
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
// Summary report
|
|
288
|
+
afterAll(() => {
|
|
289
|
+
console.log('\n' + '='.repeat(70));
|
|
290
|
+
console.log('š BASELINE BENCHMARK SUMMARY');
|
|
291
|
+
console.log('='.repeat(70));
|
|
292
|
+
console.log('\nThese results represent the CURRENT performance (v1.2.0)');
|
|
293
|
+
console.log('After implementing optimizations, run this test again to see improvements.\n');
|
|
294
|
+
console.log('Expected improvements after optimization:');
|
|
295
|
+
console.log(' ā Small files: 10-20% faster (async operations)');
|
|
296
|
+
console.log(' ā Large directories: 50-70% faster (async + array join)');
|
|
297
|
+
console.log(' ā Concurrent requests: 5-10x faster (non-blocking event loop)');
|
|
298
|
+
console.log(' ā Memory usage: 30-40% reduction (array join vs concatenation)');
|
|
299
|
+
console.log(' ā With HTTP caching: 80-95% faster (304 responses)');
|
|
300
|
+
console.log('='.repeat(70) + '\n');
|
|
301
|
+
});
|
package/__tests__/publicWwwTest/cartella vuota con spazi nel nome/file con spazio nel nome .txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
hello world
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
2
|
+
//
|
|
3
|
+
// SECURITY & BUG TESTS
|
|
4
|
+
// Questi test verificano le vulnerabilitĆ e i bug identificati nel DEBUG_REPORT.md
|
|
5
|
+
//
|
|
6
|
+
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
7
|
+
|
|
8
|
+
const supertest = require('supertest');
|
|
9
|
+
const koaClassicServer = require('../index.cjs');
|
|
10
|
+
const Koa = require('koa');
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
|
|
14
|
+
const rootDir = path.join(__dirname, 'publicWwwTest');
|
|
15
|
+
|
|
16
|
+
describe('Security Tests - Path Traversal', () => {
|
|
17
|
+
let app;
|
|
18
|
+
let server;
|
|
19
|
+
|
|
20
|
+
beforeAll(() => {
|
|
21
|
+
app = new Koa();
|
|
22
|
+
app.use(koaClassicServer(rootDir, {
|
|
23
|
+
showDirContents: true
|
|
24
|
+
}));
|
|
25
|
+
server = app.listen();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test('VULNERABILITY: Path traversal con ../ dovrebbe essere bloccato', async () => {
|
|
29
|
+
// Tenta di accedere al file package.json che ĆØ fuori da publicWwwTest
|
|
30
|
+
const res = await supertest(server).get('/../package.json');
|
|
31
|
+
|
|
32
|
+
// Il file NON dovrebbe essere accessibile
|
|
33
|
+
// ATTUALMENTE FALLISCE - questa ĆØ la vulnerabilitĆ !
|
|
34
|
+
// expect(res.status).toBe(403); // Dovrebbe essere forbidden
|
|
35
|
+
// expect(res.text).not.toContain('"name"'); // Non dovrebbe vedere il contenuto
|
|
36
|
+
|
|
37
|
+
// Per ora verifichiamo che la vulnerabilitĆ esista
|
|
38
|
+
console.log('ā ļø Path Traversal Test - Status:', res.status);
|
|
39
|
+
// Se vedi status 200 e contenuto di package.json, la vulnerabilitĆ ĆØ confermata
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('VULNERABILITY: Path traversal con encoding dovrebbe essere bloccato', async () => {
|
|
43
|
+
// Tenta con encoding URL
|
|
44
|
+
const res = await supertest(server).get('/%2e%2e%2f%2e%2e%2fpackage.json');
|
|
45
|
+
|
|
46
|
+
console.log('ā ļø Path Traversal Encoded Test - Status:', res.status);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('VULNERABILITY: Path traversal assoluto dovrebbe essere bloccato', async () => {
|
|
50
|
+
// Tenta path assoluto
|
|
51
|
+
const res = await supertest(server).get('/../../../etc/hosts');
|
|
52
|
+
|
|
53
|
+
console.log('ā ļø Path Traversal Absolute Test - Status:', res.status);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
afterAll(() => {
|
|
57
|
+
server.close();
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('Bug Tests - Status Code 404', () => {
|
|
62
|
+
let app;
|
|
63
|
+
let server;
|
|
64
|
+
|
|
65
|
+
beforeAll(() => {
|
|
66
|
+
app = new Koa();
|
|
67
|
+
app.use(koaClassicServer(rootDir, {
|
|
68
|
+
showDirContents: true
|
|
69
|
+
}));
|
|
70
|
+
server = app.listen();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test('FIXED: File inesistente dovrebbe restituire status 404', async () => {
|
|
74
|
+
const res = await supertest(server).get('/file-che-non-esiste-xyz123.txt');
|
|
75
|
+
|
|
76
|
+
console.log('ā
404 Status Test - Status Code:', res.status);
|
|
77
|
+
console.log(' Expected: 404, Got:', res.status);
|
|
78
|
+
|
|
79
|
+
// FIXED: Now returns proper 404 status
|
|
80
|
+
expect(res.status).toBe(404);
|
|
81
|
+
expect(res.text).toContain('Not Found');
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test('FIXED: Directory con showDirContents=false dovrebbe restituire 404', async () => {
|
|
85
|
+
const app2 = new Koa();
|
|
86
|
+
app2.use(koaClassicServer(rootDir, {
|
|
87
|
+
showDirContents: false
|
|
88
|
+
}));
|
|
89
|
+
const server2 = app2.listen();
|
|
90
|
+
|
|
91
|
+
const res = await supertest(server2).get('/');
|
|
92
|
+
|
|
93
|
+
console.log('ā
404 Directory Test - Status Code:', res.status);
|
|
94
|
+
|
|
95
|
+
// FIXED: Now returns proper 404 status
|
|
96
|
+
expect(res.status).toBe(404);
|
|
97
|
+
|
|
98
|
+
server2.close();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
afterAll(() => {
|
|
102
|
+
server.close();
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
describe('Bug Tests - Template Rendering Errors', () => {
|
|
107
|
+
test('BUG: Template render error dovrebbe essere gestito, non crashare', async () => {
|
|
108
|
+
const app = new Koa();
|
|
109
|
+
|
|
110
|
+
// Template che lancia errore
|
|
111
|
+
const brokenRender = async (ctx, next, filePath) => {
|
|
112
|
+
throw new Error('Simulated template rendering error');
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
app.use(koaClassicServer(rootDir, {
|
|
116
|
+
template: {
|
|
117
|
+
render: brokenRender,
|
|
118
|
+
ext: ['txt'] // Usa .txt per test
|
|
119
|
+
}
|
|
120
|
+
}));
|
|
121
|
+
|
|
122
|
+
const server = app.listen();
|
|
123
|
+
|
|
124
|
+
// Crea un file .txt per il test
|
|
125
|
+
const testFile = path.join(rootDir, 'test-template.txt');
|
|
126
|
+
fs.writeFileSync(testFile, 'test content');
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
const res = await supertest(server).get('/test-template.txt');
|
|
130
|
+
|
|
131
|
+
console.log('š Template Error Test - Status:', res.status);
|
|
132
|
+
|
|
133
|
+
// Dovrebbe gestire l'errore e restituire 500
|
|
134
|
+
// expect(res.status).toBe(500);
|
|
135
|
+
|
|
136
|
+
// ATTUALMENTE POTREBBE CRASHARE IL SERVER
|
|
137
|
+
// Se arriviamo qui senza crash, il test passa
|
|
138
|
+
console.log(' Server did not crash (good)');
|
|
139
|
+
} catch (error) {
|
|
140
|
+
console.log('ā ļø Template error caused request to fail:', error.message);
|
|
141
|
+
} finally {
|
|
142
|
+
// Cleanup
|
|
143
|
+
if (fs.existsSync(testFile)) {
|
|
144
|
+
fs.unlinkSync(testFile);
|
|
145
|
+
}
|
|
146
|
+
server.close();
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
describe('Bug Tests - File Extension Extraction', () => {
|
|
152
|
+
let app;
|
|
153
|
+
let server;
|
|
154
|
+
|
|
155
|
+
beforeAll(() => {
|
|
156
|
+
app = new Koa();
|
|
157
|
+
|
|
158
|
+
let renderCalled = false;
|
|
159
|
+
const trackingRender = async (ctx, next, filePath) => {
|
|
160
|
+
renderCalled = true;
|
|
161
|
+
ctx.renderCalled = true;
|
|
162
|
+
ctx.body = 'Rendered: ' + path.basename(filePath);
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
app.use(koaClassicServer(rootDir, {
|
|
166
|
+
template: {
|
|
167
|
+
render: trackingRender,
|
|
168
|
+
ext: ['txt']
|
|
169
|
+
}
|
|
170
|
+
}));
|
|
171
|
+
|
|
172
|
+
server = app.listen();
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
test('BUG: File senza estensione non dovrebbe attivare template rendering', async () => {
|
|
176
|
+
// Crea file senza estensione
|
|
177
|
+
const testFile = path.join(rootDir, 'README');
|
|
178
|
+
fs.writeFileSync(testFile, 'readme content');
|
|
179
|
+
|
|
180
|
+
try {
|
|
181
|
+
const res = await supertest(server).get('/README');
|
|
182
|
+
|
|
183
|
+
console.log('š No Extension Test - Render called?', res.text.includes('Rendered'));
|
|
184
|
+
|
|
185
|
+
// Non dovrebbe essere renderizzato
|
|
186
|
+
expect(res.text).not.toContain('Rendered');
|
|
187
|
+
} finally {
|
|
188
|
+
if (fs.existsSync(testFile)) {
|
|
189
|
+
fs.unlinkSync(testFile);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
test('BUG: File nascosto Unix non dovrebbe essere trattato con estensione sbagliata', async () => {
|
|
195
|
+
// Crea file nascosto
|
|
196
|
+
const testFile = path.join(rootDir, '.gitignore');
|
|
197
|
+
fs.writeFileSync(testFile, 'node_modules/');
|
|
198
|
+
|
|
199
|
+
try {
|
|
200
|
+
const res = await supertest(server).get('/.gitignore');
|
|
201
|
+
|
|
202
|
+
console.log('š Hidden File Test - Status:', res.status);
|
|
203
|
+
|
|
204
|
+
// .gitignore non ha estensione .txt, non dovrebbe essere renderizzato
|
|
205
|
+
// Ma con il bug attuale, potrebbe essere processato come estensione "gitignore"
|
|
206
|
+
} finally {
|
|
207
|
+
if (fs.existsSync(testFile)) {
|
|
208
|
+
fs.unlinkSync(testFile);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
afterAll(() => {
|
|
214
|
+
server.close();
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
describe('Bug Tests - Race Condition File Access', () => {
|
|
219
|
+
test('BUG: File cancellato tra check ed access dovrebbe essere gestito', async () => {
|
|
220
|
+
const app = new Koa();
|
|
221
|
+
app.use(koaClassicServer(rootDir));
|
|
222
|
+
const server = app.listen();
|
|
223
|
+
|
|
224
|
+
// Crea file temporaneo
|
|
225
|
+
const testFile = path.join(rootDir, 'temp-race-test.txt');
|
|
226
|
+
fs.writeFileSync(testFile, 'temporary content');
|
|
227
|
+
|
|
228
|
+
// Simula race condition: cancella il file appena dopo la richiesta
|
|
229
|
+
setTimeout(() => {
|
|
230
|
+
if (fs.existsSync(testFile)) {
|
|
231
|
+
fs.unlinkSync(testFile);
|
|
232
|
+
}
|
|
233
|
+
}, 5);
|
|
234
|
+
|
|
235
|
+
try {
|
|
236
|
+
const res = await supertest(server).get('/temp-race-test.txt');
|
|
237
|
+
|
|
238
|
+
console.log('š Race Condition Test - Status:', res.status);
|
|
239
|
+
|
|
240
|
+
// Dovrebbe gestire l'errore gracefully
|
|
241
|
+
// expect(res.status).toBe(404) o 500;
|
|
242
|
+
} catch (error) {
|
|
243
|
+
console.log('ā ļø Race condition caused error:', error.message);
|
|
244
|
+
} finally {
|
|
245
|
+
// Cleanup
|
|
246
|
+
if (fs.existsSync(testFile)) {
|
|
247
|
+
fs.unlinkSync(testFile);
|
|
248
|
+
}
|
|
249
|
+
server.close();
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
describe('Bug Tests - Directory Read Errors', () => {
|
|
255
|
+
test('BUG: Errore lettura directory dovrebbe essere gestito', async () => {
|
|
256
|
+
const app = new Koa();
|
|
257
|
+
|
|
258
|
+
// Crea una directory temporanea
|
|
259
|
+
const tempDir = path.join(rootDir, 'temp-test-dir');
|
|
260
|
+
if (!fs.existsSync(tempDir)) {
|
|
261
|
+
fs.mkdirSync(tempDir);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
app.use(koaClassicServer(rootDir, {
|
|
265
|
+
showDirContents: true
|
|
266
|
+
}));
|
|
267
|
+
|
|
268
|
+
const server = app.listen();
|
|
269
|
+
|
|
270
|
+
try {
|
|
271
|
+
// Prima richiesta normale
|
|
272
|
+
const res1 = await supertest(server).get('/temp-test-dir');
|
|
273
|
+
expect(res1.status).toBe(200);
|
|
274
|
+
|
|
275
|
+
// Ora cambia i permessi (solo su Unix)
|
|
276
|
+
if (process.platform !== 'win32') {
|
|
277
|
+
fs.chmodSync(tempDir, 0o000); // Nessun permesso
|
|
278
|
+
|
|
279
|
+
const res2 = await supertest(server).get('/temp-test-dir');
|
|
280
|
+
|
|
281
|
+
console.log('š Directory Permission Test - Status:', res2.status);
|
|
282
|
+
|
|
283
|
+
// Dovrebbe gestire l'errore
|
|
284
|
+
// expect(res2.status).toBe(500) o 403;
|
|
285
|
+
}
|
|
286
|
+
} catch (error) {
|
|
287
|
+
console.log('ā ļø Directory read error:', error.message);
|
|
288
|
+
} finally {
|
|
289
|
+
// Ripristina permessi e cleanup
|
|
290
|
+
if (process.platform !== 'win32' && fs.existsSync(tempDir)) {
|
|
291
|
+
try {
|
|
292
|
+
fs.chmodSync(tempDir, 0o755);
|
|
293
|
+
} catch (e) {}
|
|
294
|
+
}
|
|
295
|
+
if (fs.existsSync(tempDir)) {
|
|
296
|
+
fs.rmdirSync(tempDir);
|
|
297
|
+
}
|
|
298
|
+
server.close();
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
describe('Bug Tests - Content-Disposition', () => {
|
|
304
|
+
let app;
|
|
305
|
+
let server;
|
|
306
|
+
|
|
307
|
+
beforeAll(() => {
|
|
308
|
+
app = new Koa();
|
|
309
|
+
app.use(koaClassicServer(rootDir));
|
|
310
|
+
server = app.listen();
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
test('BUG: Filename con caratteri speciali dovrebbe essere quotato', async () => {
|
|
314
|
+
// Crea file con spazi e caratteri speciali
|
|
315
|
+
const testFile = path.join(rootDir, 'file with spaces & special.txt');
|
|
316
|
+
fs.writeFileSync(testFile, 'test content');
|
|
317
|
+
|
|
318
|
+
try {
|
|
319
|
+
const res = await supertest(server).get('/file%20with%20spaces%20%26%20special.txt');
|
|
320
|
+
|
|
321
|
+
const contentDisp = res.headers['content-disposition'];
|
|
322
|
+
console.log('š Content-Disposition:', contentDisp);
|
|
323
|
+
|
|
324
|
+
// Dovrebbe essere quotato
|
|
325
|
+
// expect(contentDisp).toMatch(/"file with spaces & special.txt"/);
|
|
326
|
+
} finally {
|
|
327
|
+
if (fs.existsSync(testFile)) {
|
|
328
|
+
fs.unlinkSync(testFile);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
afterAll(() => {
|
|
334
|
+
server.close();
|
|
335
|
+
});
|
|
336
|
+
});
|