Buckets:
ktongue/docker_container / .cache /opencode /node_modules /fastify /test /schema-serialization.test.js
| const { test } = require('node:test') | |
| const Fastify = require('..') | |
| const { waitForCb } = require('./toolkit') | |
| const echoBody = (req, reply) => { reply.send(req.body) } | |
| test('basic test', (t, testDone) => { | |
| t.plan(3) | |
| const fastify = Fastify() | |
| fastify.get('/', { | |
| schema: { | |
| response: { | |
| '2xx': { | |
| type: 'object', | |
| properties: { | |
| name: { type: 'string' }, | |
| work: { type: 'string' } | |
| } | |
| } | |
| } | |
| } | |
| }, function (req, reply) { | |
| reply.code(200).send({ name: 'Foo', work: 'Bar', nick: 'Boo' }) | |
| }) | |
| fastify.inject('/', (err, res) => { | |
| t.assert.ifError(err) | |
| t.assert.deepStrictEqual(res.json(), { name: 'Foo', work: 'Bar' }) | |
| t.assert.strictEqual(res.statusCode, 200) | |
| testDone() | |
| }) | |
| }) | |
| test('custom serializer options', (t, testDone) => { | |
| t.plan(3) | |
| const fastify = Fastify({ | |
| serializerOpts: { | |
| rounding: 'ceil' | |
| } | |
| }) | |
| fastify.get('/', { | |
| schema: { | |
| response: { | |
| '2xx': { | |
| type: 'integer' | |
| } | |
| } | |
| } | |
| }, function (req, reply) { | |
| reply.send(4.2) | |
| }) | |
| fastify.inject('/', (err, res) => { | |
| t.assert.ifError(err) | |
| t.assert.strictEqual(res.payload, '5', 'it must use the ceil rounding') | |
| t.assert.strictEqual(res.statusCode, 200) | |
| testDone() | |
| }) | |
| }) | |
| test('Different content types', (t, testDone) => { | |
| t.plan(46) | |
| const fastify = Fastify() | |
| fastify.addSchema({ | |
| $id: 'test', | |
| type: 'object', | |
| properties: { | |
| name: { type: 'string' }, | |
| age: { type: 'number' }, | |
| verified: { type: 'boolean' } | |
| } | |
| }) | |
| fastify.get('/', { | |
| schema: { | |
| response: { | |
| 200: { | |
| content: { | |
| 'application/json': { | |
| schema: { | |
| type: 'object', | |
| properties: { | |
| name: { type: 'string' }, | |
| image: { type: 'string' }, | |
| address: { type: 'string' } | |
| } | |
| } | |
| }, | |
| 'application/vnd.v1+json': { | |
| schema: { | |
| type: 'array', | |
| items: { $ref: 'test' } | |
| } | |
| } | |
| } | |
| }, | |
| 201: { | |
| content: { | |
| '*/*': { | |
| schema: { type: 'string' } | |
| } | |
| } | |
| }, | |
| 202: { | |
| content: { | |
| '*/*': { | |
| schema: { const: 'Processing exclusive content' } | |
| } | |
| } | |
| }, | |
| '3xx': { | |
| content: { | |
| 'application/vnd.v2+json': { | |
| schema: { | |
| type: 'object', | |
| properties: { | |
| fullName: { type: 'string' }, | |
| phone: { type: 'string' } | |
| } | |
| } | |
| } | |
| } | |
| }, | |
| '4xx': { | |
| content: { | |
| '*/*': { | |
| schema: { | |
| type: 'object', | |
| properties: { | |
| details: { type: 'string' } | |
| } | |
| } | |
| } | |
| } | |
| }, | |
| default: { | |
| content: { | |
| 'application/json': { | |
| schema: { | |
| type: 'object', | |
| properties: { | |
| details: { type: 'string' } | |
| } | |
| } | |
| }, | |
| '*/*': { | |
| schema: { | |
| type: 'object', | |
| properties: { | |
| desc: { type: 'string' }, | |
| details: { type: 'string' } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| }, function (req, reply) { | |
| switch (req.headers.accept) { | |
| case 'application/json': | |
| reply.header('Content-Type', 'application/json') | |
| reply.send({ id: 1, name: 'Foo', image: 'profile picture', address: 'New Node' }) | |
| break | |
| case 'application/vnd.v1+json': | |
| reply.header('Content-Type', 'application/vnd.v1+json') | |
| reply.send([{ id: 2, name: 'Boo', age: 18, verified: false }, { id: 3, name: 'Woo', age: 30, verified: true }]) | |
| break | |
| case 'application/vnd.v2+json': | |
| reply.header('Content-Type', 'application/vnd.v2+json') | |
| reply.code(300) | |
| reply.send({ fullName: 'Jhon Smith', phone: '01090000000', authMethod: 'google' }) | |
| break | |
| case 'application/vnd.v3+json': | |
| reply.header('Content-Type', 'application/vnd.v3+json') | |
| reply.code(300) | |
| reply.send({ firstName: 'New', lastName: 'Hoo', country: 'eg', city: 'node' }) | |
| break | |
| case 'application/vnd.v4+json': | |
| reply.header('Content-Type', 'application/vnd.v4+json') | |
| reply.code(201) | |
| reply.send({ boxId: 1, content: 'Games' }) | |
| break | |
| case 'application/vnd.v5+json': | |
| reply.header('Content-Type', 'application/vnd.v5+json') | |
| reply.code(202) | |
| reply.send({ content: 'interesting content' }) | |
| break | |
| case 'application/vnd.v6+json': | |
| reply.header('Content-Type', 'application/vnd.v6+json') | |
| reply.code(400) | |
| reply.send({ desc: 'age is missing', details: 'validation error' }) | |
| break | |
| case 'application/vnd.v7+json': | |
| reply.code(400) | |
| reply.send({ details: 'validation error' }) | |
| break | |
| case 'application/vnd.v8+json': | |
| reply.header('Content-Type', 'application/vnd.v8+json') | |
| reply.code(500) | |
| reply.send({ desc: 'age is missing', details: 'validation error' }) | |
| break | |
| case 'application/vnd.v9+json': | |
| reply.code(500) | |
| reply.send({ details: 'validation error' }) | |
| break | |
| default: | |
| // to test if schema not found | |
| reply.header('Content-Type', 'application/vnd.v3+json') | |
| reply.code(200) | |
| reply.send([{ type: 'student', grade: 6 }, { type: 'student', grade: 9 }]) | |
| } | |
| }) | |
| fastify.get('/test', { | |
| serializerCompiler: ({ contentType }) => { | |
| t.assert.strictEqual(contentType, 'application/json') | |
| return data => JSON.stringify(data) | |
| }, | |
| schema: { | |
| response: { | |
| 200: { | |
| content: { | |
| 'application/json': { | |
| schema: { | |
| type: 'object', | |
| properties: { | |
| name: { type: 'string' }, | |
| image: { type: 'string' }, | |
| address: { type: 'string' } | |
| } | |
| } | |
| } | |
| } | |
| }, | |
| default: { | |
| content: { | |
| 'application/json': { | |
| schema: { | |
| type: 'object', | |
| properties: { | |
| details: { type: 'string' } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| }, function (req, reply) { | |
| switch (req.headers['code']) { | |
| case '200': { | |
| reply.header('Content-Type', 'application/json') | |
| reply.code(200).send({ age: 18, city: 'AU' }) | |
| break | |
| } | |
| case '201': { | |
| reply.header('Content-Type', 'application/json') | |
| reply.code(201).send({ details: 'validation error' }) | |
| break | |
| } | |
| default: { | |
| reply.header('Content-Type', 'application/vnd.v1+json') | |
| reply.code(201).send({ created: true }) | |
| break | |
| } | |
| } | |
| }) | |
| const completion = waitForCb({ steps: 14 }) | |
| fastify.inject({ method: 'GET', url: '/', headers: { Accept: 'application/json' } }, (err, res) => { | |
| t.assert.ifError(err) | |
| t.assert.strictEqual(res.payload, JSON.stringify({ name: 'Foo', image: 'profile picture', address: 'New Node' })) | |
| t.assert.strictEqual(res.statusCode, 200) | |
| completion.stepIn() | |
| }) | |
| fastify.inject({ method: 'GET', url: '/', headers: { Accept: 'application/vnd.v1+json' } }, (err, res) => { | |
| t.assert.ifError(err) | |
| t.assert.strictEqual(res.payload, JSON.stringify([{ name: 'Boo', age: 18, verified: false }, { name: 'Woo', age: 30, verified: true }])) | |
| t.assert.strictEqual(res.statusCode, 200) | |
| completion.stepIn() | |
| }) | |
| fastify.inject({ method: 'GET', url: '/' }, (err, res) => { | |
| t.assert.ifError(err) | |
| t.assert.strictEqual(res.payload, JSON.stringify([{ type: 'student', grade: 6 }, { type: 'student', grade: 9 }])) | |
| t.assert.strictEqual(res.statusCode, 200) | |
| completion.stepIn() | |
| }) | |
| fastify.inject({ method: 'GET', url: '/', headers: { Accept: 'application/vnd.v2+json' } }, (err, res) => { | |
| t.assert.ifError(err) | |
| t.assert.strictEqual(res.payload, JSON.stringify({ fullName: 'Jhon Smith', phone: '01090000000' })) | |
| t.assert.strictEqual(res.statusCode, 300) | |
| completion.stepIn() | |
| }) | |
| fastify.inject({ method: 'GET', url: '/', headers: { Accept: 'application/vnd.v3+json' } }, (err, res) => { | |
| t.assert.ifError(err) | |
| t.assert.strictEqual(res.payload, JSON.stringify({ firstName: 'New', lastName: 'Hoo', country: 'eg', city: 'node' })) | |
| t.assert.strictEqual(res.statusCode, 300) | |
| completion.stepIn() | |
| }) | |
| fastify.inject({ method: 'GET', url: '/', headers: { Accept: 'application/vnd.v4+json' } }, (err, res) => { | |
| t.assert.ifError(err) | |
| t.assert.strictEqual(res.payload, '"[object Object]"') | |
| t.assert.strictEqual(res.statusCode, 201) | |
| completion.stepIn() | |
| }) | |
| fastify.inject({ method: 'GET', url: '/', headers: { Accept: 'application/vnd.v5+json' } }, (err, res) => { | |
| t.assert.ifError(err) | |
| t.assert.strictEqual(res.payload, '"Processing exclusive content"') | |
| t.assert.strictEqual(res.statusCode, 202) | |
| completion.stepIn() | |
| }) | |
| fastify.inject({ method: 'GET', url: '/', headers: { Accept: 'application/vnd.v6+json' } }, (err, res) => { | |
| t.assert.ifError(err) | |
| t.assert.strictEqual(res.payload, JSON.stringify({ details: 'validation error' })) | |
| t.assert.strictEqual(res.statusCode, 400) | |
| completion.stepIn() | |
| }) | |
| fastify.inject({ method: 'GET', url: '/', headers: { Accept: 'application/vnd.v7+json' } }, (err, res) => { | |
| t.assert.ifError(err) | |
| t.assert.strictEqual(res.payload, JSON.stringify({ details: 'validation error' })) | |
| t.assert.strictEqual(res.statusCode, 400) | |
| completion.stepIn() | |
| }) | |
| fastify.inject({ method: 'GET', url: '/', headers: { Accept: 'application/vnd.v8+json' } }, (err, res) => { | |
| t.assert.ifError(err) | |
| t.assert.strictEqual(res.payload, JSON.stringify({ desc: 'age is missing', details: 'validation error' })) | |
| t.assert.strictEqual(res.statusCode, 500) | |
| completion.stepIn() | |
| }) | |
| fastify.inject({ method: 'GET', url: '/', headers: { Accept: 'application/vnd.v9+json' } }, (err, res) => { | |
| t.assert.ifError(err) | |
| t.assert.strictEqual(res.payload, JSON.stringify({ details: 'validation error' })) | |
| t.assert.strictEqual(res.statusCode, 500) | |
| completion.stepIn() | |
| }) | |
| fastify.inject({ method: 'GET', url: '/test', headers: { Code: '200' } }, (err, res) => { | |
| t.assert.ifError(err) | |
| t.assert.strictEqual(res.payload, JSON.stringify({ age: 18, city: 'AU' })) | |
| t.assert.strictEqual(res.statusCode, 200) | |
| completion.stepIn() | |
| }) | |
| fastify.inject({ method: 'GET', url: '/test', headers: { Code: '201' } }, (err, res) => { | |
| t.assert.ifError(err) | |
| t.assert.strictEqual(res.payload, JSON.stringify({ details: 'validation error' })) | |
| t.assert.strictEqual(res.statusCode, 201) | |
| completion.stepIn() | |
| }) | |
| fastify.inject({ method: 'GET', url: '/test', headers: { Accept: 'application/vnd.v1+json' } }, (err, res) => { | |
| t.assert.ifError(err) | |
| t.assert.strictEqual(res.payload, JSON.stringify({ created: true })) | |
| t.assert.strictEqual(res.statusCode, 201) | |
| completion.stepIn() | |
| }) | |
| completion.patience.then(testDone) | |
| }) | |
| test('Invalid multiple content schema, throw FST_ERR_SCH_CONTENT_MISSING_SCHEMA error', (t, testDone) => { | |
| t.plan(3) | |
| const fastify = Fastify() | |
| fastify.get('/testInvalid', { | |
| schema: { | |
| response: { | |
| 200: { | |
| content: { | |
| 'application/json': { | |
| schema: { | |
| type: 'object', | |
| properties: { | |
| fullName: { type: 'string' }, | |
| phone: { type: 'string' } | |
| } | |
| }, | |
| example: { | |
| fullName: 'John Doe', | |
| phone: '201090243795' | |
| } | |
| }, | |
| type: 'string' | |
| } | |
| } | |
| } | |
| } | |
| }, function (req, reply) { | |
| reply.header('Content-Type', 'application/json') | |
| reply.send({ fullName: 'Any name', phone: '0109001010' }) | |
| }) | |
| fastify.ready((err) => { | |
| t.assert.strictEqual(err.message, "Schema is missing for the content type 'type'") | |
| t.assert.strictEqual(err.statusCode, 500) | |
| t.assert.strictEqual(err.code, 'FST_ERR_SCH_CONTENT_MISSING_SCHEMA') | |
| testDone() | |
| }) | |
| }) | |
| test('Use the same schema id in different places', (t, testDone) => { | |
| t.plan(2) | |
| const fastify = Fastify() | |
| fastify.addSchema({ | |
| $id: 'test', | |
| type: 'object', | |
| properties: { | |
| id: { type: 'number' } | |
| } | |
| }) | |
| fastify.get('/:id', { | |
| handler (req, reply) { | |
| reply.send([{ id: 1 }, { id: 2 }, { what: 'is this' }]) | |
| }, | |
| schema: { | |
| response: { | |
| 200: { | |
| type: 'array', | |
| items: { $ref: 'test' } | |
| } | |
| } | |
| } | |
| }) | |
| fastify.inject({ | |
| method: 'GET', | |
| url: '/123' | |
| }, (err, res) => { | |
| t.assert.ifError(err) | |
| t.assert.deepStrictEqual(res.json(), [{ id: 1 }, { id: 2 }, {}]) | |
| testDone() | |
| }) | |
| }) | |
| test('Use shared schema and $ref with $id in response ($ref to $id)', (t, testDone) => { | |
| t.plan(5) | |
| const fastify = Fastify() | |
| fastify.addSchema({ | |
| $id: 'http://foo/test', | |
| type: 'object', | |
| properties: { | |
| id: { type: 'number' } | |
| } | |
| }) | |
| const complexSchema = { | |
| $schema: 'http://json-schema.org/draft-07/schema#', | |
| $id: 'http://foo/user', | |
| type: 'object', | |
| definitions: { | |
| address: { | |
| $id: '#address', | |
| type: 'object', | |
| properties: { | |
| city: { type: 'string' } | |
| } | |
| } | |
| }, | |
| properties: { | |
| test: { $ref: 'http://foo/test#' }, | |
| address: { $ref: '#address' } | |
| }, | |
| required: ['address', 'test'] | |
| } | |
| fastify.post('/', { | |
| schema: { | |
| body: complexSchema, | |
| response: { | |
| 200: complexSchema | |
| } | |
| }, | |
| handler: (req, reply) => { | |
| req.body.removeThis = 'it should not be serialized' | |
| reply.send(req.body) | |
| } | |
| }) | |
| const payload = { | |
| address: { city: 'New Node' }, | |
| test: { id: Date.now() } | |
| } | |
| const completion = waitForCb({ steps: 2 }) | |
| fastify.inject({ | |
| method: 'POST', | |
| url: '/', | |
| payload | |
| }, (err, res) => { | |
| t.assert.ifError(err) | |
| t.assert.deepStrictEqual(res.json(), payload) | |
| completion.stepIn() | |
| }) | |
| fastify.inject({ | |
| method: 'POST', | |
| url: '/', | |
| payload: { test: { id: Date.now() } } | |
| }, (err, res) => { | |
| t.assert.ifError(err) | |
| t.assert.strictEqual(res.statusCode, 400) | |
| t.assert.deepStrictEqual(res.json(), { | |
| error: 'Bad Request', | |
| message: "body must have required property 'address'", | |
| statusCode: 400, | |
| code: 'FST_ERR_VALIDATION' | |
| }) | |
| completion.stepIn() | |
| }) | |
| completion.patience.then(testDone) | |
| }) | |
| test('Shared schema should be pass to serializer and validator ($ref to shared schema /definitions)', (t, testDone) => { | |
| t.plan(5) | |
| const fastify = Fastify() | |
| fastify.addSchema({ | |
| $id: 'http://example.com/asset.json', | |
| $schema: 'http://json-schema.org/draft-07/schema#', | |
| title: 'Physical Asset', | |
| description: 'A generic representation of a physical asset', | |
| type: 'object', | |
| required: [ | |
| 'id', | |
| 'model', | |
| 'location' | |
| ], | |
| properties: { | |
| id: { | |
| type: 'string', | |
| format: 'uuid' | |
| }, | |
| model: { | |
| type: 'string' | |
| }, | |
| location: { $ref: 'http://example.com/point.json#' } | |
| }, | |
| definitions: { | |
| inner: { | |
| $id: '#innerId', | |
| type: 'string', | |
| format: 'email' | |
| } | |
| } | |
| }) | |
| fastify.addSchema({ | |
| $id: 'http://example.com/point.json', | |
| $schema: 'http://json-schema.org/draft-07/schema#', | |
| title: 'Longitude and Latitude Values', | |
| description: 'A geographical coordinate.', | |
| type: 'object', | |
| required: [ | |
| 'latitude', | |
| 'longitude' | |
| ], | |
| properties: { | |
| email: { $ref: 'http://example.com/asset.json#/definitions/inner' }, | |
| latitude: { | |
| type: 'number', | |
| minimum: -90, | |
| maximum: 90 | |
| }, | |
| longitude: { | |
| type: 'number', | |
| minimum: -180, | |
| maximum: 180 | |
| }, | |
| altitude: { | |
| type: 'number' | |
| } | |
| } | |
| }) | |
| const schemaLocations = { | |
| $id: 'http://example.com/locations.json', | |
| $schema: 'http://json-schema.org/draft-07/schema#', | |
| title: 'List of Asset locations', | |
| type: 'array', | |
| items: { $ref: 'http://example.com/asset.json#' } | |
| } | |
| fastify.post('/', { | |
| schema: { | |
| body: schemaLocations, | |
| response: { 200: schemaLocations } | |
| } | |
| }, (req, reply) => { | |
| reply.send(locations.map(_ => Object.assign({ serializer: 'remove me' }, _))) | |
| }) | |
| const locations = [ | |
| { id: '550e8400-e29b-41d4-a716-446655440000', model: 'mod', location: { latitude: 10, longitude: 10, email: 'foo@bar.it' } }, | |
| { id: '550e8400-e29b-41d4-a716-446655440000', model: 'mod', location: { latitude: 10, longitude: 10, email: 'foo@bar.it' } } | |
| ] | |
| fastify.inject({ | |
| method: 'POST', | |
| url: '/', | |
| payload: locations | |
| }, (err, res) => { | |
| t.assert.ifError(err) | |
| t.assert.deepStrictEqual(res.json(), locations) | |
| fastify.inject({ | |
| method: 'POST', | |
| url: '/', | |
| payload: locations.map(_ => { | |
| _.location.email = 'not an email' | |
| return _ | |
| }) | |
| }, (err, res) => { | |
| t.assert.ifError(err) | |
| t.assert.strictEqual(res.statusCode, 400) | |
| t.assert.deepStrictEqual(res.json(), { | |
| error: 'Bad Request', | |
| message: 'body/0/location/email must match format "email"', | |
| statusCode: 400, | |
| code: 'FST_ERR_VALIDATION' | |
| }) | |
| testDone() | |
| }) | |
| }) | |
| }) | |
| test('Custom setSerializerCompiler', (t, testDone) => { | |
| t.plan(7) | |
| const fastify = Fastify({ exposeHeadRoutes: false }) | |
| const outSchema = { | |
| $id: 'test', | |
| type: 'object', | |
| whatever: 'need to be parsed by the custom serializer' | |
| } | |
| fastify.setSerializerCompiler(({ schema, method, url, httpStatus }) => { | |
| t.assert.strictEqual(method, 'GET') | |
| t.assert.strictEqual(url, '/foo/:id') | |
| t.assert.strictEqual(httpStatus, '200') | |
| t.assert.deepStrictEqual(schema, outSchema) | |
| return data => JSON.stringify(data) | |
| }) | |
| fastify.register((instance, opts, done) => { | |
| instance.get('/:id', { | |
| handler (req, reply) { | |
| reply.send({ id: 1 }) | |
| }, | |
| schema: { | |
| response: { | |
| 200: outSchema | |
| } | |
| } | |
| }) | |
| t.assert.ok(instance.serializerCompiler, 'the serializer is set by the parent') | |
| done() | |
| }, { prefix: '/foo' }) | |
| fastify.inject({ | |
| method: 'GET', | |
| url: '/foo/123' | |
| }, (err, res) => { | |
| t.assert.ifError(err) | |
| t.assert.strictEqual(res.payload, JSON.stringify({ id: 1 })) | |
| testDone() | |
| }) | |
| }) | |
| test('Custom setSerializerCompiler returns bad serialized output', (t, testDone) => { | |
| t.plan(4) | |
| const fastify = Fastify() | |
| const outSchema = { | |
| $id: 'test', | |
| type: 'object', | |
| whatever: 'need to be parsed by the custom serializer' | |
| } | |
| fastify.setSerializerCompiler(({ schema, method, url, httpStatus }) => { | |
| return data => { | |
| t.assert.ok('returning an invalid serialization') | |
| return { not: 'a string' } | |
| } | |
| }) | |
| fastify.get('/:id', { | |
| handler (req, reply) { throw new Error('ops') }, | |
| schema: { | |
| response: { | |
| 500: outSchema | |
| } | |
| } | |
| }) | |
| fastify.inject({ | |
| method: 'GET', | |
| url: '/123' | |
| }, (err, res) => { | |
| t.assert.ifError(err) | |
| t.assert.strictEqual(res.statusCode, 500) | |
| t.assert.deepStrictEqual(res.json(), { | |
| code: 'FST_ERR_REP_INVALID_PAYLOAD_TYPE', | |
| message: 'Attempted to send payload of invalid type \'object\'. Expected a string or Buffer.', | |
| statusCode: 500 | |
| }) | |
| testDone() | |
| }) | |
| }) | |
| test('Custom setSerializerCompiler with addSchema', (t, testDone) => { | |
| t.plan(6) | |
| const fastify = Fastify({ exposeHeadRoutes: false }) | |
| const outSchema = { | |
| $id: 'test', | |
| type: 'object', | |
| whatever: 'need to be parsed by the custom serializer' | |
| } | |
| fastify.setSerializerCompiler(({ schema, method, url, httpStatus }) => { | |
| t.assert.strictEqual(method, 'GET') | |
| t.assert.strictEqual(url, '/foo/:id') | |
| t.assert.strictEqual(httpStatus, '200') | |
| t.assert.deepStrictEqual(schema, outSchema) | |
| return _data => JSON.stringify({ id: 2 }) | |
| }) | |
| // provoke re-creation of serialization compiler in setupSerializer | |
| fastify.addSchema({ $id: 'dummy', type: 'object' }) | |
| fastify.get('/foo/:id', { | |
| handler (_req, reply) { | |
| reply.send({ id: 1 }) | |
| }, | |
| schema: { | |
| response: { | |
| 200: outSchema | |
| } | |
| } | |
| }) | |
| fastify.inject({ | |
| method: 'GET', | |
| url: '/foo/123' | |
| }, (err, res) => { | |
| t.assert.ifError(err) | |
| t.assert.strictEqual(res.payload, JSON.stringify({ id: 2 })) | |
| testDone() | |
| }) | |
| }) | |
| test('Custom serializer per route', async t => { | |
| const fastify = Fastify() | |
| const outSchema = { | |
| $id: 'test', | |
| type: 'object', | |
| properties: { | |
| mean: { type: 'string' } | |
| } | |
| } | |
| fastify.get('/default', { | |
| handler (req, reply) { reply.send({ mean: 'default' }) }, | |
| schema: { response: { 200: outSchema } } | |
| }) | |
| let hit = 0 | |
| fastify.register((instance, opts, done) => { | |
| instance.setSerializerCompiler(({ schema, method, url, httpStatus }) => { | |
| hit++ | |
| return data => JSON.stringify({ mean: 'custom' }) | |
| }) | |
| instance.get('/custom', { | |
| handler (req, reply) { reply.send({}) }, | |
| schema: { response: { 200: outSchema } } | |
| }) | |
| instance.get('/route', { | |
| handler (req, reply) { reply.send({}) }, | |
| serializerCompiler: ({ schema, method, url, httpPart }) => { | |
| hit++ | |
| return data => JSON.stringify({ mean: 'route' }) | |
| }, | |
| schema: { response: { 200: outSchema } } | |
| }) | |
| done() | |
| }) | |
| let res = await fastify.inject('/default') | |
| t.assert.strictEqual(res.json().mean, 'default') | |
| res = await fastify.inject('/custom') | |
| t.assert.strictEqual(res.json().mean, 'custom') | |
| res = await fastify.inject('/route') | |
| t.assert.strictEqual(res.json().mean, 'route') | |
| t.assert.strictEqual(hit, 4, 'the custom and route serializer has been called') | |
| }) | |
| test('Reply serializer win over serializer ', (t, testDone) => { | |
| t.plan(6) | |
| const fastify = Fastify() | |
| fastify.setReplySerializer(function (payload, statusCode) { | |
| t.assert.deepStrictEqual(payload, { name: 'Foo', work: 'Bar', nick: 'Boo' }) | |
| return 'instance serializator' | |
| }) | |
| fastify.get('/', { | |
| schema: { | |
| response: { | |
| '2xx': { | |
| type: 'object', | |
| properties: { | |
| name: { type: 'string' }, | |
| work: { type: 'string' } | |
| } | |
| } | |
| } | |
| }, | |
| serializerCompiler: ({ schema, method, url, httpPart }) => { | |
| t.assert.ok(method, 'the custom compiler has been created') | |
| return () => { | |
| t.assert.fail('the serializer must not be called when there is a reply serializer') | |
| return 'fail' | |
| } | |
| } | |
| }, function (req, reply) { | |
| reply.code(200).send({ name: 'Foo', work: 'Bar', nick: 'Boo' }) | |
| }) | |
| fastify.inject('/', (err, res) => { | |
| t.assert.ifError(err) | |
| t.assert.deepStrictEqual(res.payload, 'instance serializator') | |
| t.assert.strictEqual(res.statusCode, 200) | |
| testDone() | |
| }) | |
| }) | |
| test('Reply serializer win over serializer ', (t, testDone) => { | |
| t.plan(6) | |
| const fastify = Fastify() | |
| fastify.setReplySerializer(function (payload, statusCode) { | |
| t.assert.deepStrictEqual(payload, { name: 'Foo', work: 'Bar', nick: 'Boo' }) | |
| return 'instance serializator' | |
| }) | |
| fastify.get('/', { | |
| schema: { | |
| response: { | |
| '2xx': { | |
| type: 'object', | |
| properties: { | |
| name: { type: 'string' }, | |
| work: { type: 'string' } | |
| } | |
| } | |
| } | |
| }, | |
| serializerCompiler: ({ schema, method, url, httpPart }) => { | |
| t.assert.ok(method, 'the custom compiler has been created') | |
| return () => { | |
| t.assert.fail('the serializer must not be called when there is a reply serializer') | |
| return 'fail' | |
| } | |
| } | |
| }, function (req, reply) { | |
| reply.code(200).send({ name: 'Foo', work: 'Bar', nick: 'Boo' }) | |
| }) | |
| fastify.inject('/', (err, res) => { | |
| t.assert.ifError(err) | |
| t.assert.deepStrictEqual(res.payload, 'instance serializator') | |
| t.assert.strictEqual(res.statusCode, 200) | |
| testDone() | |
| }) | |
| }) | |
| test('The schema compiler recreate itself if needed', (t, testDone) => { | |
| t.plan(1) | |
| const fastify = Fastify() | |
| fastify.options('/', { | |
| schema: { | |
| response: { '2xx': { hello: { type: 'string' } } } | |
| } | |
| }, echoBody) | |
| fastify.register(function (fastify, options, done) { | |
| fastify.addSchema({ | |
| $id: 'identifier', | |
| type: 'string', | |
| format: 'uuid' | |
| }) | |
| fastify.get('/', { | |
| schema: { | |
| response: { | |
| '2xx': { | |
| foobarId: { $ref: 'identifier#' } | |
| } | |
| } | |
| } | |
| }, echoBody) | |
| done() | |
| }) | |
| fastify.ready(err => { | |
| t.assert.ifError(err) | |
| testDone() | |
| }) | |
| }) | |
| test('The schema changes the default error handler output', async t => { | |
| t.plan(4) | |
| const fastify = Fastify() | |
| fastify.get('/:code', { | |
| schema: { | |
| response: { | |
| '2xx': { hello: { type: 'string' } }, | |
| 501: { | |
| type: 'object', | |
| properties: { | |
| message: { type: 'string' } | |
| } | |
| }, | |
| '5xx': { | |
| type: 'object', | |
| properties: { | |
| customId: { type: 'number' }, | |
| error: { type: 'string' }, | |
| message: { type: 'string' } | |
| } | |
| } | |
| } | |
| } | |
| }, (request, reply) => { | |
| if (request.params.code === '501') { | |
| return reply.code(501).send(new Error('501 message')) | |
| } | |
| const error = new Error('500 message') | |
| error.customId = 42 | |
| reply.send(error) | |
| }) | |
| let res = await fastify.inject('/501') | |
| t.assert.strictEqual(res.statusCode, 501) | |
| t.assert.deepStrictEqual(res.json(), { message: '501 message' }) | |
| res = await fastify.inject('/500') | |
| t.assert.strictEqual(res.statusCode, 500) | |
| t.assert.deepStrictEqual(res.json(), { error: 'Internal Server Error', message: '500 message', customId: 42 }) | |
| }) | |
| test('do not crash if status code serializer errors', async t => { | |
| const fastify = Fastify() | |
| const requiresFoo = { | |
| type: 'object', | |
| properties: { foo: { type: 'string' } }, | |
| required: ['foo'] | |
| } | |
| const someUserErrorType2 = { | |
| type: 'object', | |
| properties: { | |
| customCode: { type: 'number' } | |
| }, | |
| required: ['customCode'] | |
| } | |
| fastify.get( | |
| '/', | |
| { | |
| schema: { | |
| query: requiresFoo, | |
| response: { 400: someUserErrorType2 } | |
| } | |
| }, | |
| (request, reply) => { | |
| t.assert.fail('handler, should not be called') | |
| } | |
| ) | |
| const res = await fastify.inject({ | |
| path: '/', | |
| query: { | |
| notfoo: true | |
| } | |
| }) | |
| t.assert.strictEqual(res.statusCode, 500) | |
| t.assert.deepStrictEqual(res.json(), { | |
| statusCode: 500, | |
| code: 'FST_ERR_FAILED_ERROR_SERIALIZATION', | |
| message: 'Failed to serialize an error. Error: "customCode" is required!. ' + | |
| 'Original error: querystring must have required property \'foo\'' | |
| }) | |
| }) | |
| test('custom schema serializer error, empty message', async t => { | |
| t.plan(2) | |
| const fastify = Fastify() | |
| fastify.get('/:code', { | |
| schema: { | |
| response: { | |
| '2xx': { hello: { type: 'string' } }, | |
| 501: { | |
| type: 'object', | |
| properties: { | |
| message: { type: 'string' } | |
| } | |
| } | |
| } | |
| } | |
| }, (request, reply) => { | |
| if (request.params.code === '501') { | |
| return reply.code(501).send(new Error('')) | |
| } | |
| }) | |
| const res = await fastify.inject('/501') | |
| t.assert.strictEqual(res.statusCode, 501) | |
| t.assert.deepStrictEqual(res.json(), { message: '' }) | |
| }) | |
| test('error in custom schema serialize compiler, throw FST_ERR_SCH_SERIALIZATION_BUILD error', (t, testDone) => { | |
| t.plan(3) | |
| const fastify = Fastify() | |
| fastify.get('/', { | |
| schema: { | |
| response: { | |
| '2xx': { | |
| type: 'object', | |
| properties: { | |
| some: { type: 'string' } | |
| } | |
| }, | |
| 500: { | |
| type: 'object', | |
| properties: { | |
| message: { type: 'string' } | |
| } | |
| } | |
| } | |
| }, | |
| serializerCompiler: () => { | |
| throw new Error('CUSTOM_ERROR') | |
| } | |
| }, function (req, reply) { | |
| reply.code(200).send({ some: 'thing' }) | |
| }) | |
| fastify.ready((err) => { | |
| t.assert.strictEqual(err.message, 'Failed building the serialization schema for GET: /, due to error CUSTOM_ERROR') | |
| t.assert.strictEqual(err.statusCode, 500) | |
| t.assert.strictEqual(err.code, 'FST_ERR_SCH_SERIALIZATION_BUILD') | |
| testDone() | |
| }) | |
| }) | |
| test('Errors in serializer send to errorHandler', async t => { | |
| let savedError | |
| const fastify = Fastify() | |
| fastify.get('/', { | |
| schema: { | |
| response: { | |
| 200: { | |
| type: 'object', | |
| properties: { | |
| name: { type: 'string' }, | |
| power: { type: 'string' } | |
| }, | |
| required: ['name'] | |
| } | |
| } | |
| } | |
| }, function (req, reply) { | |
| reply.code(200).send({ no: 'thing' }) | |
| }) | |
| fastify.setErrorHandler((error, request, reply) => { | |
| savedError = error | |
| reply.code(500).send(error) | |
| }) | |
| const res = await fastify.inject('/') | |
| t.assert.strictEqual(res.statusCode, 500) | |
| // t.assert.deepStrictEqual(savedError, new Error('"name" is required!')); | |
| t.assert.deepStrictEqual(res.json(), { | |
| statusCode: 500, | |
| error: 'Internal Server Error', | |
| message: '"name" is required!' | |
| }) | |
| t.assert.ok(savedError, 'error presents') | |
| t.assert.ok(savedError.serialization, 'Serialization sign presents') | |
| }) | |
| test('capital X', (t, testDone) => { | |
| t.plan(3) | |
| const fastify = Fastify() | |
| fastify.get('/', { | |
| schema: { | |
| response: { | |
| '2XX': { | |
| type: 'object', | |
| properties: { | |
| name: { type: 'string' }, | |
| work: { type: 'string' } | |
| } | |
| } | |
| } | |
| } | |
| }, function (req, reply) { | |
| reply.code(200).send({ name: 'Foo', work: 'Bar', nick: 'Boo' }) | |
| }) | |
| fastify.inject('/', (err, res) => { | |
| t.assert.ifError(err) | |
| t.assert.deepStrictEqual(res.json(), { name: 'Foo', work: 'Bar' }) | |
| t.assert.strictEqual(res.statusCode, 200) | |
| testDone() | |
| }) | |
| }) | |
| test('allow default as status code and used as last fallback', (t, testDone) => { | |
| t.plan(3) | |
| const fastify = Fastify() | |
| fastify.route({ | |
| url: '/', | |
| method: 'GET', | |
| schema: { | |
| response: { | |
| default: { | |
| type: 'object', | |
| properties: { | |
| name: { type: 'string' }, | |
| work: { type: 'string' } | |
| } | |
| } | |
| } | |
| }, | |
| handler: (req, reply) => { | |
| reply.code(200).send({ name: 'Foo', work: 'Bar', nick: 'Boo' }) | |
| } | |
| }) | |
| fastify.inject('/', (err, res) => { | |
| t.assert.ifError(err) | |
| t.assert.deepStrictEqual(res.json(), { name: 'Foo', work: 'Bar' }) | |
| t.assert.strictEqual(res.statusCode, 200) | |
| testDone() | |
| }) | |
| }) | |
Xet Storage Details
- Size:
- 31.3 kB
- Xet hash:
- ddf63864ecdf579d73107893458c1db6c30719e9bdf84b9f279f3ae622fd8d02
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.