You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					214 lines
				
				4.9 KiB
			
		
		
			
		
	
	
					214 lines
				
				4.9 KiB
			| 
											3 years ago
										 | 'use strict' | ||
|  | 
 | ||
|  | const fs = require('fs') | ||
|  | const path = require('path') | ||
|  | const t = require('tap') | ||
|  | const fastify = require('fastify') | ||
|  | const sanitize = require('sanitize-filename') | ||
|  | 
 | ||
|  | const { StandaloneSerializer: FjsStandaloneCompiler } = require('../') | ||
|  | 
 | ||
|  | function generateFileName (routeOpts) { | ||
|  |   return `/fjs-generated-${sanitize(routeOpts.schema.$id)}-${routeOpts.method}-${routeOpts.httpPart}-${sanitize(routeOpts.url)}.js` | ||
|  | } | ||
|  | 
 | ||
|  | t.test('errors', t => { | ||
|  |   t.plan(2) | ||
|  |   t.throws(() => { | ||
|  |     FjsStandaloneCompiler() | ||
|  |   }, 'missing restoreFunction') | ||
|  |   t.throws(() => { | ||
|  |     FjsStandaloneCompiler({ readMode: false }) | ||
|  |   }, 'missing storeFunction') | ||
|  | }) | ||
|  | 
 | ||
