Aktueller Stand

This commit is contained in:
2026-01-22 19:05:45 +01:00
parent 85dee61a4d
commit e280e4eadb
1967 changed files with 397327 additions and 74093 deletions

View File

@@ -1,6 +1,6 @@
'use strict'
const t = require('tap')
const { test } = require('node:test')
const AjvCompiler = require('../index')
const postSchema = Object.freeze({
@@ -37,23 +37,19 @@ const fastifyAjvOptionsDefault = Object.freeze({
customOptions: {}
})
t.test('must not store schema on compile', t => {
t.plan(4)
test('must not store schema on compile', t => {
t.plan(5)
const factory = AjvCompiler()
const compiler = factory({}, fastifyAjvOptionsDefault)
const postFn = compiler({ schema: postSchema })
const patchFn = compiler({ schema: patchSchema })
const resultForPost = postFn({})
t.equal(resultForPost, false)
t.has(postFn.errors, [
{
keyword: 'required',
message: "must have required property 'username'"
}
])
t.assert.deepStrictEqual(resultForPost, false)
t.assert.deepStrictEqual(postFn.errors[0].keyword, 'required')
t.assert.deepStrictEqual(postFn.errors[0].message, "must have required property 'username'")
const resultForPatch = patchFn({})
t.ok(resultForPatch)
t.notOk(patchFn.errors)
t.assert.ok(resultForPatch)
t.assert.ok(!patchFn.errors)
})

View File

@@ -1,6 +1,6 @@
'use strict'
const t = require('tap')
const { test } = require('node:test')
const fastify = require('fastify')
const AjvCompiler = require('../index')
@@ -45,16 +45,16 @@ const fastifyAjvOptionsCustom = Object.freeze({
]
})
t.test('basic usage', t => {
test('basic usage', t => {
t.plan(1)
const factory = AjvCompiler()
const compiler = factory(externalSchemas1, fastifyAjvOptionsDefault)
const validatorFunc = compiler({ schema: sampleSchema })
const result = validatorFunc({ name: 'hello' })
t.equal(result, true)
t.assert.deepStrictEqual(result, true)
})
t.test('array coercion', t => {
test('array coercion', t => {
t.plan(2)
const factory = AjvCompiler()
const compiler = factory(externalSchemas1, fastifyAjvOptionsDefault)
@@ -70,11 +70,11 @@ t.test('array coercion', t => {
const validatorFunc = compiler({ schema: arraySchema })
const inputObj = { name: 'hello' }
t.equal(validatorFunc(inputObj), true)
t.same(inputObj, { name: ['hello'] }, 'the name property should be coerced to an array')
t.assert.deepStrictEqual(validatorFunc(inputObj), true)
t.assert.deepStrictEqual(inputObj, { name: ['hello'] }, 'the name property should be coerced to an array')
})
t.test('nullable default', t => {
test('nullable default', t => {
t.plan(2)
const factory = AjvCompiler()
const compiler = factory({}, fastifyAjvOptionsDefault)
@@ -89,11 +89,11 @@ t.test('nullable default', t => {
})
const input = { nullable: null, notNullable: null }
const result = validatorFunc(input)
t.equal(result, true)
t.same(input, { nullable: null, notNullable: '' }, 'the notNullable field has been coerced')
t.assert.deepStrictEqual(result, true)
t.assert.deepStrictEqual(input, { nullable: null, notNullable: '' }, 'the notNullable field has been coerced')
})
t.test('plugin loading', t => {
test('plugin loading', t => {
t.plan(3)
const factory = AjvCompiler()
const compiler = factory(externalSchemas1, fastifyAjvOptionsCustom)
@@ -113,30 +113,30 @@ t.test('plugin loading', t => {
}
})
const result = validatorFunc({ q: '2016-10-02' })
t.equal(result, true)
t.assert.deepStrictEqual(result, true)
const resultFail = validatorFunc({})
t.equal(resultFail, false)
t.equal(validatorFunc.errors[0].message, 'hello world')
t.assert.deepStrictEqual(resultFail, false)
t.assert.deepStrictEqual(validatorFunc.errors[0].message, 'hello world')
})
t.test('optimization - cache ajv instance', t => {
test('optimization - cache ajv instance', t => {
t.plan(5)
const factory = AjvCompiler()
const compiler1 = factory(externalSchemas1, fastifyAjvOptionsDefault)
const compiler2 = factory(externalSchemas1, fastifyAjvOptionsDefault)
t.equal(compiler1, compiler2, 'same instance')
t.same(compiler1, compiler2, 'same instance')
t.assert.deepStrictEqual(compiler1, compiler2, 'same instance')
t.assert.deepStrictEqual(compiler1, compiler2, 'same instance')
const compiler3 = factory(externalSchemas2, fastifyAjvOptionsDefault)
t.not(compiler3, compiler1, 'new ajv instance when externa schema change')
t.assert.notEqual(compiler3, compiler1, 'new ajv instance when externa schema change')
const compiler4 = factory(externalSchemas1, fastifyAjvOptionsCustom)
t.not(compiler4, compiler1, 'new ajv instance when externa schema change')
t.not(compiler4, compiler3, 'new ajv instance when externa schema change')
t.assert.notEqual(compiler4, compiler1, 'new ajv instance when externa schema change')
t.assert.notEqual(compiler4, compiler3, 'new ajv instance when externa schema change')
})
t.test('the onCreate callback can enhance the ajv instance', t => {
test('the onCreate callback can enhance the ajv instance', t => {
t.plan(2)
const factory = AjvCompiler()
@@ -161,14 +161,14 @@ t.test('the onCreate callback can enhance the ajv instance', t => {
}
})
const result = validatorFunc('foo')
t.equal(result, true)
t.assert.deepStrictEqual(result, true)
const resultFail = validatorFunc('2016-10-02')
t.equal(resultFail, false)
t.assert.deepStrictEqual(resultFail, false)
})
// https://github.com/fastify/fastify/pull/2969
t.test('compile same $id when in external schema', t => {
test('compile same $id when in external schema', t => {
t.plan(3)
const factory = AjvCompiler()
@@ -197,7 +197,7 @@ t.test('compile same $id when in external schema', t => {
}, fastifyAjvOptionsDefault)
t.notOk(compiler[sym], 'the ajv reference do not exists if code is not activated')
t.assert.ok(!compiler[sym], 'the ajv reference do not exists if code is not activated')
const validatorFunc1 = compiler({
schema: {
@@ -211,11 +211,11 @@ t.test('compile same $id when in external schema', t => {
}
})
t.pass('the compile does not fail if the schema compiled is already in the external schemas')
t.equal(validatorFunc1, validatorFunc2, 'the returned function is the same')
t.assert.ok('the compile does not fail if the schema compiled is already in the external schemas')
t.assert.deepStrictEqual(validatorFunc1, validatorFunc2, 'the returned function is the same')
})
t.test('JTD MODE', t => {
test('JTD MODE', async t => {
t.plan(2)
t.test('compile jtd schema', t => {
@@ -240,23 +240,23 @@ t.test('JTD MODE', t => {
const compiler = factory({}, fastifyJtdDefault)
const validatorFunc = compiler({ schema: jtdSchema })
t.pass('generated validation function for JTD SCHEMA')
t.assert.ok('generated validation function for JTD SCHEMA')
const result = validatorFunc({
version: '2',
foo: []
})
t.notOk(result, 'failed validation')
t.type(validatorFunc.errors, 'Array')
t.assert.ok(!result, 'failed validation')
t.assert.ok(validatorFunc.errors instanceof Array)
const success = validatorFunc({
version: '1',
foo: 42
})
t.ok(success)
t.assert.ok(success)
})
t.test('fastify integration', async t => {
await t.test('fastify integration', async t => {
const factory = AjvCompiler()
const app = fastify({
@@ -301,7 +301,7 @@ t.test('JTD MODE', t => {
}
})
t.equal(res.statusCode, 400)
t.equal(res.json().message, 'body/foo must be uint8')
t.assert.deepStrictEqual(res.statusCode, 400)
t.assert.deepStrictEqual(res.json().message, 'body/foo must be uint8')
})
})

View File

@@ -1,6 +1,6 @@
'use strict'
const t = require('tap')
const { test } = require('node:test')
const fastify = require('fastify')
const AjvCompiler = require('../index')
@@ -8,7 +8,7 @@ const ajvFormats = require('ajv-formats')
const ajvErrors = require('ajv-errors')
const localize = require('ajv-i18n')
t.test('Format Baseline test', async (t) => {
test('Format Baseline test', async (t) => {
const app = buildApplication({
customOptions: {
validateFormats: false
@@ -28,12 +28,12 @@ t.test('Format Baseline test', async (t) => {
email: 'not an email'
}
})
t.equal(res.statusCode, 200, 'format validation does not apply as configured')
t.equal(res.payload, 'hello')
t.assert.deepStrictEqual(res.statusCode, 200, 'format validation does not apply as configured')
t.assert.deepStrictEqual(res.payload, 'hello')
})
t.test('Custom Format plugin loading test', (t) => {
t.plan(6)
test('Custom Format plugin loading test', async (t) => {
t.plan(3)
const app = buildApplication({
customOptions: {
validateFormats: true
@@ -41,17 +41,13 @@ t.test('Custom Format plugin loading test', (t) => {
plugins: [[ajvFormats, { mode: 'fast' }]]
})
app.inject('/hello', (err, res) => {
t.error(err)
t.equal(res.statusCode, 400, 'format validation applies')
})
const res = await app.inject('/hello')
t.assert.deepStrictEqual(res.statusCode, 400, 'format validation applies')
app.inject('/2ad0612c-7578-4b18-9a6f-579863f40e0b', (err, res) => {
t.error(err)
t.equal(res.statusCode, 400, 'format validation applies')
})
const res1 = await app.inject('/2ad0612c-7578-4b18-9a6f-579863f40e0b')
t.assert.deepStrictEqual(res1.statusCode, 400, 'format validation applies')
app.inject({
const res2 = await app.inject({
url: '/2ad0612c-7578-4b18-9a6f-579863f40e0b',
headers: {
'x-foo': 'hello',
@@ -63,27 +59,21 @@ t.test('Custom Format plugin loading test', (t) => {
date: new Date().toISOString(),
email: 'foo@bar.baz'
}
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 200)
})
t.assert.deepStrictEqual(res2.statusCode, 200)
})
t.test('Format plugin set by default test', (t) => {
t.plan(6)
test('Format plugin set by default test', async (t) => {
t.plan(3)
const app = buildApplication({})
app.inject('/hello', (err, res) => {
t.error(err)
t.equal(res.statusCode, 400, 'format validation applies')
})
const res = await app.inject('/hello')
t.assert.deepStrictEqual(res.statusCode, 400, 'format validation applies')
app.inject('/2ad0612c-7578-4b18-9a6f-579863f40e0b', (err, res) => {
t.error(err)
t.equal(res.statusCode, 400, 'format validation applies')
})
const res1 = await app.inject('/2ad0612c-7578-4b18-9a6f-579863f40e0b')
t.assert.deepStrictEqual(res1.statusCode, 400, 'format validation applies')
app.inject({
const res2 = await app.inject({
url: '/2ad0612c-7578-4b18-9a6f-579863f40e0b',
headers: {
'x-foo': 'hello',
@@ -95,14 +85,12 @@ t.test('Format plugin set by default test', (t) => {
date: new Date().toISOString(),
email: 'foo@bar.baz'
}
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 200)
})
t.assert.deepStrictEqual(res2.statusCode, 200)
})
t.test('Custom error messages', (t) => {
t.plan(9)
test('Custom error messages', async (t) => {
t.plan(6)
const app = buildApplication({
customOptions: {
@@ -120,7 +108,7 @@ t.test('Custom error messages', (t) => {
}
app.post('/', {
handler: () => { t.fail('dont call me') },
handler: () => { t.assert.fail('dont call me') },
schema: {
body: {
type: 'object',
@@ -134,39 +122,34 @@ t.test('Custom error messages', (t) => {
}
})
app.inject({
const res = await app.inject({
url: '/',
method: 'post',
payload: {}
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 400)
t.match(res.json().message, errorMessage.required)
})
t.assert.deepStrictEqual(res.statusCode, 400)
t.assert.ok(res.json().message.includes(errorMessage.required))
app.inject({
const res1 = await app.inject({
url: '/',
method: 'post',
payload: { foo: 'not a number' }
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 400)
t.match(res.json().message, errorMessage.type)
})
t.assert.deepStrictEqual(res1.statusCode, 400)
t.assert.ok(res1.json().message.includes(errorMessage.type))
app.inject({
const res2 = await app.inject({
url: '/',
method: 'post',
payload: { foo: 3, bar: 'ops' }
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 400)
t.match(res.json().message, errorMessage.additionalProperties)
})
t.assert.deepStrictEqual(res2.statusCode, 400)
t.assert.ok(res2.json().message.includes(errorMessage.additionalProperties))
})
t.test('Custom i18n error messages', (t) => {
t.plan(3)
test('Custom i18n error messages', async (t) => {
t.plan(2)
const app = buildApplication({
customOptions: {
@@ -177,7 +160,7 @@ t.test('Custom i18n error messages', (t) => {
})
app.post('/', {
handler: () => { t.fail('dont call me') },
handler: () => { t.assert.fail('dont call me') },
schema: {
body: {
type: 'object',
@@ -190,25 +173,24 @@ t.test('Custom i18n error messages', (t) => {
})
app.setErrorHandler((error, request, reply) => {
t.pass('Error handler executed')
t.assert.ok('Error handler executed')
if (error.validation) {
localize.ru(error.validation)
reply.status(400).send(error.validation)
return
}
t.fail('not other errors')
t.assert.fail('not other errors')
})
app.inject({
const res = await app.inject({
method: 'POST',
url: '/',
payload: {
foo: 'string'
}
}, (err, res) => {
t.error(err)
t.equal(res.json()[0].message, 'должно быть integer')
})
t.assert.deepStrictEqual(res.json()[0].message, 'должно быть integer')
})
function buildApplication (ajvOptions) {

View File

@@ -1,6 +1,6 @@
'use strict'
const t = require('tap')
const { test } = require('node:test')
const fastify = require('fastify')
const AjvCompiler = require('../index')
@@ -38,18 +38,18 @@ const fastifyAjvOptionsDefault = Object.freeze({
customOptions: {}
})
t.test('basic serializer usage', t => {
test('basic serializer usage', t => {
t.plan(4)
const factory = AjvCompiler({ jtdSerializer: true })
const compiler = factory(externalSchemas1, fastifyAjvOptionsDefault)
const serializeFunc = compiler({ schema: jtdSchema })
t.equal(serializeFunc({ version: '1', foo: 42 }), '{"version":"1","foo":42}')
t.equal(serializeFunc({ version: '2', foo: 'hello' }), '{"version":"2","foo":"hello"}')
t.equal(serializeFunc({ version: '3', foo: 'hello' }), '{"version":"3"}')
t.equal(serializeFunc({ version: '2', foo: ['not', 1, { string: 'string' }] }), '{"version":"2","foo":"not,1,[object Object]"}')
t.assert.deepStrictEqual(serializeFunc({ version: '1', foo: 42 }), '{"version":"1","foo":42}')
t.assert.deepStrictEqual(serializeFunc({ version: '2', foo: 'hello' }), '{"version":"2","foo":"hello"}')
t.assert.deepStrictEqual(serializeFunc({ version: '3', foo: 'hello' }), '{"version":"3"}')
t.assert.deepStrictEqual(serializeFunc({ version: '2', foo: ['not', 1, { string: 'string' }] }), '{"version":"2","foo":"not,1,[object Object]"}')
})
t.test('external schemas are ignored', t => {
test('external schemas are ignored', t => {
t.plan(1)
const factory = AjvCompiler({ jtdSerializer: true })
const compiler = factory(externalSchemas2, fastifyAjvOptionsDefault)
@@ -69,13 +69,13 @@ t.test('external schemas are ignored', t => {
}
}
})
t.equal(serializeFunc(
t.assert.deepStrictEqual(serializeFunc(
{ userLoc: { lat: 50, lng: -90 }, serverLoc: { lat: -15, lng: 50 } }),
'{"userLoc":{"lat":50,"lng":-90},"serverLoc":{"lat":-15,"lng":50}}'
)
})
t.test('fastify integration within JTD serializer', async t => {
test('fastify integration within JTD serializer', async t => {
const factoryValidator = AjvCompiler()
const factorySerializer = AjvCompiler({ jtdSerializer: true })
@@ -128,8 +128,8 @@ t.test('fastify integration within JTD serializer', async t => {
}
})
t.equal(res.statusCode, 400)
t.same(res.json(), { version: 'undefined' })
t.assert.deepStrictEqual(res.statusCode, 400)
t.assert.deepStrictEqual(res.json(), { version: 'undefined' })
}
{
@@ -142,8 +142,8 @@ t.test('fastify integration within JTD serializer', async t => {
}
})
t.equal(res.statusCode, 200)
t.same(res.json(), {
t.assert.deepStrictEqual(res.statusCode, 200)
t.assert.deepStrictEqual(res.json(), {
id: '123',
createdAt: '1999-01-31T23:00:00.000Z',
karma: 42,
@@ -152,7 +152,7 @@ t.test('fastify integration within JTD serializer', async t => {
}
})
t.test('fastify integration and cached serializer', async t => {
test('fastify integration and cached serializer', async t => {
const factoryValidator = AjvCompiler()
const factorySerializer = AjvCompiler({ jtdSerializer: true })
@@ -218,8 +218,8 @@ t.test('fastify integration and cached serializer', async t => {
}
})
t.equal(res.statusCode, 400)
t.same(res.json(), { version: 'undefined' })
t.assert.deepStrictEqual(res.statusCode, 400)
t.assert.deepStrictEqual(res.json(), { version: 'undefined' })
}
{
@@ -232,8 +232,8 @@ t.test('fastify integration and cached serializer', async t => {
}
})
t.equal(res.statusCode, 200)
t.same(res.json(), {
t.assert.deepStrictEqual(res.statusCode, 200)
t.assert.deepStrictEqual(res.json(), {
id: '123',
createdAt: '1999-01-31T23:00:00.000Z',
karma: 42,
@@ -242,7 +242,7 @@ t.test('fastify integration and cached serializer', async t => {
}
})
t.test('fastify integration within JTD serializer and custom options', async t => {
test('fastify integration within JTD serializer and custom options', async t => {
const factorySerializer = AjvCompiler({ jtdSerializer: true })
const app = fastify({
@@ -272,8 +272,8 @@ t.test('fastify integration within JTD serializer and custom options', async t =
try {
await app.ready()
t.fail('should throw')
t.assert.fail('should throw')
} catch (error) {
t.equal(error.message, 'logger must implement log, warn and error methods', 'the wrong setting is forwarded to ajv/jtd')
t.assert.deepStrictEqual(error.message, 'logger must implement log, warn and error methods', 'the wrong setting is forwarded to ajv/jtd')
}
})

View File

@@ -2,7 +2,7 @@
const fs = require('node:fs')
const path = require('node:path')
const t = require('tap')
const { test } = require('node:test')
const fastify = require('fastify')
const sanitize = require('sanitize-filename')
@@ -14,10 +14,10 @@ function generateFileName (routeOpts) {
const generatedFileNames = []
t.test('standalone', t => {
test('standalone', async t => {
t.plan(4)
t.teardown(async () => {
t.after(async () => {
for (const fileName of generatedFileNames) {
await fs.promises.unlink(path.join(__dirname, fileName))
}
@@ -25,10 +25,10 @@ t.test('standalone', t => {
t.test('errors', t => {
t.plan(2)
t.throws(() => {
t.assert.throws(() => {
AjvStandaloneValidator()
}, 'missing restoreFunction')
t.throws(() => {
t.assert.throws(() => {
AjvStandaloneValidator({ readMode: false })
}, 'missing storeFunction')
})
@@ -70,29 +70,29 @@ t.test('standalone', t => {
const factory = AjvStandaloneValidator({
readMode: false,
storeFunction (routeOpts, schemaValidationCode) {
t.same(routeOpts, endpointSchema)
t.type(schemaValidationCode, 'string')
t.assert.deepStrictEqual(routeOpts, endpointSchema)
t.assert.ok(typeof schemaValidationCode === 'string')
fs.writeFileSync(path.join(__dirname, '/ajv-generated.js'), schemaValidationCode)
generatedFileNames.push('/ajv-generated.js')
t.pass('stored the validation function')
t.assert.ok('stored the validation function')
}
})
const compiler = factory(schemaMap)
compiler(endpointSchema)
t.pass('compiled the endpoint schema')
t.assert.ok('compiled the endpoint schema')
t.test('usage standalone code', t => {
t.plan(3)
const standaloneValidate = require('./ajv-generated')
const valid = standaloneValidate({ hello: 'world' })
t.ok(valid)
t.assert.ok(valid)
const invalid = standaloneValidate({ hello: [] })
t.notOk(invalid)
t.assert.ok(!invalid)
t.ok(standaloneValidate)
t.assert.ok(standaloneValidate)
})
})
@@ -103,13 +103,13 @@ t.test('standalone', t => {
readMode: false,
storeFunction (routeOpts, schemaValidationCode) {
const fileName = generateFileName(routeOpts)
t.ok(routeOpts)
t.assert.ok(routeOpts)
fs.writeFileSync(path.join(__dirname, fileName), schemaValidationCode)
t.pass('stored the validation function')
t.assert.ok('stored the validation function')
generatedFileNames.push(fileName)
},
restoreFunction () {
t.fail('write mode ON')
t.assert.fail('write mode ON')
}
})
@@ -117,16 +117,16 @@ t.test('standalone', t => {
await app.ready()
})
t.test('fastify integration - readMode', async t => {
await t.test('fastify integration - readMode', async t => {
t.plan(6)
const factory = AjvStandaloneValidator({
readMode: true,
storeFunction () {
t.fail('read mode ON')
t.assert.fail('read mode ON')
},
restoreFunction (routeOpts) {
t.pass('restore the validation function')
t.assert.ok('restore the validation function')
const fileName = generateFileName(routeOpts)
return require(path.join(__dirname, fileName))
}
@@ -140,19 +140,19 @@ t.test('standalone', t => {
method: 'POST',
payload: { hello: [] }
})
t.equal(res.statusCode, 400)
t.assert.deepStrictEqual(res.statusCode, 400)
res = await app.inject({
url: '/bar?lang=invalid',
method: 'GET'
})
t.equal(res.statusCode, 400)
t.assert.deepStrictEqual(res.statusCode, 400)
res = await app.inject({
url: '/bar?lang=it',
method: 'GET'
})
t.equal(res.statusCode, 200)
t.assert.deepStrictEqual(res.statusCode, 200)
})
function buildApp (factory) {