kempo-server 1.6.1 → 1.6.3
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/package.json +1 -1
- package/tests/builtinMiddleware-cors.node-test.js +10 -10
- package/tests/builtinMiddleware.node-test.js +57 -58
- package/tests/cacheConfig.node-test.js +72 -62
- package/tests/defaultConfig.node-test.js +6 -7
- package/tests/example-middleware.node-test.js +16 -17
- package/tests/findFile.node-test.js +35 -35
- package/tests/getFiles.node-test.js +16 -16
- package/tests/getFlags.node-test.js +14 -14
- package/tests/index.node-test.js +24 -16
- package/tests/middlewareRunner.node-test.js +11 -11
- package/tests/moduleCache.node-test.js +180 -175
- package/tests/requestWrapper.node-test.js +28 -29
- package/tests/responseWrapper.node-test.js +55 -55
- package/tests/router-middleware.node-test.js +49 -37
- package/tests/router.node-test.js +96 -78
|
@@ -3,72 +3,72 @@ import createResponseWrapper from '../src/responseWrapper.js';
|
|
|
3
3
|
|
|
4
4
|
export default {
|
|
5
5
|
'status and set/get headers and type': async ({pass, fail}) => {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
6
|
+
const res = createMockRes();
|
|
7
|
+
const w = createResponseWrapper(res);
|
|
8
|
+
w.status(201).set('X-Test', '1').type('json');
|
|
9
|
+
|
|
10
|
+
if(res.statusCode !== 201) return fail('status');
|
|
11
|
+
if(res.getHeader('X-Test') !== '1') return fail('set/get');
|
|
12
|
+
if(res.getHeader('Content-Type') !== 'application/json') return fail('type');
|
|
13
|
+
|
|
14
|
+
pass('status+headers+type');
|
|
15
15
|
},
|
|
16
16
|
'json sends and prevents further changes': async ({pass, fail}) => {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
17
|
+
const res = createMockRes();
|
|
18
|
+
const w = createResponseWrapper(res);
|
|
19
|
+
w.json({a: 1});
|
|
20
|
+
|
|
21
|
+
if(!res.isEnded()) return fail('ended');
|
|
22
|
+
|
|
23
|
+
try { w.set('X', 'y'); fail('should not set after send'); } catch(_){ /* ok */ }
|
|
24
|
+
|
|
25
|
+
pass('json');
|
|
25
26
|
},
|
|
26
27
|
'send handles string, object, buffer and null': async ({pass, fail}) => {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
if(res1.getBody().toString() !== 'hello') return fail('string body');
|
|
28
|
+
const res1 = createMockRes();
|
|
29
|
+
createResponseWrapper(res1).send('hello');
|
|
30
|
+
// Content-Type defaults to text/html for string when not set
|
|
31
|
+
if(res1.getHeader('Content-Type') !== 'text/html') return fail('string content-type');
|
|
32
|
+
if(res1.getBody().toString() !== 'hello') return fail('string body');
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
const res2 = createMockRes();
|
|
35
|
+
createResponseWrapper(res2).send({a:1});
|
|
36
|
+
if(res2.getHeader('Content-Type') !== 'application/json') return fail('object content-type');
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
const res3 = createMockRes();
|
|
39
|
+
const buf = Buffer.from('abc');
|
|
40
|
+
createResponseWrapper(res3).send(buf);
|
|
41
|
+
const body3 = res3.getBody().toString();
|
|
42
|
+
if(!body3.includes('"data"')) return fail('buffer equal');
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
44
|
+
const res4 = createMockRes();
|
|
45
|
+
createResponseWrapper(res4).send(null);
|
|
46
|
+
if(!res4.isEnded()) return fail('null ended');
|
|
47
|
+
|
|
48
|
+
pass('send variants');
|
|
49
49
|
},
|
|
50
50
|
'html and text helpers': async ({pass, fail}) => {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if(r1.getHeader('Content-Type') !== 'text/html') return fail('html type');
|
|
51
|
+
const r1 = createMockRes();
|
|
52
|
+
createResponseWrapper(r1).html('<h1>Ok</h1>');
|
|
53
|
+
if(r1.getHeader('Content-Type') !== 'text/html') return fail('html type');
|
|
55
54
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
55
|
+
const r2 = createMockRes();
|
|
56
|
+
createResponseWrapper(r2).text('plain');
|
|
57
|
+
if(r2.getHeader('Content-Type') !== 'text/plain') return fail('text type');
|
|
58
|
+
|
|
59
|
+
pass('helpers');
|
|
61
60
|
},
|
|
62
61
|
'redirect and cookies': async ({pass, fail}) => {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
62
|
+
const r = createMockRes();
|
|
63
|
+
const w = createResponseWrapper(r);
|
|
64
|
+
w.cookie('a', 'b', {httpOnly: true, path: '/'});
|
|
65
|
+
const cookies = parseCookies(r.getHeader('Set-Cookie'));
|
|
66
|
+
|
|
67
|
+
if(!(cookies.length === 1 && cookies[0].includes('a=b'))) return fail('cookie added');
|
|
68
|
+
|
|
69
|
+
w.redirect('/next', 301);
|
|
70
|
+
if(!(r.statusCode === 301 && r.getHeader('Location') === '/next')) return fail('redirect');
|
|
71
|
+
|
|
72
|
+
pass('redirect+cookie');
|
|
73
73
|
}
|
|
74
74
|
};
|
|
@@ -4,43 +4,55 @@ import router from '../src/router.js';
|
|
|
4
4
|
|
|
5
5
|
export default {
|
|
6
6
|
'built-in middleware can be configured on router': async ({pass, fail}) => {
|
|
7
|
-
|
|
8
|
-
await
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
});
|
|
33
|
-
});
|
|
34
|
-
if(one.r.statusCode !== 200) return fail('first ok');
|
|
35
|
-
const two = await new Promise((res)=>{
|
|
36
|
-
get(`http://localhost:${port}/index.html`, r =>{
|
|
37
|
-
const chunks = []; r.on('data', c => chunks.push(c)); r.on('end', ()=> res({r, body: Buffer.concat(chunks)}));
|
|
38
|
-
});
|
|
39
|
-
});
|
|
40
|
-
if(two.r.statusCode !== 429) return fail('rate limited');
|
|
41
|
-
} finally { server.close(); process.chdir(prev); }
|
|
7
|
+
await withTempDir(async (dir) => {
|
|
8
|
+
await write(dir, '.config.json', JSON.stringify({
|
|
9
|
+
middleware: {
|
|
10
|
+
cors: {enabled: true, origin: '*', methods: ['GET'], headers: ['X']},
|
|
11
|
+
compression: {enabled: true, threshold: 1},
|
|
12
|
+
rateLimit: {enabled: true, maxRequests: 1, windowMs: 1000, message: 'Too many'},
|
|
13
|
+
security: {enabled: true, headers: {'X-Test':'1'}},
|
|
14
|
+
logging: {enabled: true, includeUserAgent: false, includeResponseTime: true}
|
|
15
|
+
}
|
|
16
|
+
}));
|
|
17
|
+
await write(dir, 'index.html', 'hello world');
|
|
18
|
+
const prev = process.cwd();
|
|
19
|
+
process.chdir(dir);
|
|
20
|
+
const flags = {root: '.', logging: 0, scan: false};
|
|
21
|
+
const handler = await router(flags, () => {});
|
|
22
|
+
const server = http.createServer(handler);
|
|
23
|
+
const port = randomPort();
|
|
24
|
+
await new Promise(r => server.listen(port, r));
|
|
25
|
+
await new Promise(r => setTimeout(r, 50));
|
|
26
|
+
|
|
27
|
+
const {get} = await import('http');
|
|
28
|
+
const one = await new Promise((res)=>{
|
|
29
|
+
get(`http://localhost:${port}/index.html`, {headers: {'accept-encoding': 'gzip'}}, r =>{
|
|
30
|
+
const chunks = []; r.on('data', c => chunks.push(c)); r.on('end', ()=> res({r, body: Buffer.concat(chunks)}));
|
|
31
|
+
});
|
|
42
32
|
});
|
|
43
|
-
|
|
44
|
-
|
|
33
|
+
|
|
34
|
+
if(one.r.statusCode !== 200) {
|
|
35
|
+
server.close();
|
|
36
|
+
process.chdir(prev);
|
|
37
|
+
return fail('first ok');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const two = await new Promise((res)=>{
|
|
41
|
+
get(`http://localhost:${port}/index.html`, r =>{
|
|
42
|
+
const chunks = []; r.on('data', c => chunks.push(c)); r.on('end', ()=> res({r, body: Buffer.concat(chunks)}));
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
if(two.r.statusCode !== 429) {
|
|
47
|
+
server.close();
|
|
48
|
+
process.chdir(prev);
|
|
49
|
+
return fail('rate limited');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
server.close();
|
|
53
|
+
process.chdir(prev);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
pass('router middleware');
|
|
45
57
|
}
|
|
46
58
|
};
|
|
@@ -5,86 +5,104 @@ import router from '../src/router.js';
|
|
|
5
5
|
import defaultConfig from '../src/defaultConfig.js';
|
|
6
6
|
|
|
7
7
|
export default {
|
|
8
|
-
'serves static files and 404s unknown': async ({pass, fail
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
8
|
+
'serves static files and 404s unknown': async ({pass, fail}) => {
|
|
9
|
+
await withTempDir(async (dir) => {
|
|
10
|
+
await write(dir, 'index.html', '<h1>Home</h1>');
|
|
11
|
+
const prev = process.cwd();
|
|
12
|
+
process.chdir(dir);
|
|
13
|
+
const flags = {root: '.', logging: 0, scan: false};
|
|
14
|
+
const logFn = () => {};
|
|
15
|
+
const handler = await router(flags, logFn);
|
|
16
|
+
const server = http.createServer(handler);
|
|
17
|
+
const port = randomPort();
|
|
18
|
+
await new Promise(r => server.listen(port, r));
|
|
19
|
+
await new Promise(r => setTimeout(r, 50));
|
|
20
|
+
|
|
21
|
+
const ok = await httpGet(`http://localhost:${port}/index.html`);
|
|
22
|
+
if(ok.res.statusCode !== 200) {
|
|
23
|
+
server.close();
|
|
24
|
+
process.chdir(prev);
|
|
25
|
+
return fail('static 200');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const miss = await httpGet(`http://localhost:${port}/nope`);
|
|
29
|
+
if(miss.res.statusCode !== 404) {
|
|
30
|
+
server.close();
|
|
31
|
+
process.chdir(prev);
|
|
32
|
+
return fail('404');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
server.close();
|
|
30
36
|
process.chdir(prev);
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
pass('static + 404');
|
|
34
|
-
} catch(e){ fail(e.message); }
|
|
37
|
+
});
|
|
38
|
+
pass('static + 404');
|
|
35
39
|
},
|
|
36
|
-
'rescan on 404 when enabled and not blacklisted': async ({pass, fail
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
40
|
+
'rescan on 404 when enabled and not blacklisted': async ({pass, fail}) => {
|
|
41
|
+
await withTempDir(async (dir) => {
|
|
42
|
+
const prev = process.cwd();
|
|
43
|
+
process.chdir(dir);
|
|
44
|
+
const flags = {root: '.', logging: 0, scan: true};
|
|
45
|
+
const handler = await router(flags, log);
|
|
46
|
+
const server = http.createServer(handler);
|
|
47
|
+
const port = randomPort();
|
|
48
|
+
await new Promise(r => server.listen(port, r));
|
|
49
|
+
await new Promise(r => setTimeout(r, 50));
|
|
50
|
+
|
|
51
|
+
const miss1 = await httpGet(`http://localhost:${port}/late.html`);
|
|
52
|
+
if(miss1.res.statusCode !== 404) {
|
|
53
|
+
server.close();
|
|
54
|
+
process.chdir(prev);
|
|
55
|
+
return fail('first 404');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
await write(dir, 'late.html', 'later');
|
|
59
|
+
const hit = await httpGet(`http://localhost:${port}/late.html`);
|
|
60
|
+
if(hit.res.statusCode !== 200) {
|
|
61
|
+
server.close();
|
|
62
|
+
process.chdir(prev);
|
|
63
|
+
return fail('served after rescan');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
server.close();
|
|
67
|
+
process.chdir(prev);
|
|
68
|
+
});
|
|
69
|
+
pass('rescan');
|
|
59
70
|
},
|
|
60
|
-
'custom and wildcard routes serve mapped files': async ({pass, fail
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
71
|
+
'custom and wildcard routes serve mapped files': async ({pass, fail}) => {
|
|
72
|
+
await withTempDir(async (dir) => {
|
|
73
|
+
const fileA = await write(dir, 'a.txt', 'A');
|
|
74
|
+
const fileB = await write(dir, 'b/1.txt', 'B1');
|
|
75
|
+
const prev = process.cwd();
|
|
76
|
+
process.chdir(dir);
|
|
77
|
+
const flags = {root: '.', logging: 0, scan: false};
|
|
78
|
+
const logFn = () => {};
|
|
79
|
+
// write config before init
|
|
80
|
+
await write(dir, '.config.json', JSON.stringify({
|
|
81
|
+
customRoutes: {'/a': fileA, 'b/*': path.join(dir, 'b/*')}
|
|
82
|
+
}));
|
|
83
|
+
const handler = await router(flags, logFn);
|
|
84
|
+
const server = http.createServer(handler);
|
|
85
|
+
const port = randomPort();
|
|
86
|
+
await new Promise(r => server.listen(port, r));
|
|
87
|
+
await new Promise(r => setTimeout(r, 50));
|
|
88
|
+
|
|
89
|
+
const r1 = await httpGet(`http://localhost:${port}/a`);
|
|
90
|
+
if(r1.body.toString() !== 'A') {
|
|
91
|
+
server.close();
|
|
92
|
+
process.chdir(prev);
|
|
93
|
+
return fail('custom route');
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const r2 = await httpGet(`http://localhost:${port}/b/1.txt`);
|
|
97
|
+
if(r2.body.toString() !== 'B1') {
|
|
98
|
+
server.close();
|
|
99
|
+
process.chdir(prev);
|
|
100
|
+
return fail('wildcard');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
server.close();
|
|
104
|
+
process.chdir(prev);
|
|
105
|
+
});
|
|
106
|
+
pass('custom+wildcard');
|
|
89
107
|
}
|
|
90
108
|
};
|