|  | t.test('generate standalone code', t => { | ||
|  |   t.plan(5) | ||
|  | 
 | ||
|  |   const base = { | ||
|  |     $id: 'urn:schema:base', | ||
|  |     definitions: { | ||
|  |       hello: { type: 'string' } | ||
|  |     }, | ||
|  |     type: 'object', | ||
|  |     properties: { | ||
|  |       hello: { $ref: '#/definitions/hello' } | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   const refSchema = { | ||
|  |     $id: 'urn:schema:ref', | ||
|  |     type: 'object', | ||
|  |     properties: { | ||
|  |       hello: { $ref: 'urn:schema:base#/definitions/hello' } | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   const endpointSchema = { | ||
|  |     schema: { | ||
|  |       $id: 'urn:schema:endpoint', | ||
|  |       $ref: 'urn:schema:ref' | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   const schemaMap = { | ||
|  |     [base.$id]: base, | ||
|  |     [refSchema.$id]: refSchema | ||
|  |   } | ||
|  | 
 | ||
|  |   const factory = FjsStandaloneCompiler({ | ||
|  |     readMode: false, | ||
|  |     storeFunction (routeOpts, schemaSerializerCode) { | ||
|  |       t.same(routeOpts, endpointSchema) | ||
|  |       t.type(schemaSerializerCode, 'string') | ||
|  |       fs.writeFileSync(path.join(__dirname, '/fjs-generated.js'), schemaSerializerCode) | ||
|  |       t.pass('stored the serializer function') | ||
|  |     } | ||
|  |   }) | ||
|  | 
 | ||
|  |   const compiler = factory(schemaMap) | ||
|  |   compiler(endpointSchema) | ||
|  |   t.pass('compiled the endpoint schema') | ||
|  | 
 | ||
|  |   t.test('usage standalone code', t => { | ||
|  |     t.plan(3) | ||
|  |     const standaloneSerializer = require('./fjs-generated') | ||
|  |     t.ok(standaloneSerializer) | ||
|  | 
 | ||
|  |     const valid = standaloneSerializer({ hello: 'world' }) | ||
|  |     t.same(valid, JSON.stringify({ hello: 'world' })) | ||
|  | 
 | ||
|  |     const invalid = standaloneSerializer({ hello: [] }) | ||
|  |     t.same(invalid, '{"hello":""}') | ||
|  |   }) | ||
|  | }) | ||
|  | 
 | ||
|  | t.test('fastify integration - writeMode', async t => { | ||
|  |   t.plan(4) | ||
|  | 
 | ||
|  |   const factory = FjsStandaloneCompiler({ | ||
|  |     readMode: false, | ||
|  |     storeFunction (routeOpts, schemaSerializationCode) { | ||
|  |       const fileName = generateFileName(routeOpts) | ||
|  |       t.ok(routeOpts) | ||
|  |       fs.writeFileSync(path.join(__dirname, fileName), schemaSerializationCode) | ||
|  |       t.pass(`stored the serializer function ${fileName}`) | ||
|  |     }, | ||
|  |     restoreFunction () { | ||
|  |       t.fail('write mode ON') | ||
|  |     } | ||
|  |   }) | ||
|  | 
 | ||
|  |   const app = buildApp(factory) | ||
|  |   await app.ready() | ||
|  | }) | ||
|  | 
 | ||
|  | t.test('fastify integration - writeMode forces standalone', async t => { | ||
|  |   t.plan(4) | ||
|  | 
 | ||
|  |   const factory = FjsStandaloneCompiler({ | ||
|  |     readMode: false, | ||
|  |     storeFunction (routeOpts, schemaSerializationCode) { | ||
|  |       const fileName = generateFileName(routeOpts) | ||
|  |       t.ok(routeOpts) | ||
|  |       fs.writeFileSync(path.join(__dirname, fileName), schemaSerializationCode) | ||
|  |       t.pass(`stored the serializer function ${fileName}`) | ||
|  |     }, | ||
|  |     restoreFunction () { | ||
|  |       t.fail('write mode ON') | ||
|  |     } | ||
|  |   }) | ||
|  | 
 | ||
|  |   const app = buildApp(factory, { | ||
|  |     mode: 'not-standalone', | ||
|  |     rounding: 'ceil' | ||
|  |   }) | ||
|  | 
 | ||
|  |   await app.ready() | ||
|  | }) | ||
|  | 
 | ||
|  | t.test('fastify integration - readMode', async t => { | ||
|  |   t.plan(6) | ||
|  | 
 | ||
|  |   const factory = FjsStandaloneCompiler({ | ||
|  |     readMode: true, | ||
|  |     storeFunction () { | ||
|  |       t.fail('read mode ON') | ||
|  |     }, | ||
|  |     restoreFunction (routeOpts) { | ||
|  |       const fileName = generateFileName(routeOpts) | ||
|  |       t.pass(`restore the serializer function ${fileName}}`) | ||
|  |       return require(path.join(__dirname, fileName)) | ||
|  |     } | ||
|  |   }) | ||
|  | 
 | ||
|  |   const app = buildApp(factory) | ||
|  |   await app.ready() | ||
|  | 
 | ||
|  |   let res = await app.inject({ | ||
|  |     url: '/foo', | ||
|  |     method: 'POST' | ||
|  |   }) | ||
|  |   t.equal(res.statusCode, 200) | ||
|  |   t.equal(res.payload, JSON.stringify({ hello: 'world' })) | ||
|  | 
 | ||
|  |   res = await app.inject({ | ||
|  |     url: '/bar?lang=it', | ||
|  |     method: 'GET' | ||
|  |   }) | ||
|  |   t.equal(res.statusCode, 200) | ||
|  |   t.equal(res.payload, JSON.stringify({ lang: 'en' })) | ||
|  | }) | ||
|  | 
 | ||
|  | function buildApp (factory, serializerOpts) { | ||
|  |   const app = fastify({ | ||
|  |     exposeHeadRoutes: false, | ||
|  |     jsonShorthand: false, | ||
|  |     schemaController: { | ||
|  |       compilersFactory: { | ||
|  |         buildSerializer: factory | ||
|  |       } | ||
|  |     }, | ||
|  |     serializerOpts | ||
|  |   }) | ||
|  | 
 | ||
|  |   app.addSchema({ | ||
|  |     $id: 'urn:schema:foo', | ||
|  |     type: 'object', | ||
|  |     properties: { | ||
|  |       name: { type: 'string' }, | ||
|  |       id: { type: 'integer' } | ||
|  |     } | ||
|  |   }) | ||
|  | 
 | ||
|  |   app.post('/foo', { | ||
|  |     schema: { | ||
|  |       response: { | ||
|  |         200: { | ||
|  |           $id: 'urn:schema:response', | ||
|  |           type: 'object', | ||
|  |           properties: { | ||
|  |             hello: { $ref: 'urn:schema:foo#/properties/name' } | ||
|  |           } | ||
|  |         } | ||
|  |       } | ||
|  |     } | ||
|  |   }, () => { return { hello: 'world' } }) | ||
|  | 
 | ||
|  |   app.get('/bar', { | ||
|  |     schema: { | ||
|  |       response: { | ||
|  |         200: { | ||
|  |           $id: 'urn:schema:response:bar', | ||
|  |           type: 'object', | ||
|  |           properties: { | ||
|  |             lang: { type: 'string', enum: ['it', 'en'] } | ||
|  |           } | ||
|  |         } | ||
|  |       } | ||
|  |     } | ||
|  |   }, () => { return { lang: 'en' } }) | ||
|  | 
 | ||
|  |   return app | ||
|  | } |