UNPKG

7.85 kBJavaScriptView Raw
1'use strict'
2
3const t = require('tap')
4const Joi = require('joi')
5const Fastify = require('..')
6const test = t.test
7
8const schema = {
9 body: {
10 type: 'object',
11 properties: {
12 name: { type: 'string' },
13 work: { type: 'string' }
14 },
15 required: ['name', 'work']
16 }
17}
18
19test('should work with valid payload', t => {
20 t.plan(3)
21
22 const fastify = Fastify()
23
24 fastify.post('/', { schema }, function (req, reply) {
25 reply.code(200).send(req.body.name)
26 })
27
28 fastify.inject({
29 method: 'POST',
30 payload: {
31 name: 'michelangelo',
32 work: 'sculptor, painter, architect and poet'
33 },
34 url: '/'
35 }, (err, res) => {
36 t.error(err)
37 t.deepEqual(res.payload, 'michelangelo')
38 t.strictEqual(res.statusCode, 200)
39 })
40})
41
42test('should fail immediately with invalid payload', t => {
43 t.plan(3)
44
45 const fastify = Fastify()
46
47 fastify.post('/', { schema }, function (req, reply) {
48 reply.code(200).send(req.body.name)
49 })
50
51 fastify.inject({
52 method: 'POST',
53 payload: {
54 hello: 'michelangelo'
55 },
56 url: '/'
57 }, (err, res) => {
58 t.error(err)
59 t.deepEqual(JSON.parse(res.payload), {
60 statusCode: 400,
61 error: 'Bad Request',
62 message: "body should have required property 'name', body should have required property 'work'"
63 })
64 t.strictEqual(res.statusCode, 400)
65 })
66})
67
68test('should be able to use setErrorHandler specify custom validation error', t => {
69 t.plan(3)
70
71 const fastify = Fastify()
72
73 fastify.post('/', { schema }, function (req, reply) {
74 t.fail('should not be here')
75 reply.code(200).send(req.body.name)
76 })
77
78 fastify.setErrorHandler(function (error, request, reply) {
79 if (error.validation) {
80 reply.status(422).send(new Error('validation failed'))
81 }
82 })
83
84 fastify.inject({
85 method: 'POST',
86 payload: {
87 hello: 'michelangelo'
88 },
89 url: '/'
90 }, (err, res) => {
91 t.error(err)
92 t.deepEqual(JSON.parse(res.payload), {
93 statusCode: 422,
94 error: 'Unprocessable Entity',
95 message: 'validation failed'
96 })
97 t.strictEqual(res.statusCode, 422)
98 })
99})
100
101test('should be able to attach validation to request', t => {
102 t.plan(3)
103
104 const fastify = Fastify()
105
106 fastify.post('/', { schema, attachValidation: true }, function (req, reply) {
107 reply.code(400).send(req.validationError.validation)
108 })
109
110 fastify.inject({
111 method: 'POST',
112 payload: {
113 hello: 'michelangelo'
114 },
115 url: '/'
116 }, (err, res) => {
117 t.error(err)
118
119 t.deepEqual(JSON.parse(res.payload), [{
120 keyword: 'required',
121 dataPath: '',
122 schemaPath: '#/required',
123 params: { missingProperty: 'name' },
124 message: 'should have required property \'name\''
125 },
126 {
127 keyword: 'required',
128 dataPath: '',
129 schemaPath: '#/required',
130 params: { missingProperty: 'work' },
131 message: 'should have required property \'work\''
132 }])
133 t.strictEqual(res.statusCode, 400)
134 })
135})
136
137test('should respect when attachValidation is explicitly set to false', t => {
138 t.plan(3)
139
140 const fastify = Fastify()
141
142 fastify.post('/', { schema, attachValidation: false }, function (req, reply) {
143 t.fail('should not be here')
144 reply.code(200).send(req.validationError.validation)
145 })
146
147 fastify.inject({
148 method: 'POST',
149 payload: {
150 hello: 'michelangelo'
151 },
152 url: '/'
153 }, (err, res) => {
154 t.error(err)
155 t.deepEqual(JSON.parse(res.payload), {
156 statusCode: 400,
157 error: 'Bad Request',
158 message: "body should have required property 'name', body should have required property 'work'"
159 })
160 t.strictEqual(res.statusCode, 400)
161 })
162})
163
164test('Attached validation error should take precendence over setErrorHandler', t => {
165 t.plan(3)
166
167 const fastify = Fastify()
168
169 fastify.post('/', { schema, attachValidation: true }, function (req, reply) {
170 reply.code(400).send('Attached: ' + req.validationError)
171 })
172
173 fastify.setErrorHandler(function (error, request, reply) {
174 t.fail('should not be here')
175 if (error.validation) {
176 reply.status(422).send(new Error('validation failed'))
177 }
178 })
179
180 fastify.inject({
181 method: 'POST',
182 payload: {
183 hello: 'michelangelo'
184 },
185 url: '/'
186 }, (err, res) => {
187 t.error(err)
188 t.deepEqual(res.payload, "Attached: Error: body should have required property 'name', body should have required property 'work'")
189 t.strictEqual(res.statusCode, 400)
190 })
191})
192
193test('should handle response validation error', t => {
194 t.plan(2)
195
196 const response = {
197 200: {
198 type: 'object',
199 required: ['name', 'work'],
200 properties: {
201 name: { type: 'string' },
202 work: { type: 'string' }
203 }
204 }
205 }
206
207 const fastify = Fastify()
208
209 fastify.get('/', { schema: { response } }, function (req, reply) {
210 try {
211 reply.code(200).send({ work: 'actor' })
212 } catch (error) {
213 reply.code(500).send(error)
214 }
215 })
216
217 fastify.inject({
218 method: 'GET',
219 payload: { },
220 url: '/'
221 }, (err, res) => {
222 t.error(err)
223 t.strictEqual(res.payload, '{"statusCode":500,"error":"Internal Server Error","message":"name is required!"}')
224 })
225})
226
227test('should handle response validation error with promises', t => {
228 t.plan(2)
229
230 const response = {
231 200: {
232 type: 'object',
233 required: ['name', 'work'],
234 properties: {
235 name: { type: 'string' },
236 work: { type: 'string' }
237 }
238 }
239 }
240
241 const fastify = Fastify()
242
243 fastify.get('/', { schema: { response } }, function (req, reply) {
244 return Promise.resolve({ work: 'actor' })
245 })
246
247 fastify.inject({
248 method: 'GET',
249 payload: { },
250 url: '/'
251 }, (err, res) => {
252 t.error(err)
253 t.strictEqual(res.payload, '{"statusCode":500,"error":"Internal Server Error","message":"name is required!"}')
254 })
255})
256
257test('should return a defined output message parsing AJV errors', t => {
258 t.plan(2)
259
260 const body = {
261 type: 'object',
262 required: ['name', 'work'],
263 properties: {
264 name: { type: 'string' },
265 work: { type: 'string' }
266 }
267 }
268
269 const fastify = Fastify()
270
271 fastify.post('/', { schema: { body } }, function (req, reply) {
272 t.fail()
273 })
274
275 fastify.inject({
276 method: 'POST',
277 payload: { },
278 url: '/'
279 }, (err, res) => {
280 t.error(err)
281 t.strictEqual(res.payload, '{"statusCode":400,"error":"Bad Request","message":"body should have required property \'name\', body should have required property \'work\'"}')
282 })
283})
284
285test('should return a defined output message parsing JOI errors', t => {
286 t.plan(2)
287
288 const body = Joi.object().keys({
289 name: Joi.string().required(),
290 work: Joi.string().required()
291 }).required()
292
293 const fastify = Fastify()
294
295 fastify.post('/', {
296 schema: { body },
297 schemaCompiler: schema => data => Joi.validate(data, schema)
298 },
299 function (req, reply) {
300 t.fail()
301 })
302
303 fastify.inject({
304 method: 'POST',
305 payload: {},
306 url: '/'
307 }, (err, res) => {
308 t.error(err)
309 t.strictEqual(res.payload, '{"statusCode":400,"error":"Bad Request","message":"child \\"name\\" fails because [\\"name\\" is required]"}')
310 })
311})
312
313test('should return a defined output message parsing JOI error details', t => {
314 t.plan(2)
315
316 const body = Joi.object().keys({
317 name: Joi.string().required(),
318 work: Joi.string().required()
319 }).required()
320
321 const fastify = Fastify()
322
323 fastify.post('/', {
324 schema: { body },
325 schemaCompiler: schema => data => {
326 const validation = Joi.validate(data, schema)
327 return { error: validation.error.details }
328 }
329 },
330 function (req, reply) {
331 t.fail()
332 })
333
334 fastify.inject({
335 method: 'POST',
336 payload: {},
337 url: '/'
338 }, (err, res) => {
339 t.error(err)
340 t.strictEqual(res.payload, '{"statusCode":400,"error":"Bad Request","message":"body \\"name\\" is required"}')
341 })
342})