fastify 4.6.0 → 4.7.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/docs/Guides/Ecosystem.md +19 -8
- package/docs/Guides/Plugins-Guide.md +44 -0
- package/docs/Reference/Reply.md +5 -5
- package/docs/Reference/Request.md +19 -6
- package/docs/Reference/Routes.md +2 -2
- package/docs/Reference/Type-Providers.md +3 -3
- package/fastify.js +1 -1
- package/lib/contentTypeParser.js +10 -9
- package/lib/context.js +27 -3
- package/lib/error-handler.js +7 -3
- package/lib/handleRequest.js +15 -13
- package/lib/reply.js +42 -34
- package/lib/request.js +36 -18
- package/lib/route.js +16 -10
- package/lib/schema-controller.js +7 -1
- package/lib/server.js +2 -2
- package/lib/symbols.js +2 -0
- package/lib/validation.js +4 -3
- package/lib/warnings.js +2 -0
- package/package.json +26 -26
- package/test/404s.test.js +19 -1
- package/test/context-config.test.js +2 -1
- package/test/handler-context.test.js +12 -30
- package/test/internals/contentTypeParser.test.js +3 -3
- package/test/internals/handleRequest.test.js +4 -3
- package/test/internals/reply-serialize.test.js +9 -9
- package/test/internals/reply.test.js +10 -9
- package/test/internals/request-validate.test.js +97 -9
- package/test/internals/request.test.js +57 -3
- package/test/internals/validation.test.js +15 -0
- package/test/plugin.test.js +1 -1
- package/test/reply-code.test.js +59 -0
- package/test/route.test.js +29 -0
- package/test/router-options.test.js +33 -66
- package/test/schema-feature.test.js +25 -0
- package/test/schema-validation.test.js +19 -0
- package/test/search.test.js +169 -30
- package/test/server.test.js +54 -0
- package/types/request.d.ts +9 -3
|
@@ -1,19 +1,9 @@
|
|
|
1
1
|
'use strict'
|
|
2
|
-
|
|
3
|
-
const http = require('http')
|
|
4
2
|
const test = require('tap').test
|
|
3
|
+
const { kRouteContext } = require('../lib/symbols')
|
|
5
4
|
const fastify = require('../')
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
const { address, port } = app.server.address()
|
|
9
|
-
if (address === '::1') {
|
|
10
|
-
return `http://[${address}]:${port}`
|
|
11
|
-
} else {
|
|
12
|
-
return `http://${address}:${port}`
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
test('handlers receive correct `this` context', (t) => {
|
|
6
|
+
test('handlers receive correct `this` context', async (t) => {
|
|
17
7
|
t.plan(4)
|
|
18
8
|
|
|
19
9
|
// simulate plugin that uses fastify-plugin
|
|
@@ -32,32 +22,24 @@ test('handlers receive correct `this` context', (t) => {
|
|
|
32
22
|
reply.send()
|
|
33
23
|
})
|
|
34
24
|
|
|
35
|
-
instance.
|
|
36
|
-
instance.server.unref()
|
|
37
|
-
if (err) t.threw(err)
|
|
38
|
-
t.ok(instance.foo)
|
|
39
|
-
t.equal(instance.foo, 'foo')
|
|
25
|
+
await instance.inject('/')
|
|
40
26
|
|
|
41
|
-
|
|
42
|
-
|
|
27
|
+
t.ok(instance.foo)
|
|
28
|
+
t.equal(instance.foo, 'foo')
|
|
43
29
|
})
|
|
44
30
|
|
|
45
|
-
test('handlers have access to the internal context', (t) => {
|
|
31
|
+
test('handlers have access to the internal context', async (t) => {
|
|
46
32
|
t.plan(5)
|
|
47
33
|
|
|
48
34
|
const instance = fastify()
|
|
49
35
|
instance.get('/', { config: { foo: 'bar' } }, function (req, reply) {
|
|
50
|
-
t.ok(reply
|
|
51
|
-
t.ok(reply.
|
|
52
|
-
t.type(reply.
|
|
53
|
-
t.ok(reply.
|
|
54
|
-
t.equal(reply.
|
|
36
|
+
t.ok(reply[kRouteContext])
|
|
37
|
+
t.ok(reply[kRouteContext].config)
|
|
38
|
+
t.type(reply[kRouteContext].config, Object)
|
|
39
|
+
t.ok(reply[kRouteContext].config.foo)
|
|
40
|
+
t.equal(reply[kRouteContext].config.foo, 'bar')
|
|
55
41
|
reply.send()
|
|
56
42
|
})
|
|
57
43
|
|
|
58
|
-
instance.
|
|
59
|
-
instance.server.unref()
|
|
60
|
-
if (err) t.threw(err)
|
|
61
|
-
http.get(getUrl(instance), () => {}).on('error', t.threw)
|
|
62
|
-
})
|
|
44
|
+
await instance.inject('/')
|
|
63
45
|
})
|
|
@@ -4,7 +4,7 @@ const t = require('tap')
|
|
|
4
4
|
const proxyquire = require('proxyquire')
|
|
5
5
|
const test = t.test
|
|
6
6
|
const { Readable } = require('stream')
|
|
7
|
-
const { kTestInternals } = require('../../lib/symbols')
|
|
7
|
+
const { kTestInternals, kRouteContext } = require('../../lib/symbols')
|
|
8
8
|
const Request = require('../../lib/request')
|
|
9
9
|
const Reply = require('../../lib/reply')
|
|
10
10
|
|
|
@@ -50,7 +50,7 @@ test('rawBody function', t => {
|
|
|
50
50
|
internals.rawBody(
|
|
51
51
|
request,
|
|
52
52
|
reply,
|
|
53
|
-
reply.
|
|
53
|
+
reply[kRouteContext]._parserOptions,
|
|
54
54
|
parser,
|
|
55
55
|
done
|
|
56
56
|
)
|
|
@@ -103,7 +103,7 @@ test('Should support Webpack and faux modules', t => {
|
|
|
103
103
|
internals.rawBody(
|
|
104
104
|
request,
|
|
105
105
|
reply,
|
|
106
|
-
reply.
|
|
106
|
+
reply[kRouteContext]._parserOptions,
|
|
107
107
|
parser,
|
|
108
108
|
done
|
|
109
109
|
)
|
|
@@ -5,6 +5,7 @@ const handleRequest = require('../../lib/handleRequest')
|
|
|
5
5
|
const internals = require('../../lib/handleRequest')[Symbol.for('internals')]
|
|
6
6
|
const Request = require('../../lib/request')
|
|
7
7
|
const Reply = require('../../lib/reply')
|
|
8
|
+
const { kRouteContext } = require('../../lib/symbols')
|
|
8
9
|
const buildSchema = require('../../lib/validation').compileSchemasForValidation
|
|
9
10
|
const sget = require('simple-get').concat
|
|
10
11
|
|
|
@@ -69,7 +70,7 @@ test('handler function - invalid schema', t => {
|
|
|
69
70
|
buildSchema(context, schemaValidator)
|
|
70
71
|
const request = {
|
|
71
72
|
body: { hello: 'world' },
|
|
72
|
-
context
|
|
73
|
+
[kRouteContext]: context
|
|
73
74
|
}
|
|
74
75
|
internals.handler(request, new Reply(res, request))
|
|
75
76
|
})
|
|
@@ -96,7 +97,7 @@ test('handler function - reply', t => {
|
|
|
96
97
|
onError: []
|
|
97
98
|
}
|
|
98
99
|
buildSchema(context, schemaValidator)
|
|
99
|
-
internals.handler({}, new Reply(res, { context }))
|
|
100
|
+
internals.handler({ [kRouteContext]: context }, new Reply(res, { [kRouteContext]: context }))
|
|
100
101
|
})
|
|
101
102
|
|
|
102
103
|
test('handler function - preValidationCallback with finished response', t => {
|
|
@@ -121,7 +122,7 @@ test('handler function - preValidationCallback with finished response', t => {
|
|
|
121
122
|
onError: []
|
|
122
123
|
}
|
|
123
124
|
buildSchema(context, schemaValidator)
|
|
124
|
-
internals.handler({}, new Reply(res, { context }))
|
|
125
|
+
internals.handler({ [kRouteContext]: context }, new Reply(res, { [kRouteContext]: context }))
|
|
125
126
|
})
|
|
126
127
|
|
|
127
128
|
test('request should be defined in onSend Hook on post request with content type application/json', t => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { test } = require('tap')
|
|
4
|
-
const { kReplySerializeWeakMap } = require('../../lib/symbols')
|
|
4
|
+
const { kReplySerializeWeakMap, kRouteContext } = require('../../lib/symbols')
|
|
5
5
|
const Fastify = require('../../fastify')
|
|
6
6
|
|
|
7
7
|
function getDefaultSchema () {
|
|
@@ -172,9 +172,9 @@ test('Reply#compileSerializationSchema', t => {
|
|
|
172
172
|
fastify.get('/', (req, reply) => {
|
|
173
173
|
const input = { hello: 'world' }
|
|
174
174
|
|
|
175
|
-
t.equal(reply
|
|
175
|
+
t.equal(reply[kRouteContext][kReplySerializeWeakMap], null)
|
|
176
176
|
t.equal(reply.compileSerializationSchema(getDefaultSchema())(input), JSON.stringify(input))
|
|
177
|
-
t.type(reply
|
|
177
|
+
t.type(reply[kRouteContext][kReplySerializeWeakMap], WeakMap)
|
|
178
178
|
t.equal(reply.compileSerializationSchema(getDefaultSchema())(input), JSON.stringify(input))
|
|
179
179
|
|
|
180
180
|
reply.send({ hello: 'world' })
|
|
@@ -231,7 +231,7 @@ test('Reply#getSerializationFunction', t => {
|
|
|
231
231
|
(req, reply) => {
|
|
232
232
|
const { id } = req.params
|
|
233
233
|
|
|
234
|
-
if (
|
|
234
|
+
if (Number(id) === 1) {
|
|
235
235
|
const serialize4xx = reply.getSerializationFunction('4xx')
|
|
236
236
|
const serialize201 = reply.getSerializationFunction(201)
|
|
237
237
|
const serializeUndefined = reply.getSerializationFunction(undefined)
|
|
@@ -308,7 +308,7 @@ test('Reply#getSerializationFunction', t => {
|
|
|
308
308
|
(req, reply) => {
|
|
309
309
|
const { id } = req.params
|
|
310
310
|
|
|
311
|
-
if (
|
|
311
|
+
if (Number(id) === 1) {
|
|
312
312
|
const serialize = reply.compileSerializationSchema(schemaObj)
|
|
313
313
|
|
|
314
314
|
t.type(serialize, Function)
|
|
@@ -352,9 +352,9 @@ test('Reply#getSerializationFunction', t => {
|
|
|
352
352
|
|
|
353
353
|
fastify.get('/', (req, reply) => {
|
|
354
354
|
t.notOk(reply.getSerializationFunction(getDefaultSchema()))
|
|
355
|
-
t.equal(reply
|
|
355
|
+
t.equal(reply[kRouteContext][kReplySerializeWeakMap], null)
|
|
356
356
|
t.notOk(reply.getSerializationFunction('200'))
|
|
357
|
-
t.equal(reply
|
|
357
|
+
t.equal(reply[kRouteContext][kReplySerializeWeakMap], null)
|
|
358
358
|
|
|
359
359
|
reply.send({ hello: 'world' })
|
|
360
360
|
})
|
|
@@ -568,9 +568,9 @@ test('Reply#serializeInput', t => {
|
|
|
568
568
|
|
|
569
569
|
fastify.get('/', (req, reply) => {
|
|
570
570
|
const input = { hello: 'world' }
|
|
571
|
-
t.equal(reply
|
|
571
|
+
t.equal(reply[kRouteContext][kReplySerializeWeakMap], null)
|
|
572
572
|
t.equal(reply.serializeInput(input, getDefaultSchema()), JSON.stringify(input))
|
|
573
|
-
t.type(reply
|
|
573
|
+
t.type(reply[kRouteContext][kReplySerializeWeakMap], WeakMap)
|
|
574
574
|
|
|
575
575
|
reply.send({ hello: 'world' })
|
|
576
576
|
})
|
|
@@ -12,7 +12,8 @@ const {
|
|
|
12
12
|
kReplyHeaders,
|
|
13
13
|
kReplySerializer,
|
|
14
14
|
kReplyIsError,
|
|
15
|
-
kReplySerializerDefault
|
|
15
|
+
kReplySerializerDefault,
|
|
16
|
+
kRouteContext
|
|
16
17
|
} = require('../../lib/symbols')
|
|
17
18
|
const fs = require('fs')
|
|
18
19
|
const path = require('path')
|
|
@@ -34,7 +35,7 @@ test('Once called, Reply should return an object with methods', t => {
|
|
|
34
35
|
t.plan(13)
|
|
35
36
|
const response = { res: 'res' }
|
|
36
37
|
const context = {}
|
|
37
|
-
const request = { context }
|
|
38
|
+
const request = { [kRouteContext]: context }
|
|
38
39
|
const reply = new Reply(response, request)
|
|
39
40
|
t.equal(typeof reply, 'object')
|
|
40
41
|
t.equal(typeof reply[kReplyIsError], 'boolean')
|
|
@@ -47,7 +48,7 @@ test('Once called, Reply should return an object with methods', t => {
|
|
|
47
48
|
t.equal(typeof reply.getResponseTime, 'function')
|
|
48
49
|
t.equal(typeof reply[kReplyHeaders], 'object')
|
|
49
50
|
t.same(reply.raw, response)
|
|
50
|
-
t.equal(reply
|
|
51
|
+
t.equal(reply[kRouteContext], context)
|
|
51
52
|
t.equal(reply.request, request)
|
|
52
53
|
})
|
|
53
54
|
|
|
@@ -76,7 +77,7 @@ test('reply.send will logStream error and destroy the stream', t => {
|
|
|
76
77
|
warn: () => {}
|
|
77
78
|
}
|
|
78
79
|
|
|
79
|
-
const reply = new Reply(response, {
|
|
80
|
+
const reply = new Reply(response, { [kRouteContext]: { onSend: null } }, log)
|
|
80
81
|
reply.send(payload)
|
|
81
82
|
payload.destroy(new Error('stream error'))
|
|
82
83
|
|
|
@@ -93,7 +94,7 @@ test('reply.send throw with circular JSON', t => {
|
|
|
93
94
|
write: () => {},
|
|
94
95
|
end: () => {}
|
|
95
96
|
}
|
|
96
|
-
const reply = new Reply(response, {
|
|
97
|
+
const reply = new Reply(response, { [kRouteContext]: { onSend: [] } })
|
|
97
98
|
t.throws(() => {
|
|
98
99
|
const obj = {}
|
|
99
100
|
obj.obj = obj
|
|
@@ -111,7 +112,7 @@ test('reply.send returns itself', t => {
|
|
|
111
112
|
write: () => {},
|
|
112
113
|
end: () => {}
|
|
113
114
|
}
|
|
114
|
-
const reply = new Reply(response, {
|
|
115
|
+
const reply = new Reply(response, { [kRouteContext]: { onSend: [] } })
|
|
115
116
|
t.equal(reply.send('hello'), reply)
|
|
116
117
|
})
|
|
117
118
|
|
|
@@ -152,7 +153,7 @@ test('reply.serialize should serialize payload', t => {
|
|
|
152
153
|
t.plan(1)
|
|
153
154
|
const response = { statusCode: 200 }
|
|
154
155
|
const context = {}
|
|
155
|
-
const reply = new Reply(response, { context })
|
|
156
|
+
const reply = new Reply(response, { [kRouteContext]: context })
|
|
156
157
|
t.equal(reply.serialize({ foo: 'bar' }), '{"foo":"bar"}')
|
|
157
158
|
})
|
|
158
159
|
|
|
@@ -161,7 +162,7 @@ test('reply.serialize should serialize payload with a custom serializer', t => {
|
|
|
161
162
|
let customSerializerCalled = false
|
|
162
163
|
const response = { statusCode: 200 }
|
|
163
164
|
const context = {}
|
|
164
|
-
const reply = new Reply(response, { context })
|
|
165
|
+
const reply = new Reply(response, { [kRouteContext]: context })
|
|
165
166
|
reply.serializer((x) => (customSerializerCalled = true) && JSON.stringify(x))
|
|
166
167
|
t.equal(reply.serialize({ foo: 'bar' }), '{"foo":"bar"}')
|
|
167
168
|
t.equal(customSerializerCalled, true, 'custom serializer not called')
|
|
@@ -172,7 +173,7 @@ test('reply.serialize should serialize payload with a context default serializer
|
|
|
172
173
|
let customSerializerCalled = false
|
|
173
174
|
const response = { statusCode: 200 }
|
|
174
175
|
const context = { [kReplySerializerDefault]: (x) => (customSerializerCalled = true) && JSON.stringify(x) }
|
|
175
|
-
const reply = new Reply(response, { context })
|
|
176
|
+
const reply = new Reply(response, { [kRouteContext]: context })
|
|
176
177
|
t.equal(reply.serialize({ foo: 'bar' }), '{"foo":"bar"}')
|
|
177
178
|
t.equal(customSerializerCalled, true, 'custom serializer not called')
|
|
178
179
|
})
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { test } = require('tap')
|
|
4
4
|
const Ajv = require('ajv')
|
|
5
|
-
const { kRequestValidateWeakMap } = require('../../lib/symbols')
|
|
5
|
+
const { kRequestValidateWeakMap, kRouteContext } = require('../../lib/symbols')
|
|
6
6
|
const Fastify = require('../../fastify')
|
|
7
7
|
|
|
8
8
|
const defaultSchema = {
|
|
@@ -36,7 +36,7 @@ const requestSchema = {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
test('#compileValidationSchema', subtest => {
|
|
39
|
-
subtest.plan(
|
|
39
|
+
subtest.plan(7)
|
|
40
40
|
|
|
41
41
|
subtest.test('Should return a function - Route without schema', async t => {
|
|
42
42
|
const fastify = Fastify()
|
|
@@ -59,6 +59,49 @@ test('#compileValidationSchema', subtest => {
|
|
|
59
59
|
})
|
|
60
60
|
})
|
|
61
61
|
|
|
62
|
+
subtest.test('Validate function errors property should be null after validation when input is valid', async t => {
|
|
63
|
+
const fastify = Fastify()
|
|
64
|
+
|
|
65
|
+
t.plan(3)
|
|
66
|
+
|
|
67
|
+
fastify.get('/', (req, reply) => {
|
|
68
|
+
const validate = req.compileValidationSchema(defaultSchema)
|
|
69
|
+
|
|
70
|
+
t.ok(validate({ hello: 'world' }))
|
|
71
|
+
t.ok(Object.prototype.hasOwnProperty.call(validate, 'errors'))
|
|
72
|
+
t.equal(validate.errors, null)
|
|
73
|
+
|
|
74
|
+
reply.send({ hello: 'world' })
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
await fastify.inject({
|
|
78
|
+
path: '/',
|
|
79
|
+
method: 'GET'
|
|
80
|
+
})
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
subtest.test('Validate function errors property should be an array of errors after validation when input is valid', async t => {
|
|
84
|
+
const fastify = Fastify()
|
|
85
|
+
|
|
86
|
+
t.plan(4)
|
|
87
|
+
|
|
88
|
+
fastify.get('/', (req, reply) => {
|
|
89
|
+
const validate = req.compileValidationSchema(defaultSchema)
|
|
90
|
+
|
|
91
|
+
t.notOk(validate({ world: 'foo' }))
|
|
92
|
+
t.ok(Object.prototype.hasOwnProperty.call(validate, 'errors'))
|
|
93
|
+
t.ok(Array.isArray(validate.errors))
|
|
94
|
+
t.ok(validate.errors.length > 0)
|
|
95
|
+
|
|
96
|
+
reply.send({ hello: 'world' })
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
await fastify.inject({
|
|
100
|
+
path: '/',
|
|
101
|
+
method: 'GET'
|
|
102
|
+
})
|
|
103
|
+
})
|
|
104
|
+
|
|
62
105
|
subtest.test(
|
|
63
106
|
'Should reuse the validate fn across multiple invocations - Route without schema',
|
|
64
107
|
async t => {
|
|
@@ -188,11 +231,11 @@ test('#compileValidationSchema', subtest => {
|
|
|
188
231
|
t.plan(5)
|
|
189
232
|
|
|
190
233
|
fastify.get('/', (req, reply) => {
|
|
191
|
-
t.equal(req
|
|
234
|
+
t.equal(req[kRouteContext][kRequestValidateWeakMap], null)
|
|
192
235
|
t.type(req.compileValidationSchema(defaultSchema), Function)
|
|
193
|
-
t.type(req
|
|
236
|
+
t.type(req[kRouteContext][kRequestValidateWeakMap], WeakMap)
|
|
194
237
|
t.type(req.compileValidationSchema(Object.assign({}, defaultSchema)), Function)
|
|
195
|
-
t.type(req
|
|
238
|
+
t.type(req[kRouteContext][kRequestValidateWeakMap], WeakMap)
|
|
196
239
|
|
|
197
240
|
reply.send({ hello: 'world' })
|
|
198
241
|
})
|
|
@@ -206,7 +249,7 @@ test('#compileValidationSchema', subtest => {
|
|
|
206
249
|
})
|
|
207
250
|
|
|
208
251
|
test('#getValidationFunction', subtest => {
|
|
209
|
-
subtest.plan(
|
|
252
|
+
subtest.plan(6)
|
|
210
253
|
|
|
211
254
|
subtest.test('Should return a validation function', async t => {
|
|
212
255
|
const fastify = Fastify()
|
|
@@ -228,6 +271,51 @@ test('#getValidationFunction', subtest => {
|
|
|
228
271
|
})
|
|
229
272
|
})
|
|
230
273
|
|
|
274
|
+
subtest.test('Validate function errors property should be null after validation when input is valid', async t => {
|
|
275
|
+
const fastify = Fastify()
|
|
276
|
+
|
|
277
|
+
t.plan(3)
|
|
278
|
+
|
|
279
|
+
fastify.get('/', (req, reply) => {
|
|
280
|
+
req.compileValidationSchema(defaultSchema)
|
|
281
|
+
const validate = req.getValidationFunction(defaultSchema)
|
|
282
|
+
|
|
283
|
+
t.ok(validate({ hello: 'world' }))
|
|
284
|
+
t.ok(Object.prototype.hasOwnProperty.call(validate, 'errors'))
|
|
285
|
+
t.equal(validate.errors, null)
|
|
286
|
+
|
|
287
|
+
reply.send({ hello: 'world' })
|
|
288
|
+
})
|
|
289
|
+
|
|
290
|
+
await fastify.inject({
|
|
291
|
+
path: '/',
|
|
292
|
+
method: 'GET'
|
|
293
|
+
})
|
|
294
|
+
})
|
|
295
|
+
|
|
296
|
+
subtest.test('Validate function errors property should be an array of errors after validation when input is valid', async t => {
|
|
297
|
+
const fastify = Fastify()
|
|
298
|
+
|
|
299
|
+
t.plan(4)
|
|
300
|
+
|
|
301
|
+
fastify.get('/', (req, reply) => {
|
|
302
|
+
req.compileValidationSchema(defaultSchema)
|
|
303
|
+
const validate = req.getValidationFunction(defaultSchema)
|
|
304
|
+
|
|
305
|
+
t.notOk(validate({ world: 'foo' }))
|
|
306
|
+
t.ok(Object.prototype.hasOwnProperty.call(validate, 'errors'))
|
|
307
|
+
t.ok(Array.isArray(validate.errors))
|
|
308
|
+
t.ok(validate.errors.length > 0)
|
|
309
|
+
|
|
310
|
+
reply.send({ hello: 'world' })
|
|
311
|
+
})
|
|
312
|
+
|
|
313
|
+
await fastify.inject({
|
|
314
|
+
path: '/',
|
|
315
|
+
method: 'GET'
|
|
316
|
+
})
|
|
317
|
+
})
|
|
318
|
+
|
|
231
319
|
subtest.test('Should return undefined if no schema compiled', async t => {
|
|
232
320
|
const fastify = Fastify()
|
|
233
321
|
|
|
@@ -336,7 +424,7 @@ test('#getValidationFunction', subtest => {
|
|
|
336
424
|
req.getValidationFunction(defaultSchema)
|
|
337
425
|
req.getValidationFunction('body')
|
|
338
426
|
|
|
339
|
-
t.equal(req
|
|
427
|
+
t.equal(req[kRouteContext][kRequestValidateWeakMap], null)
|
|
340
428
|
reply.send({ hello: 'world' })
|
|
341
429
|
})
|
|
342
430
|
|
|
@@ -636,9 +724,9 @@ test('#validate', subtest => {
|
|
|
636
724
|
t.plan(3)
|
|
637
725
|
|
|
638
726
|
fastify.get('/', (req, reply) => {
|
|
639
|
-
t.equal(req
|
|
727
|
+
t.equal(req[kRouteContext][kRequestValidateWeakMap], null)
|
|
640
728
|
t.equal(req.validateInput({ hello: 'world' }, defaultSchema), true)
|
|
641
|
-
t.type(req
|
|
729
|
+
t.type(req[kRouteContext][kRequestValidateWeakMap], WeakMap)
|
|
642
730
|
|
|
643
731
|
reply.send({ hello: 'world' })
|
|
644
732
|
})
|
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
const { test } = require('tap')
|
|
4
4
|
|
|
5
5
|
const Request = require('../../lib/request')
|
|
6
|
+
const Context = require('../../lib/context')
|
|
7
|
+
const {
|
|
8
|
+
kPublicRouteContext,
|
|
9
|
+
kReply,
|
|
10
|
+
kRequest
|
|
11
|
+
} = require('../../lib/symbols')
|
|
6
12
|
|
|
7
13
|
process.removeAllListeners('warning')
|
|
8
14
|
|
|
@@ -16,8 +22,28 @@ test('Regular request', t => {
|
|
|
16
22
|
socket: { remoteAddress: 'ip' },
|
|
17
23
|
headers
|
|
18
24
|
}
|
|
25
|
+
const context = new Context({
|
|
26
|
+
schema: {
|
|
27
|
+
body: {
|
|
28
|
+
type: 'object',
|
|
29
|
+
required: ['hello'],
|
|
30
|
+
properties: {
|
|
31
|
+
hello: { type: 'string' }
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
config: {
|
|
36
|
+
some: 'config',
|
|
37
|
+
url: req.url,
|
|
38
|
+
method: req.method
|
|
39
|
+
},
|
|
40
|
+
server: {
|
|
41
|
+
[kReply]: {},
|
|
42
|
+
[kRequest]: Request
|
|
43
|
+
}
|
|
44
|
+
})
|
|
19
45
|
req.connection = req.socket
|
|
20
|
-
const request = new Request('id', 'params', req, 'query', 'log')
|
|
46
|
+
const request = new Request('id', 'params', req, 'query', 'log', context)
|
|
21
47
|
t.type(request, Request)
|
|
22
48
|
t.type(request.validateInput, Function)
|
|
23
49
|
t.type(request.getValidationFunction, Function)
|
|
@@ -36,6 +62,10 @@ test('Regular request', t => {
|
|
|
36
62
|
t.equal(request.url, '/')
|
|
37
63
|
t.equal(request.socket, req.socket)
|
|
38
64
|
t.equal(request.protocol, 'http')
|
|
65
|
+
t.equal(request.routerPath, context.config.url)
|
|
66
|
+
t.equal(request.routerMethod, context.config.method)
|
|
67
|
+
t.equal(request.routeConfig, context[kPublicRouteContext].config)
|
|
68
|
+
t.equal(request.routeSchema, context[kPublicRouteContext].schema)
|
|
39
69
|
|
|
40
70
|
// This will be removed, it's deprecated
|
|
41
71
|
t.equal(request.connection, req.connection)
|
|
@@ -77,7 +107,7 @@ test('Regular request - host header has precedence over authority', t => {
|
|
|
77
107
|
})
|
|
78
108
|
|
|
79
109
|
test('Request with trust proxy', t => {
|
|
80
|
-
t.plan(
|
|
110
|
+
t.plan(22)
|
|
81
111
|
const headers = {
|
|
82
112
|
'x-forwarded-for': '2.2.2.2, 1.1.1.1',
|
|
83
113
|
'x-forwarded-host': 'example.com'
|
|
@@ -88,9 +118,29 @@ test('Request with trust proxy', t => {
|
|
|
88
118
|
socket: { remoteAddress: 'ip' },
|
|
89
119
|
headers
|
|
90
120
|
}
|
|
121
|
+
const context = new Context({
|
|
122
|
+
schema: {
|
|
123
|
+
body: {
|
|
124
|
+
type: 'object',
|
|
125
|
+
required: ['hello'],
|
|
126
|
+
properties: {
|
|
127
|
+
hello: { type: 'string' }
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
config: {
|
|
132
|
+
some: 'config',
|
|
133
|
+
url: req.url,
|
|
134
|
+
method: req.method
|
|
135
|
+
},
|
|
136
|
+
server: {
|
|
137
|
+
[kReply]: {},
|
|
138
|
+
[kRequest]: Request
|
|
139
|
+
}
|
|
140
|
+
})
|
|
91
141
|
|
|
92
142
|
const TpRequest = Request.buildRequest(Request, true)
|
|
93
|
-
const request = new TpRequest('id', 'params', req, 'query', 'log')
|
|
143
|
+
const request = new TpRequest('id', 'params', req, 'query', 'log', context)
|
|
94
144
|
t.type(request, TpRequest)
|
|
95
145
|
t.equal(request.id, 'id')
|
|
96
146
|
t.equal(request.params, 'params')
|
|
@@ -109,6 +159,10 @@ test('Request with trust proxy', t => {
|
|
|
109
159
|
t.type(request.validateInput, Function)
|
|
110
160
|
t.type(request.getValidationFunction, Function)
|
|
111
161
|
t.type(request.compileValidationSchema, Function)
|
|
162
|
+
t.equal(request.routerPath, context.config.url)
|
|
163
|
+
t.equal(request.routerMethod, context.config.method)
|
|
164
|
+
t.equal(request.routeConfig, context[kPublicRouteContext].config)
|
|
165
|
+
t.equal(request.routeSchema, context[kPublicRouteContext].schema)
|
|
112
166
|
})
|
|
113
167
|
|
|
114
168
|
test('Request with trust proxy, encrypted', t => {
|
|
@@ -268,6 +268,21 @@ test('build schema - headers are not lowercased in case of custom object', t =>
|
|
|
268
268
|
})
|
|
269
269
|
})
|
|
270
270
|
|
|
271
|
+
test('build schema - headers are not lowercased in case of custom validator provided', t => {
|
|
272
|
+
t.plan(1)
|
|
273
|
+
|
|
274
|
+
class Headers {}
|
|
275
|
+
const opts = {
|
|
276
|
+
schema: {
|
|
277
|
+
headers: new Headers()
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
validation.compileSchemasForValidation(opts, ({ schema, method, url, httpPart }) => {
|
|
281
|
+
t.type(schema, Headers)
|
|
282
|
+
return () => {}
|
|
283
|
+
}, true)
|
|
284
|
+
})
|
|
285
|
+
|
|
271
286
|
test('build schema - uppercased headers are not included', t => {
|
|
272
287
|
t.plan(1)
|
|
273
288
|
const opts = {
|
package/test/plugin.test.js
CHANGED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const t = require('tap')
|
|
4
|
+
const test = t.test
|
|
5
|
+
const Fastify = require('..')
|
|
6
|
+
|
|
7
|
+
test('code should handle null/undefined/float', t => {
|
|
8
|
+
t.plan(8)
|
|
9
|
+
|
|
10
|
+
const fastify = Fastify()
|
|
11
|
+
|
|
12
|
+
fastify.get('/null', function (request, reply) {
|
|
13
|
+
reply.status(null).send()
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
fastify.get('/undefined', function (request, reply) {
|
|
17
|
+
reply.status(undefined).send()
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
fastify.get('/404.5', function (request, reply) {
|
|
21
|
+
reply.status(404.5).send()
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
fastify.inject({
|
|
25
|
+
method: 'GET',
|
|
26
|
+
url: '/null'
|
|
27
|
+
}, (error, res) => {
|
|
28
|
+
t.error(error)
|
|
29
|
+
t.equal(res.statusCode, 500)
|
|
30
|
+
t.same(res.json(), {
|
|
31
|
+
statusCode: 500,
|
|
32
|
+
code: 'FST_ERR_BAD_STATUS_CODE',
|
|
33
|
+
error: 'Internal Server Error',
|
|
34
|
+
message: 'Called reply with an invalid status code: null'
|
|
35
|
+
})
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
fastify.inject({
|
|
39
|
+
method: 'GET',
|
|
40
|
+
url: '/undefined'
|
|
41
|
+
}, (error, res) => {
|
|
42
|
+
t.error(error)
|
|
43
|
+
t.equal(res.statusCode, 500)
|
|
44
|
+
t.same(res.json(), {
|
|
45
|
+
statusCode: 500,
|
|
46
|
+
code: 'FST_ERR_BAD_STATUS_CODE',
|
|
47
|
+
error: 'Internal Server Error',
|
|
48
|
+
message: 'Called reply with an invalid status code: undefined'
|
|
49
|
+
})
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
fastify.inject({
|
|
53
|
+
method: 'GET',
|
|
54
|
+
url: '/404.5'
|
|
55
|
+
}, (error, res) => {
|
|
56
|
+
t.error(error)
|
|
57
|
+
t.equal(res.statusCode, 404)
|
|
58
|
+
})
|
|
59
|
+
})
|
package/test/route.test.js
CHANGED
|
@@ -1464,3 +1464,32 @@ test('invalid url attribute - non string URL', t => {
|
|
|
1464
1464
|
t.equal(error.code, FST_ERR_INVALID_URL().code)
|
|
1465
1465
|
}
|
|
1466
1466
|
})
|
|
1467
|
+
|
|
1468
|
+
test('exposeHeadRoute should not reuse the same route option', async t => {
|
|
1469
|
+
t.plan(2)
|
|
1470
|
+
|
|
1471
|
+
const fastify = Fastify()
|
|
1472
|
+
|
|
1473
|
+
// we update the onRequest hook in onRoute hook
|
|
1474
|
+
// if we reuse the same route option
|
|
1475
|
+
// that means we will append another function inside the array
|
|
1476
|
+
fastify.addHook('onRoute', function (routeOption) {
|
|
1477
|
+
if (Array.isArray(routeOption.onRequest)) {
|
|
1478
|
+
routeOption.onRequest.push(() => {})
|
|
1479
|
+
} else {
|
|
1480
|
+
routeOption.onRequest = [() => {}]
|
|
1481
|
+
}
|
|
1482
|
+
})
|
|
1483
|
+
|
|
1484
|
+
fastify.addHook('onRoute', function (routeOption) {
|
|
1485
|
+
t.equal(routeOption.onRequest.length, 1)
|
|
1486
|
+
})
|
|
1487
|
+
|
|
1488
|
+
fastify.route({
|
|
1489
|
+
method: 'GET',
|
|
1490
|
+
path: '/more-coffee',
|
|
1491
|
+
async handler () {
|
|
1492
|
+
return 'hello world'
|
|
1493
|
+
}
|
|
1494
|
+
})
|
|
1495
|
+
})
|