Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
"lint:fix": "standard --fix",
"unit": "tap",
"test": "npm run unit && npm run test:typescript",
"posttest": "rimraf test/ajv-generated*.js",
"test:typescript": "tsd",
"ajv:compile": "ajv compile -s test/source.json -o test/validate_schema.js"
},
Expand Down Expand Up @@ -41,7 +40,6 @@
"cronometro": "^1.1.4",
"fastify": "^4.0.0",
"require-from-string": "^2.0.2",
"rimraf": "^3.0.2",
"sanitize-filename": "^1.6.3",
"standard": "^17.0.0",
"tap": "^16.2.0",
Expand Down
308 changes: 161 additions & 147 deletions test/standalone.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,178 +12,192 @@ function generateFileName (routeOpts) {
return `/ajv-generated-${sanitize(routeOpts.schema.$id)}-${routeOpts.method}-${routeOpts.httpPart}-${sanitize(routeOpts.url)}.js`
}

t.test('errors', t => {
t.plan(2)
t.throws(() => {
AjvStandaloneValidator()
}, 'missing restoreFunction')
t.throws(() => {
AjvStandaloneValidator({ 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 generatedFileNames = []

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
}
t.test('standalone', t => {
t.plan(4)

const factory = AjvStandaloneValidator({
readMode: false,
storeFunction (routeOpts, schemaValidationCode) {
t.same(routeOpts, endpointSchema)
t.type(schemaValidationCode, 'string')
fs.writeFileSync(path.join(__dirname, '/ajv-generated.js'), schemaValidationCode)
t.pass('stored the validation function')
t.teardown(async () => {
for (const fileName of generatedFileNames) {
await fs.promises.unlink(path.join(__dirname, fileName))
}
})

const compiler = factory(schemaMap)
compiler(endpointSchema)
t.pass('compiled the endpoint schema')

t.test('usage standalone code', t => {
t.plan(3)
const standaloneValidate = require('./ajv-generated')
t.test('errors', t => {
t.plan(2)
t.throws(() => {
AjvStandaloneValidator()
}, 'missing restoreFunction')
t.throws(() => {
AjvStandaloneValidator({ readMode: false })
}, 'missing storeFunction')
})

const valid = standaloneValidate({ hello: 'world' })
t.ok(valid)
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 invalid = standaloneValidate({ hello: [] })
t.notOk(invalid)
const refSchema = {
$id: 'urn:schema:ref',
type: 'object',
properties: {
hello: { $ref: 'urn:schema:base#/definitions/hello' }
}
}

t.ok(standaloneValidate)
})
})
const endpointSchema = {
schema: {
$id: 'urn:schema:endpoint',
$ref: 'urn:schema:ref'
}
}

t.test('fastify integration - writeMode', async t => {
t.plan(6)

const factory = AjvStandaloneValidator({
readMode: false,
storeFunction (routeOpts, schemaValidationCode) {
const fileName = generateFileName(routeOpts)
t.ok(routeOpts)
fs.writeFileSync(path.join(__dirname, fileName), schemaValidationCode)
t.pass('stored the validation function')
},
restoreFunction () {
t.fail('write mode ON')
const schemaMap = {
[base.$id]: base,
[refSchema.$id]: refSchema
}
})

const app = buildApp(factory)
await app.ready()
})
const factory = AjvStandaloneValidator({
readMode: false,
storeFunction (routeOpts, schemaValidationCode) {
t.same(routeOpts, endpointSchema)
t.type(schemaValidationCode, 'string')
fs.writeFileSync(path.join(__dirname, '/ajv-generated.js'), schemaValidationCode)
generatedFileNames.push('/ajv-generated.js')
t.pass('stored the validation function')
}
})

t.test('fastify integration - readMode', async t => {
t.plan(6)

const factory = AjvStandaloneValidator({
readMode: true,
storeFunction () {
t.fail('read mode ON')
},
restoreFunction (routeOpts) {
t.pass('restore the validation function')
const fileName = generateFileName(routeOpts)
return require(path.join(__dirname, fileName))
}
})
const compiler = factory(schemaMap)
compiler(endpointSchema)
t.pass('compiled the endpoint schema')

const app = buildApp(factory)
await app.ready()
t.test('usage standalone code', t => {
t.plan(3)
const standaloneValidate = require('./ajv-generated')

let res = await app.inject({
url: '/foo',
method: 'POST',
payload: { hello: [] }
})
t.equal(res.statusCode, 400)
const valid = standaloneValidate({ hello: 'world' })
t.ok(valid)

res = await app.inject({
url: '/bar?lang=invalid',
method: 'GET'
})
t.equal(res.statusCode, 400)
const invalid = standaloneValidate({ hello: [] })
t.notOk(invalid)

res = await app.inject({
url: '/bar?lang=it',
method: 'GET'
t.ok(standaloneValidate)
})
})
t.equal(res.statusCode, 200)
})

function buildApp (factory) {
const app = fastify({
jsonShorthand: false,
schemaController: {
compilersFactory: {
buildValidator: factory
t.test('fastify integration - writeMode', async t => {
t.plan(6)

const factory = AjvStandaloneValidator({
readMode: false,
storeFunction (routeOpts, schemaValidationCode) {
const fileName = generateFileName(routeOpts)
t.ok(routeOpts)
fs.writeFileSync(path.join(__dirname, fileName), schemaValidationCode)
t.pass('stored the validation function')
generatedFileNames.push(fileName)
},
restoreFunction () {
t.fail('write mode ON')
}
}
})

const app = buildApp(factory)
await app.ready()
})

app.addSchema({
$id: 'urn:schema:foo',
type: 'object',
properties: {
name: { type: 'string' },
id: { type: 'integer' }
}
t.test('fastify integration - readMode', async t => {
t.plan(6)

const factory = AjvStandaloneValidator({
readMode: true,
storeFunction () {
t.fail('read mode ON')
},
restoreFunction (routeOpts) {
t.pass('restore the validation function')
const fileName = generateFileName(routeOpts)
return require(path.join(__dirname, fileName))
}
})

const app = buildApp(factory)
await app.ready()

let res = await app.inject({
url: '/foo',
method: 'POST',
payload: { hello: [] }
})
t.equal(res.statusCode, 400)

res = await app.inject({
url: '/bar?lang=invalid',
method: 'GET'
})
t.equal(res.statusCode, 400)

res = await app.inject({
url: '/bar?lang=it',
method: 'GET'
})
t.equal(res.statusCode, 200)
})

app.post('/foo', {
schema: {
body: {
$id: 'urn:schema:body',
type: 'object',
properties: {
hello: { $ref: 'urn:schema:foo#/properties/name' }
function buildApp (factory) {
const app = fastify({
jsonShorthand: false,
schemaController: {
compilersFactory: {
buildValidator: factory
}
}
}
}, () => { return 'ok' })

app.get('/bar', {
schema: {
query: {
$id: 'urn:schema:query',
type: 'object',
properties: {
lang: { type: 'string', enum: ['it', 'en'] }
})

app.addSchema({
$id: 'urn:schema:foo',
type: 'object',
properties: {
name: { type: 'string' },
id: { type: 'integer' }
}
})

app.post('/foo', {
schema: {
body: {
$id: 'urn:schema:body',
type: 'object',
properties: {
hello: { $ref: 'urn:schema:foo#/properties/name' }
}
}
}
}
}, () => { return 'ok' })
}, () => { return 'ok' })

app.get('/bar', {
schema: {
query: {
$id: 'urn:schema:query',
type: 'object',
properties: {
lang: { type: 'string', enum: ['it', 'en'] }
}
}
}
}, () => { return 'ok' })

return app
}
return app
}
})