impulse-api 3.0.7 → 3.0.9
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 +4 -2
- package/src/server.js +11 -10
- package/test/integration/rawbody-integration-test.js +113 -0
- package/test/integration/routes/normal.js +24 -0
- package/test/integration/routes/webhook.js +18 -0
- package/test/server-test.js +74 -0
- package/test/{api-test.js → unit/api-test.js} +1 -1
- package/test/{custom-jwt-validation.js → unit/custom-jwt-validation.js} +2 -2
- package/test/unit/server-test.js +779 -0
package/package.json
CHANGED
|
@@ -9,10 +9,12 @@
|
|
|
9
9
|
],
|
|
10
10
|
"scripts": {
|
|
11
11
|
"lint": "eslint --ignore-path .gitignore .",
|
|
12
|
-
"test": "
|
|
12
|
+
"test": "npm run test:unit && npm run test:integration",
|
|
13
|
+
"test:unit": "nyc --reporter=lcov --reporter=text-summary mocha test/unit/**/*.js",
|
|
14
|
+
"test:integration": "nyc --reporter=lcov --reporter=text-summary mocha 'test/integration/**/*-test.js'",
|
|
13
15
|
"test-server": "node ./test/integration/test-server.js"
|
|
14
16
|
},
|
|
15
|
-
"version": "3.0.
|
|
17
|
+
"version": "3.0.9",
|
|
16
18
|
"engines": {
|
|
17
19
|
"node": ">=22"
|
|
18
20
|
},
|
package/src/server.js
CHANGED
|
@@ -92,9 +92,8 @@ class Server {
|
|
|
92
92
|
this.http.use(express.urlencoded({
|
|
93
93
|
extended: true,
|
|
94
94
|
}));
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}));
|
|
95
|
+
// Don't use global express.json() - apply it per-route to avoid conflicts with rawBody routes
|
|
96
|
+
// express.urlencoded() is safe to keep global as it only parses application/x-www-form-urlencoded
|
|
98
97
|
this.http.use(fileUpload({
|
|
99
98
|
preserveExtension: 10
|
|
100
99
|
}));
|
|
@@ -266,10 +265,12 @@ class Server {
|
|
|
266
265
|
|
|
267
266
|
if (route.inputs) {
|
|
268
267
|
try {
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
268
|
+
// When rawBody is true, exclude req.body from inputs processing since it's a Buffer
|
|
269
|
+
const paramsForInputs = route.rawBody === true
|
|
270
|
+
? Object.assign(req.query || {}, req.params || {}, req.files || {})
|
|
271
|
+
: Object.assign(req.query || {}, req.body || {}, req.params || {}, req.files || {});
|
|
272
|
+
|
|
273
|
+
data = this.buildParameters(paramsForInputs, route.inputs);
|
|
273
274
|
} catch (error) {
|
|
274
275
|
sendResponse(error);
|
|
275
276
|
return;
|
|
@@ -483,10 +484,10 @@ class Server {
|
|
|
483
484
|
if (route.rawBody === true) {
|
|
484
485
|
this.http[verb](route.endpoint, express.raw({ type: 'application/json' }), this.preprocessor.bind(this, route));
|
|
485
486
|
} else {
|
|
487
|
+
// Apply express.json() per-route for routes that need JSON parsing
|
|
486
488
|
// express-fileupload handles multipart/form-data (including files)
|
|
487
|
-
// express.
|
|
488
|
-
|
|
489
|
-
this.http[verb](route.endpoint, this.preprocessor.bind(this, route));
|
|
489
|
+
// express.urlencoded() is already global and only parses form-encoded data
|
|
490
|
+
this.http[verb](route.endpoint, express.json({ extended: true }), this.preprocessor.bind(this, route));
|
|
490
491
|
}
|
|
491
492
|
|
|
492
493
|
});
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
const Server = require('../../src/server');
|
|
2
|
+
const assert = require('assert');
|
|
3
|
+
const http = require('http');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
describe('rawBody HTTP Integration', () => {
|
|
7
|
+
let testServer;
|
|
8
|
+
let testRouteDir;
|
|
9
|
+
let testPort = 9998;
|
|
10
|
+
|
|
11
|
+
before(async () => {
|
|
12
|
+
testRouteDir = path.join(__dirname, 'routes');
|
|
13
|
+
// Create and start test server once for all tests (use 'dev' env so server actually starts)
|
|
14
|
+
testServer = new Server({
|
|
15
|
+
name: 'test-server',
|
|
16
|
+
routeDir: testRouteDir,
|
|
17
|
+
port: testPort,
|
|
18
|
+
env: 'dev',
|
|
19
|
+
services: {}
|
|
20
|
+
});
|
|
21
|
+
await testServer.init();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
after(async () => {
|
|
25
|
+
// Clean up test server
|
|
26
|
+
if (testServer && testServer.http) {
|
|
27
|
+
await new Promise((resolve) => {
|
|
28
|
+
if (testServer.http.listening) {
|
|
29
|
+
testServer.http.close(() => resolve());
|
|
30
|
+
} else {
|
|
31
|
+
resolve();
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should receive raw Buffer via HTTP request when rawBody is true', async () => {
|
|
38
|
+
|
|
39
|
+
// Make HTTP POST request
|
|
40
|
+
const testBody = JSON.stringify({ test: 'data', value: 123 });
|
|
41
|
+
const response = await new Promise((resolve, reject) => {
|
|
42
|
+
const options = {
|
|
43
|
+
hostname: 'localhost',
|
|
44
|
+
port: testPort,
|
|
45
|
+
path: '/webhook',
|
|
46
|
+
method: 'POST',
|
|
47
|
+
headers: {
|
|
48
|
+
'Content-Type': 'application/json',
|
|
49
|
+
'Content-Length': Buffer.byteLength(testBody)
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const req = http.request(options, (res) => {
|
|
54
|
+
let data = '';
|
|
55
|
+
res.on('data', (chunk) => {
|
|
56
|
+
data += chunk;
|
|
57
|
+
});
|
|
58
|
+
res.on('end', () => {
|
|
59
|
+
resolve({ statusCode: res.statusCode, body: data });
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
req.on('error', reject);
|
|
64
|
+
req.write(testBody);
|
|
65
|
+
req.end();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
assert.strictEqual(response.statusCode, 200);
|
|
69
|
+
const responseBody = JSON.parse(response.body);
|
|
70
|
+
assert.strictEqual(responseBody.success, true);
|
|
71
|
+
assert.strictEqual(responseBody.isBuffer, true);
|
|
72
|
+
assert.strictEqual(responseBody.rawBodyString, testBody);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('should receive parsed JSON via HTTP request when rawBody is false', async () => {
|
|
76
|
+
|
|
77
|
+
// Make HTTP POST request
|
|
78
|
+
const testBody = JSON.stringify({ test: 'data', value: 123 });
|
|
79
|
+
const response = await new Promise((resolve, reject) => {
|
|
80
|
+
const options = {
|
|
81
|
+
hostname: 'localhost',
|
|
82
|
+
port: testPort,
|
|
83
|
+
path: '/normal',
|
|
84
|
+
method: 'POST',
|
|
85
|
+
headers: {
|
|
86
|
+
'Content-Type': 'application/json',
|
|
87
|
+
'Content-Length': Buffer.byteLength(testBody)
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const req = http.request(options, (res) => {
|
|
92
|
+
let data = '';
|
|
93
|
+
res.on('data', (chunk) => {
|
|
94
|
+
data += chunk;
|
|
95
|
+
});
|
|
96
|
+
res.on('end', () => {
|
|
97
|
+
resolve({ statusCode: res.statusCode, body: data });
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
req.on('error', reject);
|
|
102
|
+
req.write(testBody);
|
|
103
|
+
req.end();
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
assert.strictEqual(response.statusCode, 200);
|
|
107
|
+
const responseBody = JSON.parse(response.body);
|
|
108
|
+
assert.strictEqual(responseBody.success, true);
|
|
109
|
+
assert.strictEqual(responseBody.test, 'data');
|
|
110
|
+
assert.strictEqual(responseBody.value, 123);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
exports.normal = {
|
|
2
|
+
name: 'normal',
|
|
3
|
+
method: 'post',
|
|
4
|
+
endpoint: '/normal',
|
|
5
|
+
inputs: {
|
|
6
|
+
test: {
|
|
7
|
+
required: true
|
|
8
|
+
},
|
|
9
|
+
value: {
|
|
10
|
+
required: true
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
run: (services, inputs, next) => {
|
|
14
|
+
if (inputs.rawBody !== undefined) {
|
|
15
|
+
return next(500, { error: 'rawBody should not be set' });
|
|
16
|
+
}
|
|
17
|
+
next(200, {
|
|
18
|
+
success: true,
|
|
19
|
+
test: inputs.test,
|
|
20
|
+
value: inputs.value
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
exports.webhook = {
|
|
2
|
+
name: 'webhook',
|
|
3
|
+
method: 'post',
|
|
4
|
+
endpoint: '/webhook',
|
|
5
|
+
rawBody: true,
|
|
6
|
+
run: (services, inputs, next) => {
|
|
7
|
+
if (!Buffer.isBuffer(inputs.rawBody)) {
|
|
8
|
+
return next(500, { error: 'rawBody is not a Buffer' });
|
|
9
|
+
}
|
|
10
|
+
next(200, {
|
|
11
|
+
success: true,
|
|
12
|
+
isBuffer: Buffer.isBuffer(inputs.rawBody),
|
|
13
|
+
rawBodyLength: inputs.rawBody.length,
|
|
14
|
+
rawBodyString: inputs.rawBody.toString()
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
|
package/test/server-test.js
CHANGED
|
@@ -701,5 +701,79 @@ describe('server-test', () => {
|
|
|
701
701
|
await Api.preprocessor(route, req, res);
|
|
702
702
|
assert.strictEqual(res.statusCode, 200);
|
|
703
703
|
});
|
|
704
|
+
|
|
705
|
+
it('should not include Buffer body in inputs processing when rawBody is true', async () => {
|
|
706
|
+
const Api = new Server({
|
|
707
|
+
name: 'test-Server',
|
|
708
|
+
routeDir: './test-routes',
|
|
709
|
+
port: 4000,
|
|
710
|
+
env: 'test',
|
|
711
|
+
services: {}
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
const testBody = Buffer.from(JSON.stringify({ test: 'data', shouldNotBeParsed: true }));
|
|
715
|
+
const req = {
|
|
716
|
+
body: testBody,
|
|
717
|
+
query: { param1: 'value1' },
|
|
718
|
+
params: { id: '123' },
|
|
719
|
+
files: {},
|
|
720
|
+
headers: {},
|
|
721
|
+
get: (header) => {
|
|
722
|
+
if (header === 'origin') return 'http://localhost:4000';
|
|
723
|
+
if (header === 'host') return 'localhost:4000';
|
|
724
|
+
return null;
|
|
725
|
+
}
|
|
726
|
+
};
|
|
727
|
+
const res = {
|
|
728
|
+
status: (code) => {
|
|
729
|
+
res.statusCode = code;
|
|
730
|
+
return res;
|
|
731
|
+
},
|
|
732
|
+
send: (data) => {
|
|
733
|
+
res.sentData = data;
|
|
734
|
+
}
|
|
735
|
+
};
|
|
736
|
+
|
|
737
|
+
const route = {
|
|
738
|
+
name: 'test-raw-body-excluded-from-inputs',
|
|
739
|
+
method: 'post',
|
|
740
|
+
endpoint: '/test/:id',
|
|
741
|
+
rawBody: true,
|
|
742
|
+
inputs: {
|
|
743
|
+
param1: {
|
|
744
|
+
required: true
|
|
745
|
+
},
|
|
746
|
+
id: {
|
|
747
|
+
required: true
|
|
748
|
+
},
|
|
749
|
+
// This should NOT be found in inputs even though it's in the Buffer body
|
|
750
|
+
test: {
|
|
751
|
+
required: false
|
|
752
|
+
},
|
|
753
|
+
shouldNotBeParsed: {
|
|
754
|
+
required: false
|
|
755
|
+
}
|
|
756
|
+
},
|
|
757
|
+
run: (services, inputs, next) => {
|
|
758
|
+
// Verify rawBody is still a Buffer
|
|
759
|
+
assert.strictEqual(Buffer.isBuffer(inputs.rawBody), true);
|
|
760
|
+
assert.deepStrictEqual(inputs.rawBody, testBody);
|
|
761
|
+
|
|
762
|
+
// Verify inputs from query/params work
|
|
763
|
+
assert.strictEqual(inputs.param1, 'value1');
|
|
764
|
+
assert.strictEqual(inputs.id, '123');
|
|
765
|
+
|
|
766
|
+
// Verify that parsed body fields are NOT in inputs (because Buffer was excluded)
|
|
767
|
+
// buildParameters sets missing optional params to empty string, not undefined
|
|
768
|
+
assert.strictEqual(inputs.test, "");
|
|
769
|
+
assert.strictEqual(inputs.shouldNotBeParsed, "");
|
|
770
|
+
|
|
771
|
+
next(200, { success: true });
|
|
772
|
+
}
|
|
773
|
+
};
|
|
774
|
+
|
|
775
|
+
await Api.preprocessor(route, req, res);
|
|
776
|
+
assert.strictEqual(res.statusCode, 200);
|
|
777
|
+
});
|
|
704
778
|
});
|
|
705
779
|
});
|
|
@@ -0,0 +1,779 @@
|
|
|
1
|
+
const Server = require('../../src/server');
|
|
2
|
+
const Auth = require('../../src/auth');
|
|
3
|
+
const assert = require('assert');
|
|
4
|
+
const sinon = require('sinon');
|
|
5
|
+
|
|
6
|
+
const testApi = new Server({
|
|
7
|
+
name: 'test-server',
|
|
8
|
+
routeDir: './test-routes',
|
|
9
|
+
port: 4000,
|
|
10
|
+
env: 'test',
|
|
11
|
+
services: {}
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
assert.contains = (orig, key, message) => {
|
|
15
|
+
assert.strictEqual(orig.indexOf(key) >= 0, true, message);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
describe('server-test', () => {
|
|
19
|
+
describe('instantiation', () => {
|
|
20
|
+
it('should throw an error if a configuration object is missing', () => {
|
|
21
|
+
try {
|
|
22
|
+
new Server();
|
|
23
|
+
} catch (e) {
|
|
24
|
+
assert.contains(e.message, 'valid config');
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
it('should throw an error if service name is missing', () => {
|
|
28
|
+
try {
|
|
29
|
+
new Server({
|
|
30
|
+
routeDir: './'
|
|
31
|
+
});
|
|
32
|
+
} catch (e) {
|
|
33
|
+
assert.contains(e.message, 'name');
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
it('should throw an error if route directory is missing', () => {
|
|
37
|
+
try {
|
|
38
|
+
new Server({
|
|
39
|
+
name: 'test-Server'
|
|
40
|
+
});
|
|
41
|
+
} catch (e) {
|
|
42
|
+
assert.contains(e.message, 'route');
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
it('should property set the port', () => {
|
|
46
|
+
const Api = new Server({
|
|
47
|
+
name: 'test-server',
|
|
48
|
+
routeDir: './test-routes',
|
|
49
|
+
port: 4000,
|
|
50
|
+
env: 'test',
|
|
51
|
+
services: {}
|
|
52
|
+
});
|
|
53
|
+
assert.strictEqual(Api.port, 4000);
|
|
54
|
+
});
|
|
55
|
+
it('should properly set the environment', () => {
|
|
56
|
+
const Api = new Server({
|
|
57
|
+
name: 'test-Server',
|
|
58
|
+
routeDir: './test-routes',
|
|
59
|
+
port: 4000,
|
|
60
|
+
env: 'test',
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
assert.strictEqual(Api.env, 'test');
|
|
64
|
+
});
|
|
65
|
+
it('should set the app key to whatever is passed in', () => {
|
|
66
|
+
const Api = new Server({
|
|
67
|
+
name: 'test-Server',
|
|
68
|
+
routeDir: './routes/sample-routes',
|
|
69
|
+
port: 4000,
|
|
70
|
+
env: 'test',
|
|
71
|
+
appKey: 'TestKey'
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
assert.strictEqual(Api.appKey, 'TestKey');
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
describe('route parameters', () => {
|
|
79
|
+
it('should not crash if there are no inputs provided', () => {
|
|
80
|
+
const Api = new Server({
|
|
81
|
+
name: 'test-Server',
|
|
82
|
+
routeDir: './test-routes',
|
|
83
|
+
port: 4000,
|
|
84
|
+
env: 'test',
|
|
85
|
+
});
|
|
86
|
+
Api.buildParameters({}, null);
|
|
87
|
+
});
|
|
88
|
+
it('should not crash if there are no params provided', () => {
|
|
89
|
+
const Api = new Server({
|
|
90
|
+
name: 'test-Server',
|
|
91
|
+
routeDir: './test-routes',
|
|
92
|
+
port: 4000,
|
|
93
|
+
env: 'test',
|
|
94
|
+
});
|
|
95
|
+
Api.buildParameters(null, {});
|
|
96
|
+
});
|
|
97
|
+
it('should pass a param with a true/false value', () => {
|
|
98
|
+
const Api = new Server({
|
|
99
|
+
name: 'test-Server',
|
|
100
|
+
routeDir: './test-routes',
|
|
101
|
+
port: 4000,
|
|
102
|
+
env: 'test',
|
|
103
|
+
});
|
|
104
|
+
const output = Api.buildParameters({ grade: 0 }, {
|
|
105
|
+
grade: {
|
|
106
|
+
required: true
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
assert.strictEqual(output.grade, 0);
|
|
111
|
+
});
|
|
112
|
+
it('should throw an error if there is a required input that is not in the params', () => {
|
|
113
|
+
const Api = new Server({
|
|
114
|
+
name: 'test-Server',
|
|
115
|
+
routeDir: './test-routes',
|
|
116
|
+
port: 4000,
|
|
117
|
+
env: 'test',
|
|
118
|
+
});
|
|
119
|
+
try {
|
|
120
|
+
Api.buildParameters({
|
|
121
|
+
'test-param': {
|
|
122
|
+
required: true
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
{}
|
|
126
|
+
);
|
|
127
|
+
} catch (e) {
|
|
128
|
+
console.log(e)
|
|
129
|
+
assert.contains(e.message, 'Missing parameter [test-param]');
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
it('should apply a formatter if the input provides one', () => {
|
|
133
|
+
const Api = new Server({
|
|
134
|
+
name: 'test-Server',
|
|
135
|
+
routeDir: './test-routes',
|
|
136
|
+
port: 4000,
|
|
137
|
+
env: 'test'
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
let output;
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
output = Api.buildParameters({
|
|
144
|
+
param1: '1',
|
|
145
|
+
param2: '2'
|
|
146
|
+
}, {
|
|
147
|
+
param1: {
|
|
148
|
+
required: true,
|
|
149
|
+
formatter: (param) => {
|
|
150
|
+
assert.strictEqual(param, '1');
|
|
151
|
+
return parseInt(param, 10) + 1;
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
param2: {
|
|
155
|
+
required: true
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
assert.strictEqual(output.param1, 2);
|
|
160
|
+
assert.strictEqual(output.param2, '2');
|
|
161
|
+
} catch (e) {
|
|
162
|
+
assert.ifError(e);
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
it('should run parameters through the validator after being formatted', () => {
|
|
166
|
+
const Api = new Server({
|
|
167
|
+
name: 'test-Server',
|
|
168
|
+
routeDir: './test-routes',
|
|
169
|
+
port: 4000,
|
|
170
|
+
env: 'test',
|
|
171
|
+
});
|
|
172
|
+
let output;
|
|
173
|
+
try {
|
|
174
|
+
output = Api.buildParameters({
|
|
175
|
+
param1: '1',
|
|
176
|
+
param2: '2'
|
|
177
|
+
}, {
|
|
178
|
+
param1: {
|
|
179
|
+
required: true,
|
|
180
|
+
formatter: (param) => {
|
|
181
|
+
assert.strictEqual(param, '1');
|
|
182
|
+
return parseInt(param, 10) + 1;
|
|
183
|
+
},
|
|
184
|
+
validator: () => {
|
|
185
|
+
assert.strictEqual(output.param1, 2);
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
param2: {
|
|
189
|
+
required: true
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
assert.strictEqual(output.param1, 2);
|
|
194
|
+
assert.strictEqual(output.param2, '2');
|
|
195
|
+
} catch (e) {
|
|
196
|
+
assert.ifError(e);
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
it('should return only the data that is defined in the inputs', () => {
|
|
200
|
+
const Api = new Server({
|
|
201
|
+
name: 'test-Server',
|
|
202
|
+
routeDir: './test-routes',
|
|
203
|
+
port: 4000,
|
|
204
|
+
env: 'test',
|
|
205
|
+
});
|
|
206
|
+
let output;
|
|
207
|
+
try {
|
|
208
|
+
output = Api.buildParameters({
|
|
209
|
+
param1: '1',
|
|
210
|
+
param2: '2',
|
|
211
|
+
param3: '3'
|
|
212
|
+
}, {
|
|
213
|
+
param1: {
|
|
214
|
+
required: true
|
|
215
|
+
},
|
|
216
|
+
param2: {
|
|
217
|
+
required: true
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
assert.strictEqual(output.param1, '1');
|
|
222
|
+
assert.strictEqual(output.param2, '2');
|
|
223
|
+
assert.strictEqual(output.param3, undefined);
|
|
224
|
+
} catch (e) {
|
|
225
|
+
assert.ifError(e);
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
describe('#loadRoutes', () => {
|
|
231
|
+
it('it should return an error if the route does not have a "run" method', () => {
|
|
232
|
+
const Api = new Server({
|
|
233
|
+
name: 'test-Server',
|
|
234
|
+
routeDir: require('path').join(__dirname, '../routes'),
|
|
235
|
+
port: 4000,
|
|
236
|
+
env: 'test'
|
|
237
|
+
});
|
|
238
|
+
const routeFiles = [
|
|
239
|
+
'missing-run.js'
|
|
240
|
+
];
|
|
241
|
+
Api.loadRoutes(routeFiles).catch((error) => {
|
|
242
|
+
assert.contains(error.message, 'missing a "run" property');
|
|
243
|
+
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
it('it should return an error if the route does not have a name', () => {
|
|
247
|
+
const Api = new Server({
|
|
248
|
+
name: 'test-Server',
|
|
249
|
+
routeDir: require('path').join(__dirname, '../routes'),
|
|
250
|
+
port: 4000,
|
|
251
|
+
env: 'test'
|
|
252
|
+
});
|
|
253
|
+
const routeFiles = [
|
|
254
|
+
'missing-name.js'
|
|
255
|
+
];
|
|
256
|
+
Api.loadRoutes(routeFiles).catch((error) => {
|
|
257
|
+
assert.contains(error.message, 'missing a "name" property');
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
it('should return an error if the route does not have an endpoint', () => {
|
|
261
|
+
const Api = new Server({
|
|
262
|
+
name: 'test-Server',
|
|
263
|
+
routeDir: require('path').join(__dirname, '../routes'),
|
|
264
|
+
port: 4000,
|
|
265
|
+
env: 'test'
|
|
266
|
+
});
|
|
267
|
+
const routeFiles = [
|
|
268
|
+
'missing-endpoint.js'
|
|
269
|
+
];
|
|
270
|
+
Api.loadRoutes(routeFiles).catch((error) => {
|
|
271
|
+
assert.contains(error.message, 'missing an endpoint');
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it('should return an error if the input is missing', () => {
|
|
276
|
+
const Api = new Server({
|
|
277
|
+
name: 'test-Server',
|
|
278
|
+
routeDir: require('path').join(__dirname, '../routes'),
|
|
279
|
+
port: 4000,
|
|
280
|
+
env: 'test'
|
|
281
|
+
});
|
|
282
|
+
const routeFiles = [
|
|
283
|
+
'missing-params.js'
|
|
284
|
+
];
|
|
285
|
+
Api.loadRoutes(routeFiles).catch((error) => {
|
|
286
|
+
assert.contains(error.message, 'Missing required input');
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
it('should return an error if the route does not have a method', () => {
|
|
291
|
+
const Api = new Server({
|
|
292
|
+
name: 'test-Server',
|
|
293
|
+
routeDir: require('path').join(__dirname, '../routes'),
|
|
294
|
+
port: 4000,
|
|
295
|
+
env: 'test'
|
|
296
|
+
});
|
|
297
|
+
const routeFiles = [
|
|
298
|
+
'missing-method.js'
|
|
299
|
+
];
|
|
300
|
+
Api.loadRoutes(routeFiles).catch((error) => {
|
|
301
|
+
assert.contains(error.message, 'missing http method');
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
describe('#loadRouteFiles', () => {
|
|
306
|
+
it('should return an error if given a broken directory', () => {
|
|
307
|
+
const Api = new Server({
|
|
308
|
+
name: 'test-Server',
|
|
309
|
+
routeDir: 'missing',
|
|
310
|
+
port: 4000,
|
|
311
|
+
env: 'test'
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
Api.loadRouteFiles().catch((error) => {
|
|
315
|
+
assert.contains(error.message, 'file or directory');
|
|
316
|
+
});
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
describe('#checkConflictingRoutes', () => {
|
|
321
|
+
it('should not call callback if routes array is empty', () => {
|
|
322
|
+
const spy = sinon.spy();
|
|
323
|
+
testApi.checkConflictingRoutes([], spy);
|
|
324
|
+
sinon.assert.notCalled(spy);
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
it('should find conflicting endpoints', () => {
|
|
328
|
+
const spy = sinon.spy();
|
|
329
|
+
const routes = [
|
|
330
|
+
{ method: 'get', endpoint: '/api/user/info' },
|
|
331
|
+
{ method: 'get', endpoint: '/api/user/:id' }
|
|
332
|
+
];
|
|
333
|
+
testApi.checkConflictingRoutes(routes, spy);
|
|
334
|
+
sinon.assert.calledOnce(spy);
|
|
335
|
+
sinon.assert.calledWith(spy, routes[1], routes[0]);
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
it('should find conflicting endpoints with multiple dynamic parameters', () => {
|
|
339
|
+
const spy = sinon.spy();
|
|
340
|
+
const routes = [
|
|
341
|
+
{ method: 'get', endpoint: '/api/user/info/:test' },
|
|
342
|
+
{ method: 'get', endpoint: '/api/user/:id/:test' }
|
|
343
|
+
];
|
|
344
|
+
testApi.checkConflictingRoutes(routes, spy);
|
|
345
|
+
sinon.assert.calledOnce(spy);
|
|
346
|
+
sinon.assert.calledWith(spy, routes[1], routes[0]);
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
it('should not find conflicting endpoints when HTTP verbs are different', () => {
|
|
350
|
+
const spy = sinon.spy();
|
|
351
|
+
const routes = [
|
|
352
|
+
{ method: 'post', endpoint: '/api/user/info' },
|
|
353
|
+
{ method: 'get', endpoint: '/api/user/:id' }
|
|
354
|
+
];
|
|
355
|
+
testApi.checkConflictingRoutes(routes, spy);
|
|
356
|
+
sinon.assert.notCalled(spy);
|
|
357
|
+
});
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
describe('Auth module', () => {
|
|
361
|
+
describe('instantiation', () => {
|
|
362
|
+
it('should throw an error if secretKey is not provided', () => {
|
|
363
|
+
try {
|
|
364
|
+
new Auth();
|
|
365
|
+
assert.fail('Should have thrown an error');
|
|
366
|
+
} catch (e) {
|
|
367
|
+
assert.contains(e.message, 'must be initialized with secretKey');
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
it('should throw an error if secretKey is null', () => {
|
|
372
|
+
try {
|
|
373
|
+
new Auth(null);
|
|
374
|
+
assert.fail('Should have thrown an error');
|
|
375
|
+
} catch (e) {
|
|
376
|
+
assert.contains(e.message, 'must be initialized with secretKey');
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
it('should throw an error if secretKey is undefined', () => {
|
|
381
|
+
try {
|
|
382
|
+
new Auth(undefined);
|
|
383
|
+
assert.fail('Should have thrown an error');
|
|
384
|
+
} catch (e) {
|
|
385
|
+
assert.contains(e.message, 'must be initialized with secretKey');
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
it('should create an instance when secretKey is provided', () => {
|
|
390
|
+
const auth = new Auth('test-secret-key');
|
|
391
|
+
assert.strictEqual(auth.secretKey, 'test-secret-key');
|
|
392
|
+
});
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
describe('generateToken', () => {
|
|
396
|
+
it('should generate a token without requiring secretKey parameter', () => {
|
|
397
|
+
const auth = new Auth('test-secret-key');
|
|
398
|
+
const params = { userId: 'test-user', role: 'admin' };
|
|
399
|
+
const token = auth.generateToken(params);
|
|
400
|
+
assert.strictEqual(typeof token, 'string');
|
|
401
|
+
assert.strictEqual(token.length > 0, true);
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
it('should generate different tokens for different params', () => {
|
|
405
|
+
const auth = new Auth('test-secret-key');
|
|
406
|
+
const token1 = auth.generateToken({ userId: 'user1' });
|
|
407
|
+
const token2 = auth.generateToken({ userId: 'user2' });
|
|
408
|
+
assert.notStrictEqual(token1, token2);
|
|
409
|
+
});
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
describe('verifyToken', () => {
|
|
413
|
+
it('should verify a token without requiring secretKey parameter', () => {
|
|
414
|
+
const auth = new Auth('test-secret-key');
|
|
415
|
+
const params = { userId: 'test-user', role: 'admin' };
|
|
416
|
+
const token = auth.generateToken(params);
|
|
417
|
+
const decoded = auth.verifyToken(token);
|
|
418
|
+
assert.strictEqual(decoded.userId, 'test-user');
|
|
419
|
+
assert.strictEqual(decoded.role, 'admin');
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
it('should throw an error when verifying a token with wrong secretKey', () => {
|
|
423
|
+
const auth1 = new Auth('secret-key-1');
|
|
424
|
+
const auth2 = new Auth('secret-key-2');
|
|
425
|
+
const token = auth1.generateToken({ userId: 'test-user' });
|
|
426
|
+
try {
|
|
427
|
+
auth2.verifyToken(token);
|
|
428
|
+
assert.fail('Should have thrown an error');
|
|
429
|
+
} catch (e) {
|
|
430
|
+
assert.contains(e.message, 'invalid signature');
|
|
431
|
+
}
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
it('should throw an error when verifying an invalid token', () => {
|
|
435
|
+
const auth = new Auth('test-secret-key');
|
|
436
|
+
try {
|
|
437
|
+
auth.verifyToken('invalid-token-string');
|
|
438
|
+
assert.fail('Should have thrown an error');
|
|
439
|
+
} catch (e) {
|
|
440
|
+
assert.strictEqual(typeof e.message, 'string');
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
describe('createCustomValidator', () => {
|
|
446
|
+
it('should throw an error if validator is not a function', () => {
|
|
447
|
+
const auth = new Auth('test-secret-key');
|
|
448
|
+
try {
|
|
449
|
+
auth.createCustomValidator('not-a-function');
|
|
450
|
+
assert.fail('Should have thrown an error');
|
|
451
|
+
} catch (e) {
|
|
452
|
+
assert.contains(e.message, 'Custom validator must be a function');
|
|
453
|
+
}
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
it('should return the validator function if valid', () => {
|
|
457
|
+
const auth = new Auth('test-secret-key');
|
|
458
|
+
const validatorFn = () => true;
|
|
459
|
+
const result = auth.createCustomValidator(validatorFn);
|
|
460
|
+
assert.strictEqual(result, validatorFn);
|
|
461
|
+
});
|
|
462
|
+
});
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
describe('Server Auth integration', () => {
|
|
466
|
+
it('should create auth instance when secretKey is provided', () => {
|
|
467
|
+
const server = new Server({
|
|
468
|
+
name: 'test-server',
|
|
469
|
+
routeDir: './test-routes',
|
|
470
|
+
port: 4000,
|
|
471
|
+
env: 'test',
|
|
472
|
+
secretKey: 'test-secret-key',
|
|
473
|
+
services: {}
|
|
474
|
+
});
|
|
475
|
+
assert.strictEqual(server.auth !== null, true);
|
|
476
|
+
assert.strictEqual(server.auth.secretKey, 'test-secret-key');
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
it('should not create auth instance when secretKey is not provided', () => {
|
|
480
|
+
const server = new Server({
|
|
481
|
+
name: 'test-server',
|
|
482
|
+
routeDir: './test-routes',
|
|
483
|
+
port: 4000,
|
|
484
|
+
env: 'test',
|
|
485
|
+
services: {}
|
|
486
|
+
});
|
|
487
|
+
assert.strictEqual(server.auth, null);
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
it('should not create auth instance when secretKey is null', () => {
|
|
491
|
+
const server = new Server({
|
|
492
|
+
name: 'test-server',
|
|
493
|
+
routeDir: './test-routes',
|
|
494
|
+
port: 4000,
|
|
495
|
+
env: 'test',
|
|
496
|
+
secretKey: null,
|
|
497
|
+
services: {}
|
|
498
|
+
});
|
|
499
|
+
assert.strictEqual(server.auth, null);
|
|
500
|
+
});
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
describe('rawBody functionality', () => {
|
|
504
|
+
it('should set rawBody as Buffer when route.rawBody is true', async () => {
|
|
505
|
+
const Api = new Server({
|
|
506
|
+
name: 'test-Server',
|
|
507
|
+
routeDir: './test-routes',
|
|
508
|
+
port: 4000,
|
|
509
|
+
env: 'test',
|
|
510
|
+
services: {}
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
const testBody = Buffer.from(JSON.stringify({ test: 'data' }));
|
|
514
|
+
const req = {
|
|
515
|
+
body: testBody,
|
|
516
|
+
query: {},
|
|
517
|
+
params: {},
|
|
518
|
+
files: {},
|
|
519
|
+
headers: {},
|
|
520
|
+
get: (header) => {
|
|
521
|
+
if (header === 'origin') return 'http://localhost:4000';
|
|
522
|
+
if (header === 'host') return 'localhost:4000';
|
|
523
|
+
return null;
|
|
524
|
+
}
|
|
525
|
+
};
|
|
526
|
+
const res = {
|
|
527
|
+
status: (code) => {
|
|
528
|
+
res.statusCode = code;
|
|
529
|
+
return res;
|
|
530
|
+
},
|
|
531
|
+
send: (data) => {
|
|
532
|
+
res.sentData = data;
|
|
533
|
+
}
|
|
534
|
+
};
|
|
535
|
+
|
|
536
|
+
const route = {
|
|
537
|
+
name: 'test-raw-body',
|
|
538
|
+
method: 'post',
|
|
539
|
+
endpoint: '/test',
|
|
540
|
+
rawBody: true,
|
|
541
|
+
run: (services, inputs, next) => {
|
|
542
|
+
assert.strictEqual(Buffer.isBuffer(inputs.rawBody), true);
|
|
543
|
+
assert.deepStrictEqual(inputs.rawBody, testBody);
|
|
544
|
+
next(200, { success: true });
|
|
545
|
+
}
|
|
546
|
+
};
|
|
547
|
+
|
|
548
|
+
await Api.preprocessor(route, req, res);
|
|
549
|
+
assert.strictEqual(res.statusCode, 200);
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
it('should not set rawBody when route.rawBody is false', async () => {
|
|
553
|
+
const Api = new Server({
|
|
554
|
+
name: 'test-Server',
|
|
555
|
+
routeDir: './test-routes',
|
|
556
|
+
port: 4000,
|
|
557
|
+
env: 'test',
|
|
558
|
+
services: {}
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
const testBody = { test: 'data' };
|
|
562
|
+
const req = {
|
|
563
|
+
body: testBody,
|
|
564
|
+
query: {},
|
|
565
|
+
params: {},
|
|
566
|
+
files: {},
|
|
567
|
+
headers: {},
|
|
568
|
+
get: (header) => {
|
|
569
|
+
if (header === 'origin') return 'http://localhost:4000';
|
|
570
|
+
if (header === 'host') return 'localhost:4000';
|
|
571
|
+
return null;
|
|
572
|
+
}
|
|
573
|
+
};
|
|
574
|
+
const res = {
|
|
575
|
+
status: (code) => {
|
|
576
|
+
res.statusCode = code;
|
|
577
|
+
return res;
|
|
578
|
+
},
|
|
579
|
+
send: (data) => {
|
|
580
|
+
res.sentData = data;
|
|
581
|
+
}
|
|
582
|
+
};
|
|
583
|
+
|
|
584
|
+
const route = {
|
|
585
|
+
name: 'test-no-raw-body',
|
|
586
|
+
method: 'post',
|
|
587
|
+
endpoint: '/test',
|
|
588
|
+
rawBody: false,
|
|
589
|
+
run: (services, inputs, next) => {
|
|
590
|
+
assert.strictEqual(inputs.rawBody, undefined);
|
|
591
|
+
next(200, { success: true });
|
|
592
|
+
}
|
|
593
|
+
};
|
|
594
|
+
|
|
595
|
+
await Api.preprocessor(route, req, res);
|
|
596
|
+
assert.strictEqual(res.statusCode, 200);
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
it('should process inputs alongside rawBody when both are present', async () => {
|
|
600
|
+
const Api = new Server({
|
|
601
|
+
name: 'test-Server',
|
|
602
|
+
routeDir: './test-routes',
|
|
603
|
+
port: 4000,
|
|
604
|
+
env: 'test',
|
|
605
|
+
services: {}
|
|
606
|
+
});
|
|
607
|
+
|
|
608
|
+
const testBody = Buffer.from(JSON.stringify({ test: 'data' }));
|
|
609
|
+
const req = {
|
|
610
|
+
body: testBody,
|
|
611
|
+
query: { param1: 'value1' },
|
|
612
|
+
params: { id: '123' },
|
|
613
|
+
files: {},
|
|
614
|
+
headers: {},
|
|
615
|
+
get: (header) => {
|
|
616
|
+
if (header === 'origin') return 'http://localhost:4000';
|
|
617
|
+
if (header === 'host') return 'localhost:4000';
|
|
618
|
+
return null;
|
|
619
|
+
}
|
|
620
|
+
};
|
|
621
|
+
const res = {
|
|
622
|
+
status: (code) => {
|
|
623
|
+
res.statusCode = code;
|
|
624
|
+
return res;
|
|
625
|
+
},
|
|
626
|
+
send: (data) => {
|
|
627
|
+
res.sentData = data;
|
|
628
|
+
}
|
|
629
|
+
};
|
|
630
|
+
|
|
631
|
+
const route = {
|
|
632
|
+
name: 'test-raw-body-with-inputs',
|
|
633
|
+
method: 'post',
|
|
634
|
+
endpoint: '/test/:id',
|
|
635
|
+
rawBody: true,
|
|
636
|
+
inputs: {
|
|
637
|
+
param1: {
|
|
638
|
+
required: true
|
|
639
|
+
},
|
|
640
|
+
id: {
|
|
641
|
+
required: true
|
|
642
|
+
}
|
|
643
|
+
},
|
|
644
|
+
run: (services, inputs, next) => {
|
|
645
|
+
assert.strictEqual(Buffer.isBuffer(inputs.rawBody), true);
|
|
646
|
+
assert.deepStrictEqual(inputs.rawBody, testBody);
|
|
647
|
+
assert.strictEqual(inputs.param1, 'value1');
|
|
648
|
+
assert.strictEqual(inputs.id, '123');
|
|
649
|
+
next(200, { success: true });
|
|
650
|
+
}
|
|
651
|
+
};
|
|
652
|
+
|
|
653
|
+
await Api.preprocessor(route, req, res);
|
|
654
|
+
assert.strictEqual(res.statusCode, 200);
|
|
655
|
+
});
|
|
656
|
+
|
|
657
|
+
it('should handle rawBody without inputs', async () => {
|
|
658
|
+
const Api = new Server({
|
|
659
|
+
name: 'test-Server',
|
|
660
|
+
routeDir: './test-routes',
|
|
661
|
+
port: 4000,
|
|
662
|
+
env: 'test',
|
|
663
|
+
services: {}
|
|
664
|
+
});
|
|
665
|
+
|
|
666
|
+
const testBody = Buffer.from('raw string data');
|
|
667
|
+
const req = {
|
|
668
|
+
body: testBody,
|
|
669
|
+
query: {},
|
|
670
|
+
params: {},
|
|
671
|
+
files: {},
|
|
672
|
+
headers: {},
|
|
673
|
+
get: (header) => {
|
|
674
|
+
if (header === 'origin') return 'http://localhost:4000';
|
|
675
|
+
if (header === 'host') return 'localhost:4000';
|
|
676
|
+
return null;
|
|
677
|
+
}
|
|
678
|
+
};
|
|
679
|
+
const res = {
|
|
680
|
+
status: (code) => {
|
|
681
|
+
res.statusCode = code;
|
|
682
|
+
return res;
|
|
683
|
+
},
|
|
684
|
+
send: (data) => {
|
|
685
|
+
res.sentData = data;
|
|
686
|
+
}
|
|
687
|
+
};
|
|
688
|
+
|
|
689
|
+
const route = {
|
|
690
|
+
name: 'test-raw-body-only',
|
|
691
|
+
method: 'post',
|
|
692
|
+
endpoint: '/test',
|
|
693
|
+
rawBody: true,
|
|
694
|
+
run: (services, inputs, next) => {
|
|
695
|
+
assert.strictEqual(Buffer.isBuffer(inputs.rawBody), true);
|
|
696
|
+
assert.deepStrictEqual(inputs.rawBody, testBody);
|
|
697
|
+
next(200, { success: true });
|
|
698
|
+
}
|
|
699
|
+
};
|
|
700
|
+
|
|
701
|
+
await Api.preprocessor(route, req, res);
|
|
702
|
+
assert.strictEqual(res.statusCode, 200);
|
|
703
|
+
});
|
|
704
|
+
|
|
705
|
+
it('should not include Buffer body in inputs processing when rawBody is true', async () => {
|
|
706
|
+
const Api = new Server({
|
|
707
|
+
name: 'test-Server',
|
|
708
|
+
routeDir: './test-routes',
|
|
709
|
+
port: 4000,
|
|
710
|
+
env: 'test',
|
|
711
|
+
services: {}
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
const testBody = Buffer.from(JSON.stringify({ test: 'data', shouldNotBeParsed: true }));
|
|
715
|
+
const req = {
|
|
716
|
+
body: testBody,
|
|
717
|
+
query: { param1: 'value1' },
|
|
718
|
+
params: { id: '123' },
|
|
719
|
+
files: {},
|
|
720
|
+
headers: {},
|
|
721
|
+
get: (header) => {
|
|
722
|
+
if (header === 'origin') return 'http://localhost:4000';
|
|
723
|
+
if (header === 'host') return 'localhost:4000';
|
|
724
|
+
return null;
|
|
725
|
+
}
|
|
726
|
+
};
|
|
727
|
+
const res = {
|
|
728
|
+
status: (code) => {
|
|
729
|
+
res.statusCode = code;
|
|
730
|
+
return res;
|
|
731
|
+
},
|
|
732
|
+
send: (data) => {
|
|
733
|
+
res.sentData = data;
|
|
734
|
+
}
|
|
735
|
+
};
|
|
736
|
+
|
|
737
|
+
const route = {
|
|
738
|
+
name: 'test-raw-body-excluded-from-inputs',
|
|
739
|
+
method: 'post',
|
|
740
|
+
endpoint: '/test/:id',
|
|
741
|
+
rawBody: true,
|
|
742
|
+
inputs: {
|
|
743
|
+
param1: {
|
|
744
|
+
required: true
|
|
745
|
+
},
|
|
746
|
+
id: {
|
|
747
|
+
required: true
|
|
748
|
+
},
|
|
749
|
+
// This should NOT be found in inputs even though it's in the Buffer body
|
|
750
|
+
test: {
|
|
751
|
+
required: false
|
|
752
|
+
},
|
|
753
|
+
shouldNotBeParsed: {
|
|
754
|
+
required: false
|
|
755
|
+
}
|
|
756
|
+
},
|
|
757
|
+
run: (services, inputs, next) => {
|
|
758
|
+
// Verify rawBody is still a Buffer
|
|
759
|
+
assert.strictEqual(Buffer.isBuffer(inputs.rawBody), true);
|
|
760
|
+
assert.deepStrictEqual(inputs.rawBody, testBody);
|
|
761
|
+
|
|
762
|
+
// Verify inputs from query/params work
|
|
763
|
+
assert.strictEqual(inputs.param1, 'value1');
|
|
764
|
+
assert.strictEqual(inputs.id, '123');
|
|
765
|
+
|
|
766
|
+
// Verify that parsed body fields are NOT in inputs (because Buffer was excluded)
|
|
767
|
+
// buildParameters sets missing optional params to empty string, not undefined
|
|
768
|
+
assert.strictEqual(inputs.test, "");
|
|
769
|
+
assert.strictEqual(inputs.shouldNotBeParsed, "");
|
|
770
|
+
|
|
771
|
+
next(200, { success: true });
|
|
772
|
+
}
|
|
773
|
+
};
|
|
774
|
+
|
|
775
|
+
await Api.preprocessor(route, req, res);
|
|
776
|
+
assert.strictEqual(res.statusCode, 200);
|
|
777
|
+
});
|
|
778
|
+
});
|
|
779
|
+
});
|