Publish
This commit is contained in:
33
test/runtime/compiler-ajv/any.ts
Normal file
33
test/runtime/compiler-ajv/any.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok } from './validate'
|
||||
|
||||
describe('compiler-ajv/Any', () => {
|
||||
it('Should validate number', () => {
|
||||
const T = Type.Any()
|
||||
Ok(T, 1)
|
||||
})
|
||||
it('Should validate string', () => {
|
||||
const T = Type.Any()
|
||||
Ok(T, 'hello')
|
||||
})
|
||||
it('Should validate boolean', () => {
|
||||
const T = Type.Any()
|
||||
Ok(T, true)
|
||||
})
|
||||
it('Should validate array', () => {
|
||||
const T = Type.Any()
|
||||
Ok(T, [1, 2, 3])
|
||||
})
|
||||
it('Should validate object', () => {
|
||||
const T = Type.Any()
|
||||
Ok(T, { a: 1, b: 2 })
|
||||
})
|
||||
it('Should validate null', () => {
|
||||
const T = Type.Any()
|
||||
Ok(T, null)
|
||||
})
|
||||
it('Should validate undefined', () => {
|
||||
const T = Type.Any()
|
||||
Ok(T, undefined)
|
||||
})
|
||||
})
|
||||
186
test/runtime/compiler-ajv/array.ts
Normal file
186
test/runtime/compiler-ajv/array.ts
Normal file
@@ -0,0 +1,186 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler-ajv/Array', () => {
|
||||
it('Should validate an array of any', () => {
|
||||
const T = Type.Array(Type.Any())
|
||||
Ok(T, [0, true, 'hello', {}])
|
||||
})
|
||||
it('Should not validate varying array when item is number', () => {
|
||||
const T = Type.Array(Type.Number())
|
||||
Fail(T, [1, 2, 3, 'hello'])
|
||||
})
|
||||
it('Should validate for an array of unions', () => {
|
||||
const T = Type.Array(Type.Union([Type.Number(), Type.String()]))
|
||||
Ok(T, [1, 'hello', 3, 'world'])
|
||||
})
|
||||
it('Should not validate for an array of unions where item is not in union.', () => {
|
||||
const T = Type.Array(Type.Union([Type.Number(), Type.String()]))
|
||||
Fail(T, [1, 'hello', 3, 'world', true])
|
||||
})
|
||||
it('Should validate for an empty array', () => {
|
||||
const T = Type.Array(Type.Union([Type.Number(), Type.String()]))
|
||||
Ok(T, [])
|
||||
})
|
||||
it('Should validate for an array of intersection types', () => {
|
||||
const A = Type.Object({ a: Type.String() })
|
||||
const B = Type.Object({ b: Type.String() })
|
||||
const C = Type.Intersect([A, B])
|
||||
const T = Type.Array(C)
|
||||
Ok(T, [
|
||||
{ a: 'hello', b: 'hello' },
|
||||
{ a: 'hello', b: 'hello' },
|
||||
{ a: 'hello', b: 'hello' },
|
||||
])
|
||||
})
|
||||
it('Should not validate for an array of composite types when passing additionalProperties false', () => {
|
||||
const A = Type.Object({ a: Type.String() })
|
||||
const B = Type.Object({ b: Type.String() })
|
||||
const C = Type.Composite([A, B], { additionalProperties: false })
|
||||
const T = Type.Array(C)
|
||||
Fail(T, [
|
||||
{ a: 'hello', b: 'hello' },
|
||||
{ a: 'hello', b: 'hello' },
|
||||
{ a: 'hello', b: 'hello', c: 'additional' },
|
||||
])
|
||||
})
|
||||
it('Should validate an array of tuples', () => {
|
||||
const A = Type.String()
|
||||
const B = Type.Number()
|
||||
const C = Type.Tuple([A, B])
|
||||
const T = Type.Array(C)
|
||||
Ok(T, [
|
||||
['hello', 1],
|
||||
['hello', 1],
|
||||
['hello', 1],
|
||||
])
|
||||
})
|
||||
it('Should not validate an array of tuples when tuple values are incorrect', () => {
|
||||
const A = Type.String()
|
||||
const B = Type.Number()
|
||||
const C = Type.Tuple([A, B])
|
||||
const T = Type.Array(C)
|
||||
Fail(T, [
|
||||
[1, 'hello'],
|
||||
[1, 'hello'],
|
||||
[1, 'hello'],
|
||||
])
|
||||
})
|
||||
it('Should not validate array with failed minItems', () => {
|
||||
const T = Type.Array(Type.Number(), { minItems: 3 })
|
||||
Fail(T, [0, 1])
|
||||
})
|
||||
it('Should not validate array with failed maxItems', () => {
|
||||
const T = Type.Array(Type.Number(), { maxItems: 3 })
|
||||
Fail(T, [0, 1, 2, 3])
|
||||
})
|
||||
// ---------------------------------------------------------
|
||||
// Unique Items
|
||||
// ---------------------------------------------------------
|
||||
it('Should validate array with uniqueItems when items are distinct objects', () => {
|
||||
const T = Type.Array(Type.Object({ x: Type.Number(), y: Type.Number() }), { uniqueItems: true })
|
||||
Ok(T, [
|
||||
{ x: 0, y: 1 },
|
||||
{ x: 1, y: 0 },
|
||||
])
|
||||
})
|
||||
it('Should not validate array with uniqueItems when items are not distinct objects', () => {
|
||||
const T = Type.Array(Type.Object({ x: Type.Number(), y: Type.Number() }), { uniqueItems: true })
|
||||
Fail(T, [
|
||||
{ x: 1, y: 0 },
|
||||
{ x: 1, y: 0 },
|
||||
])
|
||||
})
|
||||
it('Should not validate array with non uniqueItems', () => {
|
||||
const T = Type.Array(Type.Number(), { uniqueItems: true })
|
||||
Fail(T, [0, 0])
|
||||
})
|
||||
// ---------------------------------------------------------
|
||||
// Contains
|
||||
// ---------------------------------------------------------
|
||||
it('Should validate for contains', () => {
|
||||
const T = Type.Array(Type.Number(), { contains: Type.Literal(1) })
|
||||
Ok(T, [1])
|
||||
Ok(T, [1, 2])
|
||||
Fail(T, [])
|
||||
Fail(T, [2])
|
||||
})
|
||||
it('Should validate for minContains', () => {
|
||||
const T = Type.Array(Type.Number(), { contains: Type.Literal(1), minContains: 3 })
|
||||
Ok(T, [1, 1, 1, 2])
|
||||
Ok(T, [2, 1, 1, 1, 2])
|
||||
Ok(T, [1, 1, 1])
|
||||
Fail(T, [])
|
||||
Fail(T, [1, 1])
|
||||
Fail(T, [2])
|
||||
})
|
||||
it('Should validate for maxContains', () => {
|
||||
const T = Type.Array(Type.Number(), { contains: Type.Literal(1), maxContains: 3 })
|
||||
Ok(T, [1, 1, 1])
|
||||
Ok(T, [1, 1])
|
||||
Ok(T, [2, 2, 2, 2, 1, 1, 1])
|
||||
Fail(T, [1, 1, 1, 1])
|
||||
})
|
||||
it('Should validate for minContains and maxContains', () => {
|
||||
const T = Type.Array(Type.Number(), { contains: Type.Literal(1), minContains: 3, maxContains: 5 })
|
||||
Fail(T, [1, 1])
|
||||
Ok(T, [1, 1, 1])
|
||||
Ok(T, [1, 1, 1, 1])
|
||||
Ok(T, [1, 1, 1, 1, 1])
|
||||
Fail(T, [1, 1, 1, 1, 1, 1])
|
||||
})
|
||||
it('Should not validate minContains or maxContains when contains is unspecified', () => {
|
||||
const T = Type.Array(Type.Number(), { minContains: 3, maxContains: 5 })
|
||||
Fail(T, [1, 1])
|
||||
Fail(T, [1, 1, 1])
|
||||
Fail(T, [1, 1, 1, 1])
|
||||
Fail(T, [1, 1, 1, 1, 1])
|
||||
Fail(T, [1, 1, 1, 1, 1, 1])
|
||||
})
|
||||
it('Should produce illogical schema when contains is not sub type of items', () => {
|
||||
const T = Type.Array(Type.Number(), { contains: Type.String(), minContains: 3, maxContains: 5 })
|
||||
Fail(T, [1, 1])
|
||||
Fail(T, [1, 1, 1])
|
||||
Fail(T, [1, 1, 1, 1])
|
||||
Fail(T, [1, 1, 1, 1, 1])
|
||||
Fail(T, [1, 1, 1, 1, 1, 1])
|
||||
})
|
||||
// ----------------------------------------------------------------
|
||||
// Issue: https://github.com/sinclairzx81/typebox/discussions/607
|
||||
// ----------------------------------------------------------------
|
||||
it('Should correctly handle undefined array properties', () => {
|
||||
const Answer = Type.Object({
|
||||
text: Type.String(),
|
||||
isCorrect: Type.Boolean(),
|
||||
})
|
||||
const Question = Type.Object({
|
||||
text: Type.String(),
|
||||
options: Type.Array(Answer, {
|
||||
minContains: 1,
|
||||
maxContains: 1,
|
||||
contains: Type.Object({
|
||||
text: Type.String(),
|
||||
isCorrect: Type.Literal(true),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
Fail(Question, { text: 'A' })
|
||||
Fail(Question, { text: 'A', options: [] })
|
||||
Ok(Question, { text: 'A', options: [{ text: 'A', isCorrect: true }] })
|
||||
Ok(Question, {
|
||||
text: 'A',
|
||||
options: [
|
||||
{ text: 'A', isCorrect: true },
|
||||
{ text: 'B', isCorrect: false },
|
||||
],
|
||||
})
|
||||
Fail(Question, { text: 'A', options: [{ text: 'A', isCorrect: false }] })
|
||||
Fail(Question, {
|
||||
text: 'A',
|
||||
options: [
|
||||
{ text: 'A', isCorrect: true },
|
||||
{ text: 'B', isCorrect: true },
|
||||
],
|
||||
})
|
||||
})
|
||||
})
|
||||
34
test/runtime/compiler-ajv/boolean.ts
Normal file
34
test/runtime/compiler-ajv/boolean.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler-ajv/Boolean', () => {
|
||||
it('Should validate a boolean', () => {
|
||||
const T = Type.Boolean()
|
||||
Ok(T, true)
|
||||
Ok(T, false)
|
||||
})
|
||||
it('Should not validate a number', () => {
|
||||
const T = Type.Boolean()
|
||||
Fail(T, 1)
|
||||
})
|
||||
it('Should not validate a string', () => {
|
||||
const T = Type.Boolean()
|
||||
Fail(T, 'true')
|
||||
})
|
||||
it('Should not validate an array', () => {
|
||||
const T = Type.Boolean()
|
||||
Fail(T, [true])
|
||||
})
|
||||
it('Should not validate an object', () => {
|
||||
const T = Type.Boolean()
|
||||
Fail(T, {})
|
||||
})
|
||||
it('Should not validate an null', () => {
|
||||
const T = Type.Boolean()
|
||||
Fail(T, null)
|
||||
})
|
||||
it('Should not validate an undefined', () => {
|
||||
const T = Type.Boolean()
|
||||
Fail(T, undefined)
|
||||
})
|
||||
})
|
||||
111
test/runtime/compiler-ajv/composite.ts
Normal file
111
test/runtime/compiler-ajv/composite.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Assert } from '../assert'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler-ajv/Composite', () => {
|
||||
it('Should compose two objects', () => {
|
||||
const A = Type.Object({ a: Type.String() })
|
||||
const B = Type.Object({ b: Type.Number() })
|
||||
const T = Type.Composite([A, B], { additionalProperties: false })
|
||||
Ok(T, { a: 'hello', b: 42 })
|
||||
})
|
||||
it('Should compose with partial', () => {
|
||||
const A = Type.Partial(Type.Object({ a: Type.Number() }))
|
||||
const B = Type.Partial(Type.Object({ b: Type.Number() }))
|
||||
const P = Type.Composite([A, B], { additionalProperties: false })
|
||||
Ok(P, { a: 1, b: 2 })
|
||||
Ok(P, { a: 1 })
|
||||
Ok(P, { b: 1 })
|
||||
Ok(P, {})
|
||||
Fail(P, { a: 1, b: 2, c: 3 })
|
||||
Fail(P, { c: 1 })
|
||||
})
|
||||
it('Should compose with overlapping same type', () => {
|
||||
const A = Type.Object({ a: Type.Number() })
|
||||
const B = Type.Object({ a: Type.Number() })
|
||||
const P = Type.Composite([A, B])
|
||||
Ok(P, { a: 1 })
|
||||
Fail(P, { a: '1' })
|
||||
})
|
||||
it('Should not compose with overlapping varying type', () => {
|
||||
const A = Type.Object({ a: Type.Number() })
|
||||
const B = Type.Object({ a: Type.String() })
|
||||
const T = Type.Composite([A, B])
|
||||
Fail(T, { a: 1 })
|
||||
Fail(T, { a: 'hello' })
|
||||
Fail(T, {})
|
||||
})
|
||||
it('Should compose with deeply nest overlapping varying type', () => {
|
||||
const A = Type.Object({ a: Type.Number() })
|
||||
const B = Type.Object({ b: Type.String() })
|
||||
const C = Type.Object({ c: Type.Boolean() })
|
||||
const D = Type.Object({ d: Type.Null() })
|
||||
const T = Type.Composite([A, B, C, D])
|
||||
Ok(T, { a: 1, b: 'hello', c: true, d: null })
|
||||
})
|
||||
it('Should not compose with deeply nest overlapping varying type', () => {
|
||||
const A = Type.Object({ a: Type.Number() })
|
||||
const B = Type.Object({ a: Type.String() })
|
||||
const C = Type.Object({ a: Type.Boolean() })
|
||||
const D = Type.Object({ a: Type.Null() })
|
||||
const T = Type.Composite([A, B, C, D])
|
||||
Fail(T, { a: 1 })
|
||||
Fail(T, { a: 'hello' })
|
||||
Fail(T, { a: false })
|
||||
Fail(T, { a: null })
|
||||
Fail(T, { a: [] })
|
||||
Fail(T, {})
|
||||
})
|
||||
it('Should pick from composited type', () => {
|
||||
const A = Type.Object({ x: Type.Number() })
|
||||
const B = Type.Object({ y: Type.Number() })
|
||||
const C = Type.Object({ z: Type.Number() })
|
||||
const T = Type.Composite([A, B, C])
|
||||
const P = Type.Pick(T, ['x', 'y'], { additionalProperties: false })
|
||||
Ok(P, { x: 1, y: 1 })
|
||||
Fail(P, { x: 1, y: 1, z: 1 })
|
||||
})
|
||||
it('Should omit from composited type', () => {
|
||||
const A = Type.Object({ x: Type.Number() })
|
||||
const B = Type.Object({ y: Type.Number() })
|
||||
const C = Type.Object({ z: Type.Number() })
|
||||
const T = Type.Composite([A, B, C])
|
||||
const P = Type.Omit(T, ['z'], { additionalProperties: false })
|
||||
Ok(P, { x: 1, y: 1 })
|
||||
Fail(P, { x: 1, y: 1, z: 1 })
|
||||
})
|
||||
it('Should compose nested object properties', () => {
|
||||
const A = Type.Object({ x: Type.Object({ x: Type.Number() }) })
|
||||
const B = Type.Object({ y: Type.Object({ x: Type.String() }) })
|
||||
const T = Type.Composite([A, B])
|
||||
Ok(T, { x: { x: 1 }, y: { x: '' } })
|
||||
Fail(T, { x: { x: '1' }, y: { x: '' } })
|
||||
Fail(T, { x: { x: 1 }, y: { x: 1 } })
|
||||
})
|
||||
// todo: move to composition / type guard spec
|
||||
it('Should compose and produce the same schema', () => {
|
||||
const T = Type.Object({
|
||||
field: Type.Optional(Type.String()),
|
||||
})
|
||||
const A = Type.Composite([T])
|
||||
const B = Type.Composite([T])
|
||||
Assert.IsEqual(A, B)
|
||||
})
|
||||
// prettier-ignore
|
||||
it('Should composite intersection', () => {
|
||||
const T = Type.Composite([
|
||||
Type.Intersect([
|
||||
Type.Object({ x: Type.Number() })
|
||||
]),
|
||||
Type.Intersect([
|
||||
Type.Object({ y: Type.Number() })
|
||||
]),
|
||||
Type.Intersect([
|
||||
Type.Object({ z: Type.Number() })
|
||||
]),
|
||||
])
|
||||
Ok(T, { x: 1, y: 2, z: 3 })
|
||||
Fail(T, { x: 1, y: 2, z: '3' })
|
||||
Fail(T, { x: 1, y: 2 })
|
||||
})
|
||||
})
|
||||
39
test/runtime/compiler-ajv/const.ts
Normal file
39
test/runtime/compiler-ajv/const.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok } from './validate'
|
||||
|
||||
describe('compiler-ajv/Const', () => {
|
||||
it('Should validate 1', () => {
|
||||
const T = Type.Const(1)
|
||||
Ok(T, 1)
|
||||
})
|
||||
it('Should validate 2', () => {
|
||||
const T = Type.Const('hello')
|
||||
Ok(T, 'hello')
|
||||
})
|
||||
it('Should validate 3', () => {
|
||||
const T = Type.Const(true)
|
||||
Ok(T, true)
|
||||
})
|
||||
it('Should validate 4', () => {
|
||||
const T = Type.Const({ x: 1, y: 2 })
|
||||
Ok(T, { x: 1, y: 2 })
|
||||
})
|
||||
it('Should validate 5', () => {
|
||||
const T = Type.Const([1, 2, 3])
|
||||
Ok(T, [1, 2, 3])
|
||||
})
|
||||
it('Should validate 6', () => {
|
||||
const T = Type.Const([1, true, 'hello'])
|
||||
Ok(T, [1, true, 'hello'])
|
||||
})
|
||||
it('Should validate 7', () => {
|
||||
const T = Type.Const({
|
||||
x: [1, 2, 3, 4],
|
||||
y: { x: 1, y: 2, z: 3 },
|
||||
})
|
||||
Ok(T, {
|
||||
x: [1, 2, 3, 4],
|
||||
y: { x: 1, y: 2, z: 3 },
|
||||
})
|
||||
})
|
||||
})
|
||||
70
test/runtime/compiler-ajv/date.ts
Normal file
70
test/runtime/compiler-ajv/date.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
// ---------------------------------------------------
|
||||
// No Longer Supported
|
||||
// ---------------------------------------------------
|
||||
|
||||
// import { Type } from '@sinclair/typebox'
|
||||
// import { Ok, Fail } from './validate'
|
||||
|
||||
// ----------------------------------------------------
|
||||
// These tests are implemented by way of .addKeyword()
|
||||
// which are configured to use Value.Check()
|
||||
// ----------------------------------------------------
|
||||
|
||||
// describe('compiler-ajv/Date', () => {
|
||||
// it('Should not validate number', () => {
|
||||
// const T = Type.Date()
|
||||
// Fail(T, 1)
|
||||
// })
|
||||
// it('Should not validate string', () => {
|
||||
// const T = Type.Date()
|
||||
// Fail(T, 'hello')
|
||||
// })
|
||||
// it('Should not validate boolean', () => {
|
||||
// const T = Type.Date()
|
||||
// Fail(T, true)
|
||||
// })
|
||||
// it('Should not validate array', () => {
|
||||
// const T = Type.Date()
|
||||
// Fail(T, [1, 2, 3])
|
||||
// })
|
||||
// it('Should not validate object', () => {
|
||||
// const T = Type.Date()
|
||||
// Fail(T, { a: 1, b: 2 })
|
||||
// })
|
||||
// it('Should not validate null', () => {
|
||||
// const T = Type.Date()
|
||||
// Fail(T, null)
|
||||
// })
|
||||
// it('Should not validate undefined', () => {
|
||||
// const T = Type.Date()
|
||||
// Fail(T, undefined)
|
||||
// })
|
||||
// it('Should validate Date', () => {
|
||||
// const T = Type.Date()
|
||||
// Ok(T, new Date())
|
||||
// })
|
||||
// it('Should not validate Date if is invalid', () => {
|
||||
// const T = Type.Date()
|
||||
// Fail(T, new Date('not-a-valid-date'))
|
||||
// })
|
||||
// it('Should validate Date minimumTimestamp', () => {
|
||||
// const T = Type.Date({ minimumTimestamp: 10 })
|
||||
// Fail(T, new Date(9))
|
||||
// Ok(T, new Date(10))
|
||||
// })
|
||||
// it('Should validate Date maximumTimestamp', () => {
|
||||
// const T = Type.Date({ maximumTimestamp: 10 })
|
||||
// Ok(T, new Date(10))
|
||||
// Fail(T, new Date(11))
|
||||
// })
|
||||
// it('Should validate Date exclusiveMinimumTimestamp', () => {
|
||||
// const T = Type.Date({ exclusiveMinimumTimestamp: 10 })
|
||||
// Fail(T, new Date(10))
|
||||
// Ok(T, new Date(11))
|
||||
// })
|
||||
// it('Should validate Date exclusiveMaximumTimestamp', () => {
|
||||
// const T = Type.Date({ exclusiveMaximumTimestamp: 10 })
|
||||
// Ok(T, new Date(9))
|
||||
// Fail(T, new Date(10))
|
||||
// })
|
||||
// })
|
||||
53
test/runtime/compiler-ajv/enum.ts
Normal file
53
test/runtime/compiler-ajv/enum.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler-ajv/Enum', () => {
|
||||
it('Should validate when emum uses default numeric values', () => {
|
||||
enum Kind {
|
||||
Foo, // = 0
|
||||
Bar, // = 1
|
||||
}
|
||||
const T = Type.Enum(Kind)
|
||||
Ok(T, 0)
|
||||
Ok(T, 1)
|
||||
})
|
||||
it('Should not validate when given enum values are not numeric', () => {
|
||||
enum Kind {
|
||||
Foo, // = 0
|
||||
Bar, // = 1
|
||||
}
|
||||
const T = Type.Enum(Kind)
|
||||
Fail(T, 'Foo')
|
||||
Fail(T, 'Bar')
|
||||
})
|
||||
it('Should validate when emum has defined string values', () => {
|
||||
enum Kind {
|
||||
Foo = 'foo',
|
||||
Bar = 'bar',
|
||||
}
|
||||
const T = Type.Enum(Kind)
|
||||
Ok(T, 'foo')
|
||||
Ok(T, 'bar')
|
||||
})
|
||||
it('Should not validate when emum has defined string values and user passes numeric', () => {
|
||||
enum Kind {
|
||||
Foo = 'foo',
|
||||
Bar = 'bar',
|
||||
}
|
||||
const T = Type.Enum(Kind)
|
||||
Fail(T, 0)
|
||||
Fail(T, 1)
|
||||
})
|
||||
it('Should validate when enum has one or more string values', () => {
|
||||
enum Kind {
|
||||
Foo,
|
||||
Bar = 'bar',
|
||||
}
|
||||
const T = Type.Enum(Kind)
|
||||
Ok(T, 0)
|
||||
Ok(T, 'bar')
|
||||
Fail(T, 'baz')
|
||||
Fail(T, 'Foo')
|
||||
Fail(T, 1)
|
||||
})
|
||||
})
|
||||
36
test/runtime/compiler-ajv/index.ts
Normal file
36
test/runtime/compiler-ajv/index.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import './any'
|
||||
import './array'
|
||||
import './boolean'
|
||||
import './composite'
|
||||
import './const'
|
||||
import './date'
|
||||
import './enum'
|
||||
import './integer'
|
||||
import './intersect'
|
||||
import './keyof'
|
||||
import './literal'
|
||||
import './module'
|
||||
import './never'
|
||||
import './not'
|
||||
import './null'
|
||||
import './number'
|
||||
import './object'
|
||||
import './omit'
|
||||
import './optional'
|
||||
import './partial'
|
||||
import './pick'
|
||||
import './readonly-optional'
|
||||
import './readonly'
|
||||
import './recursive'
|
||||
import './record'
|
||||
import './ref'
|
||||
import './required'
|
||||
import './string-pattern'
|
||||
import './string'
|
||||
import './template-literal'
|
||||
import './tuple'
|
||||
import './uint8array'
|
||||
import './union'
|
||||
import './unknown'
|
||||
import './unsafe'
|
||||
import './void'
|
||||
60
test/runtime/compiler-ajv/integer.ts
Normal file
60
test/runtime/compiler-ajv/integer.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler-ajv/Integer', () => {
|
||||
it('Should not validate number', () => {
|
||||
const T = Type.Integer()
|
||||
Fail(T, 3.14)
|
||||
})
|
||||
it('Should validate integer', () => {
|
||||
const T = Type.Integer()
|
||||
Ok(T, 1)
|
||||
})
|
||||
it('Should not validate string', () => {
|
||||
const T = Type.Integer()
|
||||
Fail(T, 'hello')
|
||||
})
|
||||
it('Should not validate boolean', () => {
|
||||
const T = Type.Integer()
|
||||
Fail(T, true)
|
||||
})
|
||||
it('Should not validate array', () => {
|
||||
const T = Type.Integer()
|
||||
Fail(T, [1, 2, 3])
|
||||
})
|
||||
it('Should not validate object', () => {
|
||||
const T = Type.Integer()
|
||||
Fail(T, { a: 1, b: 2 })
|
||||
})
|
||||
it('Should not validate null', () => {
|
||||
const T = Type.Integer()
|
||||
Fail(T, null)
|
||||
})
|
||||
it('Should not validate undefined', () => {
|
||||
const T = Type.Integer()
|
||||
Fail(T, undefined)
|
||||
})
|
||||
it('Should validate minimum', () => {
|
||||
const T = Type.Integer({ minimum: 10 })
|
||||
Fail(T, 9)
|
||||
Ok(T, 10)
|
||||
})
|
||||
it('Should validate maximum', () => {
|
||||
const T = Type.Integer({ maximum: 10 })
|
||||
Ok(T, 10)
|
||||
Fail(T, 11)
|
||||
})
|
||||
it('Should validate Date exclusiveMinimum', () => {
|
||||
const T = Type.Integer({ exclusiveMinimum: 10 })
|
||||
Fail(T, 10)
|
||||
Ok(T, 11)
|
||||
})
|
||||
it('Should validate Date exclusiveMaximum', () => {
|
||||
const T = Type.Integer({ exclusiveMaximum: 10 })
|
||||
Ok(T, 9)
|
||||
Fail(T, 10)
|
||||
})
|
||||
it('Should not validate NaN', () => {
|
||||
Fail(Type.Integer(), NaN)
|
||||
})
|
||||
})
|
||||
218
test/runtime/compiler-ajv/intersect.ts
Normal file
218
test/runtime/compiler-ajv/intersect.ts
Normal file
@@ -0,0 +1,218 @@
|
||||
import { Type, Static } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler-ajv/Intersect', () => {
|
||||
it('Should intersect number and number', () => {
|
||||
const A = Type.Number()
|
||||
const B = Type.Number()
|
||||
const T = Type.Intersect([A, B], {})
|
||||
Ok(T, 1)
|
||||
})
|
||||
it('Should not intersect string and number', () => {
|
||||
const A = Type.String()
|
||||
const B = Type.Number()
|
||||
const T = Type.Intersect([A, B], {})
|
||||
Fail(T, 1)
|
||||
Fail(T, '1')
|
||||
})
|
||||
it('Should intersect two objects', () => {
|
||||
const A = Type.Object({ x: Type.Number() })
|
||||
const B = Type.Object({ y: Type.Number() })
|
||||
const T = Type.Intersect([A, B], {})
|
||||
Ok(T, { x: 1, y: 1 })
|
||||
})
|
||||
it('Should not intersect two objects with internal additionalProperties false', () => {
|
||||
const A = Type.Object({ x: Type.Number() }, { additionalProperties: false })
|
||||
const B = Type.Object({ y: Type.Number() }, { additionalProperties: false })
|
||||
const T = Type.Intersect([A, B], {})
|
||||
Fail(T, { x: 1, y: 1 })
|
||||
})
|
||||
it('Should intersect two objects and mandate required properties', () => {
|
||||
const A = Type.Object({ x: Type.Number() })
|
||||
const B = Type.Object({ y: Type.Number() })
|
||||
const T = Type.Intersect([A, B], {})
|
||||
Ok(T, { x: 1, y: 1 })
|
||||
Fail(T, { x: 1 })
|
||||
Fail(T, { y: 1 })
|
||||
})
|
||||
it('Should intersect two objects with unevaluated properties', () => {
|
||||
const A = Type.Object({ x: Type.Number() })
|
||||
const B = Type.Object({ y: Type.Number() })
|
||||
const T = Type.Intersect([A, B], {})
|
||||
Ok(T, { x: 1, y: 2, z: 1 })
|
||||
})
|
||||
it('Should intersect two objects and restrict unevaluated properties', () => {
|
||||
const A = Type.Object({ x: Type.Number() })
|
||||
const B = Type.Object({ y: Type.Number() })
|
||||
const T = Type.Intersect([A, B], { unevaluatedProperties: false })
|
||||
Fail(T, { x: 1, y: 2, z: 1 })
|
||||
})
|
||||
it('Should intersect two objects and allow unevaluated properties of number', () => {
|
||||
const A = Type.Object({ x: Type.Number() })
|
||||
const B = Type.Object({ y: Type.Number() })
|
||||
const T = Type.Intersect([A, B], { unevaluatedProperties: Type.Number() })
|
||||
Ok(T, { x: 1, y: 2, z: 3 })
|
||||
Fail(T, { x: 1, y: 2, z: '1' })
|
||||
})
|
||||
it('Should intersect two union objects with overlapping properties of the same type', () => {
|
||||
const A = Type.Union([Type.Object({ x: Type.Number() })])
|
||||
const B = Type.Union([Type.Object({ x: Type.Number() })])
|
||||
const T = Type.Intersect([A, B])
|
||||
Ok(T, { x: 1 })
|
||||
Fail(T, { x: '1' })
|
||||
})
|
||||
it('Should not intersect two union objects with overlapping properties of varying types', () => {
|
||||
const A = Type.Union([Type.Object({ x: Type.Number() })])
|
||||
const B = Type.Union([Type.Object({ x: Type.String() })])
|
||||
const T = Type.Intersect([A, B])
|
||||
Fail(T, { x: 1 })
|
||||
Fail(T, { x: '1' })
|
||||
})
|
||||
it('Should intersect two union objects with non-overlapping properties', () => {
|
||||
const A = Type.Union([Type.Object({ x: Type.Number() })])
|
||||
const B = Type.Union([Type.Object({ y: Type.Number() })])
|
||||
const T = Type.Intersect([A, B])
|
||||
Ok(T, { x: 1, y: 1 })
|
||||
})
|
||||
it('Should not intersect two union objects with non-overlapping properties for additionalProperties false', () => {
|
||||
const A = Type.Union([Type.Object({ x: Type.Number() }, { additionalProperties: false })])
|
||||
const B = Type.Union([Type.Object({ y: Type.Number() }, { additionalProperties: false })])
|
||||
const T = Type.Intersect([A, B])
|
||||
Fail(T, { x: 1, y: 1 })
|
||||
})
|
||||
it('unevaluatedProperties with Record 1', () => {
|
||||
const T = Type.Intersect(
|
||||
[
|
||||
Type.Record(Type.Number(), Type.String()),
|
||||
Type.Object({
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
}),
|
||||
],
|
||||
{
|
||||
unevaluatedProperties: false,
|
||||
},
|
||||
)
|
||||
Ok(T, { x: 1, y: 2 })
|
||||
})
|
||||
it('unevaluatedProperties with Record 2', () => {
|
||||
const T = Type.Intersect(
|
||||
[
|
||||
Type.Record(Type.Number(), Type.String()),
|
||||
Type.Object({
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
}),
|
||||
],
|
||||
{
|
||||
unevaluatedProperties: false,
|
||||
},
|
||||
)
|
||||
Ok(T, { x: 1, y: 2, 0: 'hello' })
|
||||
})
|
||||
it('unevaluatedProperties with Record 3', () => {
|
||||
const T = Type.Intersect(
|
||||
[
|
||||
Type.Record(Type.Number(), Type.String()),
|
||||
Type.Object({
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
}),
|
||||
],
|
||||
{
|
||||
unevaluatedProperties: false,
|
||||
},
|
||||
)
|
||||
Fail(T, { x: 1, y: 2, 0: 1 })
|
||||
})
|
||||
it('unevaluatedProperties with Record 4', () => {
|
||||
const T = Type.Intersect(
|
||||
[
|
||||
Type.Record(Type.Number(), Type.String()),
|
||||
Type.Object({
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
}),
|
||||
],
|
||||
{
|
||||
unevaluatedProperties: Type.Boolean(),
|
||||
},
|
||||
)
|
||||
Ok(T, { x: 1, y: 2 })
|
||||
})
|
||||
it('unevaluatedProperties with Record 5', () => {
|
||||
const T = Type.Intersect(
|
||||
[
|
||||
Type.Record(Type.Number(), Type.String()),
|
||||
Type.Object({
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
}),
|
||||
],
|
||||
{
|
||||
unevaluatedProperties: Type.Boolean(),
|
||||
},
|
||||
)
|
||||
Ok(T, { x: 1, y: 2, z: true })
|
||||
})
|
||||
it('unevaluatedProperties with Record 6', () => {
|
||||
const T = Type.Intersect(
|
||||
[
|
||||
Type.Record(Type.Number(), Type.String()),
|
||||
Type.Object({
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
}),
|
||||
],
|
||||
{
|
||||
unevaluatedProperties: Type.Boolean(),
|
||||
},
|
||||
)
|
||||
Fail(T, { x: 1, y: 2, z: 1 })
|
||||
})
|
||||
it('unevaluatedProperties with Record 7', () => {
|
||||
const T = Type.Intersect(
|
||||
[
|
||||
Type.Record(Type.Number(), Type.String()),
|
||||
Type.Object({
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
}),
|
||||
],
|
||||
{
|
||||
unevaluatedProperties: Type.Boolean(),
|
||||
},
|
||||
)
|
||||
Ok(T, { x: 1, y: 2, 0: '' })
|
||||
})
|
||||
it('unevaluatedProperties with Record 8', () => {
|
||||
const T = Type.Intersect(
|
||||
[
|
||||
Type.Record(Type.Number(), Type.String()),
|
||||
Type.Object({
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
}),
|
||||
],
|
||||
{
|
||||
unevaluatedProperties: Type.Boolean(),
|
||||
},
|
||||
)
|
||||
Ok(T, { x: 1, y: 2, 0: '', z: true })
|
||||
})
|
||||
it('unevaluatedProperties with Record 9', () => {
|
||||
const T = Type.Intersect(
|
||||
[
|
||||
Type.Record(Type.Number(), Type.String()),
|
||||
Type.Object({
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
}),
|
||||
],
|
||||
{
|
||||
unevaluatedProperties: Type.Boolean(),
|
||||
},
|
||||
)
|
||||
Fail(T, { x: 1, y: 2, 0: '', z: 1 })
|
||||
})
|
||||
})
|
||||
48
test/runtime/compiler-ajv/keyof.ts
Normal file
48
test/runtime/compiler-ajv/keyof.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler-ajv/KeyOf', () => {
|
||||
it('Should validate with all object keys as a kind of union', () => {
|
||||
const T = Type.KeyOf(
|
||||
Type.Object({
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
z: Type.Number(),
|
||||
}),
|
||||
)
|
||||
Ok(T, 'x')
|
||||
Ok(T, 'y')
|
||||
Ok(T, 'z')
|
||||
Fail(T, 'w')
|
||||
})
|
||||
it('Should validate when using pick', () => {
|
||||
const T = Type.KeyOf(
|
||||
Type.Pick(
|
||||
Type.Object({
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
z: Type.Number(),
|
||||
}),
|
||||
['x', 'y'],
|
||||
),
|
||||
)
|
||||
Ok(T, 'x')
|
||||
Ok(T, 'y')
|
||||
Fail(T, 'z')
|
||||
})
|
||||
it('Should validate when using omit', () => {
|
||||
const T = Type.KeyOf(
|
||||
Type.Omit(
|
||||
Type.Object({
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
z: Type.Number(),
|
||||
}),
|
||||
['x', 'y'],
|
||||
),
|
||||
)
|
||||
Fail(T, 'x')
|
||||
Fail(T, 'y')
|
||||
Ok(T, 'z')
|
||||
})
|
||||
})
|
||||
50
test/runtime/compiler-ajv/literal.ts
Normal file
50
test/runtime/compiler-ajv/literal.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler-ajv/Literal', () => {
|
||||
it('Should validate literal number', () => {
|
||||
const T = Type.Literal(42)
|
||||
Ok(T, 42)
|
||||
})
|
||||
it('Should validate literal string', () => {
|
||||
const T = Type.Literal('hello')
|
||||
Ok(T, 'hello')
|
||||
})
|
||||
it('Should validate literal boolean', () => {
|
||||
const T = Type.Literal(true)
|
||||
Ok(T, true)
|
||||
})
|
||||
it('Should not validate invalid literal number', () => {
|
||||
const T = Type.Literal(42)
|
||||
Fail(T, 43)
|
||||
})
|
||||
it('Should not validate invalid literal string', () => {
|
||||
const T = Type.Literal('hello')
|
||||
Fail(T, 'world')
|
||||
})
|
||||
it('Should not validate invalid literal boolean', () => {
|
||||
const T = Type.Literal(false)
|
||||
Fail(T, true)
|
||||
})
|
||||
it('Should validate literal union', () => {
|
||||
const T = Type.Union([Type.Literal(42), Type.Literal('hello')])
|
||||
Ok(T, 42)
|
||||
Ok(T, 'hello')
|
||||
})
|
||||
it('Should not validate invalid literal union', () => {
|
||||
const T = Type.Union([Type.Literal(42), Type.Literal('hello')])
|
||||
Fail(T, 43)
|
||||
Fail(T, 'world')
|
||||
})
|
||||
// reference: https://github.com/sinclairzx81/typebox/issues/539
|
||||
it('Should escape single quote literals', () => {
|
||||
const T = Type.Literal("it's")
|
||||
Ok(T, "it's")
|
||||
Fail(T, "it''s")
|
||||
})
|
||||
it('Should escape multiple single quote literals', () => {
|
||||
const T = Type.Literal("'''''''''")
|
||||
Ok(T, "'''''''''")
|
||||
Fail(T, "''''''''") // minus 1
|
||||
})
|
||||
})
|
||||
144
test/runtime/compiler-ajv/module.ts
Normal file
144
test/runtime/compiler-ajv/module.ts
Normal file
@@ -0,0 +1,144 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler-ajv/Module', () => {
|
||||
it('Should validate string', () => {
|
||||
const Module = Type.Module({
|
||||
A: Type.String(),
|
||||
})
|
||||
const T = Module.Import('A')
|
||||
Ok(T, 'hello')
|
||||
Fail(T, true)
|
||||
})
|
||||
it('Should validate referenced string', () => {
|
||||
const Module = Type.Module({
|
||||
A: Type.String(),
|
||||
B: Type.Ref('A'),
|
||||
})
|
||||
const T = Module.Import('B')
|
||||
Ok(T, 'hello')
|
||||
Fail(T, true)
|
||||
})
|
||||
it('Should validate self referential', () => {
|
||||
const Module = Type.Module({
|
||||
A: Type.Object({
|
||||
nodes: Type.Array(Type.Ref('A')),
|
||||
}),
|
||||
})
|
||||
const T = Module.Import('A')
|
||||
Ok(T, { nodes: [{ nodes: [{ nodes: [] }, { nodes: [] }] }] })
|
||||
Fail(T, { nodes: [{ nodes: [{ nodes: [] }, { nodes: false }] }] })
|
||||
Fail(T, true)
|
||||
})
|
||||
it('Should validate mutual recursive', () => {
|
||||
const Module = Type.Module({
|
||||
A: Type.Object({
|
||||
b: Type.Ref('B'),
|
||||
}),
|
||||
B: Type.Object({
|
||||
a: Type.Union([Type.Ref('A'), Type.Null()]),
|
||||
}),
|
||||
})
|
||||
const T = Module.Import('A')
|
||||
Ok(T, { b: { a: null } })
|
||||
Ok(T, { b: { a: { b: { a: null } } } })
|
||||
Fail(T, { b: { a: 1 } })
|
||||
Fail(T, { b: { a: { b: { a: 1 } } } })
|
||||
Fail(T, true)
|
||||
})
|
||||
it('Should validate mutual recursive (Array)', () => {
|
||||
const Module = Type.Module({
|
||||
A: Type.Object({
|
||||
b: Type.Ref('B'),
|
||||
}),
|
||||
B: Type.Object({
|
||||
a: Type.Array(Type.Ref('A')),
|
||||
}),
|
||||
})
|
||||
const T = Module.Import('A')
|
||||
Ok(T, { b: { a: [{ b: { a: [] } }] } })
|
||||
Fail(T, { b: { a: [{ b: { a: [null] } }] } })
|
||||
Fail(T, true)
|
||||
})
|
||||
it('Should validate deep referential', () => {
|
||||
const Module = Type.Module({
|
||||
A: Type.Ref('B'),
|
||||
B: Type.Ref('C'),
|
||||
C: Type.Ref('D'),
|
||||
D: Type.Ref('E'),
|
||||
E: Type.Ref('F'),
|
||||
F: Type.Ref('G'),
|
||||
G: Type.Ref('H'),
|
||||
H: Type.Literal('hello'),
|
||||
})
|
||||
const T = Module.Import('A')
|
||||
Ok(T, 'hello')
|
||||
Fail(T, 'world')
|
||||
})
|
||||
// ----------------------------------------------------------------
|
||||
// Modifiers
|
||||
// ----------------------------------------------------------------
|
||||
it('Should validate objects with property modifiers 1', () => {
|
||||
const Module = Type.Module({
|
||||
T: Type.Object({
|
||||
x: Type.ReadonlyOptional(Type.Null()),
|
||||
y: Type.Readonly(Type.Null()),
|
||||
z: Type.Optional(Type.Null()),
|
||||
w: Type.Null(),
|
||||
}),
|
||||
})
|
||||
const T = Module.Import('T')
|
||||
Ok(T, { x: null, y: null, w: null })
|
||||
Ok(T, { y: null, w: null })
|
||||
Fail(T, { x: 1, y: null, w: null })
|
||||
})
|
||||
it('Should validate objects with property modifiers 2', () => {
|
||||
const Module = Type.Module({
|
||||
T: Type.Object({
|
||||
x: Type.ReadonlyOptional(Type.Array(Type.Null())),
|
||||
y: Type.Readonly(Type.Array(Type.Null())),
|
||||
z: Type.Optional(Type.Array(Type.Null())),
|
||||
w: Type.Array(Type.Null()),
|
||||
}),
|
||||
})
|
||||
const T = Module.Import('T')
|
||||
Ok(T, { x: [null], y: [null], w: [null] })
|
||||
Ok(T, { y: [null], w: [null] })
|
||||
Fail(T, { x: [1], y: [null], w: [null] })
|
||||
})
|
||||
// ----------------------------------------------------------------
|
||||
// https://github.com/sinclairzx81/typebox/issues/1109
|
||||
// ----------------------------------------------------------------
|
||||
it('Should validate deep referential 1', () => {
|
||||
const Module = Type.Module({
|
||||
A: Type.Union([Type.Literal('Foo'), Type.Literal('Bar')]),
|
||||
B: Type.Ref('A'),
|
||||
C: Type.Object({ ref: Type.Ref('B') }),
|
||||
D: Type.Union([Type.Ref('B'), Type.Ref('C')]),
|
||||
})
|
||||
Ok(Module.Import('A') as never, 'Foo')
|
||||
Ok(Module.Import('A') as never, 'Bar')
|
||||
Ok(Module.Import('B') as never, 'Foo')
|
||||
Ok(Module.Import('B') as never, 'Bar')
|
||||
Ok(Module.Import('C') as never, { ref: 'Foo' })
|
||||
Ok(Module.Import('C') as never, { ref: 'Bar' })
|
||||
Ok(Module.Import('D') as never, 'Foo')
|
||||
Ok(Module.Import('D') as never, 'Bar')
|
||||
Ok(Module.Import('D') as never, { ref: 'Foo' })
|
||||
Ok(Module.Import('D') as never, { ref: 'Bar' })
|
||||
})
|
||||
it('Should validate deep referential 2', () => {
|
||||
const Module = Type.Module({
|
||||
A: Type.Literal('Foo'),
|
||||
B: Type.Ref('A'),
|
||||
C: Type.Ref('B'),
|
||||
D: Type.Ref('C'),
|
||||
E: Type.Ref('D'),
|
||||
})
|
||||
Ok(Module.Import('A'), 'Foo')
|
||||
Ok(Module.Import('B'), 'Foo')
|
||||
Ok(Module.Import('C'), 'Foo')
|
||||
Ok(Module.Import('D'), 'Foo')
|
||||
Ok(Module.Import('E'), 'Foo')
|
||||
})
|
||||
})
|
||||
33
test/runtime/compiler-ajv/never.ts
Normal file
33
test/runtime/compiler-ajv/never.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Fail } from './validate'
|
||||
|
||||
describe('compiler-ajv/Never', () => {
|
||||
it('Should not validate number', () => {
|
||||
const T = Type.Never()
|
||||
Fail(T, 1)
|
||||
})
|
||||
it('Should not validate string', () => {
|
||||
const T = Type.Never()
|
||||
Fail(T, 'hello')
|
||||
})
|
||||
it('Should not validate boolean', () => {
|
||||
const T = Type.Never()
|
||||
Fail(T, true)
|
||||
})
|
||||
it('Should not validate array', () => {
|
||||
const T = Type.Never()
|
||||
Fail(T, [1, 2, 3])
|
||||
})
|
||||
it('Should not validate object', () => {
|
||||
const T = Type.Never()
|
||||
Fail(T, { a: 1, b: 2 })
|
||||
})
|
||||
it('Should not validate null', () => {
|
||||
const T = Type.Never()
|
||||
Fail(T, null)
|
||||
})
|
||||
it('Should validate undefined', () => {
|
||||
const T = Type.Never()
|
||||
Fail(T, undefined)
|
||||
})
|
||||
})
|
||||
45
test/runtime/compiler-ajv/not.ts
Normal file
45
test/runtime/compiler-ajv/not.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler-ajv/Not', () => {
|
||||
it('Should validate not number', () => {
|
||||
const T = Type.Not(Type.Number())
|
||||
Fail(T, 1)
|
||||
Ok(T, '1')
|
||||
})
|
||||
it('Should validate not not number', () => {
|
||||
const T = Type.Not(Type.Not(Type.Number()))
|
||||
Ok(T, 1)
|
||||
Fail(T, '1')
|
||||
})
|
||||
it('Should validate not union', () => {
|
||||
// prettier-ignore
|
||||
const T = Type.Not(Type.Union([
|
||||
Type.Literal('A'),
|
||||
Type.Literal('B'),
|
||||
Type.Literal('C')
|
||||
]))
|
||||
Fail(T, 'A')
|
||||
Fail(T, 'B')
|
||||
Fail(T, 'C')
|
||||
Ok(T, 'D')
|
||||
})
|
||||
it('Should validate not object intersection', () => {
|
||||
const T = Type.Intersect([
|
||||
Type.Object({
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
z: Type.Number(),
|
||||
}),
|
||||
Type.Object({
|
||||
x: Type.Not(Type.Literal(0)),
|
||||
y: Type.Not(Type.Literal(0)),
|
||||
z: Type.Not(Type.Literal(0)),
|
||||
}),
|
||||
])
|
||||
Fail(T, { x: 0, y: 0, z: 0 })
|
||||
Fail(T, { x: 1, y: 0, z: 0 })
|
||||
Fail(T, { x: 1, y: 1, z: 0 })
|
||||
Ok(T, { x: 1, y: 1, z: 1 })
|
||||
})
|
||||
})
|
||||
33
test/runtime/compiler-ajv/null.ts
Normal file
33
test/runtime/compiler-ajv/null.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler-ajv/Null', () => {
|
||||
it('Should not validate number', () => {
|
||||
const T = Type.Null()
|
||||
Fail(T, 1)
|
||||
})
|
||||
it('Should not validate string', () => {
|
||||
const T = Type.Null()
|
||||
Fail(T, 'hello')
|
||||
})
|
||||
it('Should not validate boolean', () => {
|
||||
const T = Type.Null()
|
||||
Fail(T, true)
|
||||
})
|
||||
it('Should not validate array', () => {
|
||||
const T = Type.Null()
|
||||
Fail(T, [1, 2, 3])
|
||||
})
|
||||
it('Should not validate object', () => {
|
||||
const T = Type.Null()
|
||||
Fail(T, { a: 1, b: 2 })
|
||||
})
|
||||
it('Should not validate null', () => {
|
||||
const T = Type.Null()
|
||||
Ok(T, null)
|
||||
})
|
||||
it('Should not validate undefined', () => {
|
||||
const T = Type.Null()
|
||||
Fail(T, undefined)
|
||||
})
|
||||
})
|
||||
60
test/runtime/compiler-ajv/number.ts
Normal file
60
test/runtime/compiler-ajv/number.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler-ajv/Number', () => {
|
||||
it('Should validate number', () => {
|
||||
const T = Type.Number()
|
||||
Ok(T, 3.14)
|
||||
})
|
||||
it('Should validate integer', () => {
|
||||
const T = Type.Number()
|
||||
Ok(T, 1)
|
||||
})
|
||||
it('Should not validate string', () => {
|
||||
const T = Type.Number()
|
||||
Fail(T, 'hello')
|
||||
})
|
||||
it('Should not validate boolean', () => {
|
||||
const T = Type.Number()
|
||||
Fail(T, true)
|
||||
})
|
||||
it('Should not validate array', () => {
|
||||
const T = Type.Number()
|
||||
Fail(T, [1, 2, 3])
|
||||
})
|
||||
it('Should not validate object', () => {
|
||||
const T = Type.Number()
|
||||
Fail(T, { a: 1, b: 2 })
|
||||
})
|
||||
it('Should not validate null', () => {
|
||||
const T = Type.Number()
|
||||
Fail(T, null)
|
||||
})
|
||||
it('Should not validate undefined', () => {
|
||||
const T = Type.Number()
|
||||
Fail(T, undefined)
|
||||
})
|
||||
it('Should validate minimum', () => {
|
||||
const T = Type.Number({ minimum: 10 })
|
||||
Fail(T, 9)
|
||||
Ok(T, 10)
|
||||
})
|
||||
it('Should validate maximum', () => {
|
||||
const T = Type.Number({ maximum: 10 })
|
||||
Ok(T, 10)
|
||||
Fail(T, 11)
|
||||
})
|
||||
it('Should validate Date exclusiveMinimum', () => {
|
||||
const T = Type.Number({ exclusiveMinimum: 10 })
|
||||
Fail(T, 10)
|
||||
Ok(T, 11)
|
||||
})
|
||||
it('Should validate Date exclusiveMaximum', () => {
|
||||
const T = Type.Number({ exclusiveMaximum: 10 })
|
||||
Ok(T, 9)
|
||||
Fail(T, 10)
|
||||
})
|
||||
it('Should not validate NaN', () => {
|
||||
Fail(Type.Number(), NaN)
|
||||
})
|
||||
})
|
||||
204
test/runtime/compiler-ajv/object.ts
Normal file
204
test/runtime/compiler-ajv/object.ts
Normal file
@@ -0,0 +1,204 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler-ajv/Object', () => {
|
||||
it('Should not validate a number', () => {
|
||||
const T = Type.Object({})
|
||||
Fail(T, 42)
|
||||
})
|
||||
it('Should not validate a string', () => {
|
||||
const T = Type.Object({})
|
||||
Fail(T, 'hello')
|
||||
})
|
||||
it('Should not validate a boolean', () => {
|
||||
const T = Type.Object({})
|
||||
Fail(T, true)
|
||||
})
|
||||
it('Should not validate a null', () => {
|
||||
const T = Type.Object({})
|
||||
Fail(T, null)
|
||||
})
|
||||
it('Should not validate an array', () => {
|
||||
const T = Type.Object({})
|
||||
Fail(T, [1, 2])
|
||||
})
|
||||
it('Should validate with correct property values', () => {
|
||||
const T = Type.Object({
|
||||
a: Type.Number(),
|
||||
b: Type.String(),
|
||||
c: Type.Boolean(),
|
||||
d: Type.Array(Type.Number()),
|
||||
e: Type.Object({ x: Type.Number(), y: Type.Number() }),
|
||||
})
|
||||
Ok(T, {
|
||||
a: 10,
|
||||
b: 'hello',
|
||||
c: true,
|
||||
d: [1, 2, 3],
|
||||
e: { x: 10, y: 20 },
|
||||
})
|
||||
})
|
||||
it('Should not validate with incorrect property values', () => {
|
||||
const T = Type.Object({
|
||||
a: Type.Number(),
|
||||
b: Type.String(),
|
||||
c: Type.Boolean(),
|
||||
d: Type.Array(Type.Number()),
|
||||
e: Type.Object({ x: Type.Number(), y: Type.Number() }),
|
||||
})
|
||||
Fail(T, {
|
||||
a: 'not a number', // error
|
||||
b: 'hello',
|
||||
c: true,
|
||||
d: [1, 2, 3],
|
||||
e: { x: 10, y: 20 },
|
||||
})
|
||||
})
|
||||
|
||||
it('Should allow additionalProperties by default', () => {
|
||||
const T = Type.Object({
|
||||
a: Type.Number(),
|
||||
b: Type.String(),
|
||||
})
|
||||
Ok(T, {
|
||||
a: 1,
|
||||
b: 'hello',
|
||||
c: true,
|
||||
})
|
||||
})
|
||||
|
||||
it('Should not allow an empty object if minProperties is set to 1', () => {
|
||||
const T = Type.Object(
|
||||
{
|
||||
a: Type.Optional(Type.Number()),
|
||||
b: Type.Optional(Type.String()),
|
||||
},
|
||||
{ additionalProperties: false, minProperties: 1 },
|
||||
)
|
||||
Ok(T, { a: 1 })
|
||||
Ok(T, { b: 'hello' })
|
||||
Fail(T, {})
|
||||
})
|
||||
|
||||
it('Should not allow 3 properties if maxProperties is set to 2', () => {
|
||||
const T = Type.Object(
|
||||
{
|
||||
a: Type.Optional(Type.Number()),
|
||||
b: Type.Optional(Type.String()),
|
||||
c: Type.Optional(Type.Boolean()),
|
||||
},
|
||||
{ additionalProperties: false, maxProperties: 2 },
|
||||
)
|
||||
Ok(T, { a: 1 })
|
||||
Ok(T, { a: 1, b: 'hello' })
|
||||
Fail(T, {
|
||||
a: 1,
|
||||
b: 'hello',
|
||||
c: true,
|
||||
})
|
||||
})
|
||||
|
||||
it('Should not allow additionalProperties if additionalProperties is false', () => {
|
||||
const T = Type.Object(
|
||||
{
|
||||
a: Type.Number(),
|
||||
b: Type.String(),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
)
|
||||
Fail(T, {
|
||||
a: 1,
|
||||
b: 'hello',
|
||||
c: true,
|
||||
})
|
||||
})
|
||||
|
||||
it('Should not allow properties for an empty object when additionalProperties is false', () => {
|
||||
const T = Type.Object({}, { additionalProperties: false })
|
||||
Ok(T, {})
|
||||
Fail(T, { a: 10 })
|
||||
})
|
||||
|
||||
it('Should validate with non-syntax property keys', () => {
|
||||
const T = Type.Object({
|
||||
'with-hyphen': Type.Literal(1),
|
||||
'0-leading': Type.Literal(2),
|
||||
'$-leading': Type.Literal(3),
|
||||
'!@#$%^&*(': Type.Literal(4),
|
||||
})
|
||||
Ok(T, {
|
||||
'with-hyphen': 1,
|
||||
'0-leading': 2,
|
||||
'$-leading': 3,
|
||||
'!@#$%^&*(': 4,
|
||||
})
|
||||
})
|
||||
|
||||
it('Should validate schema additional properties of string', () => {
|
||||
const T = Type.Object(
|
||||
{
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
},
|
||||
{
|
||||
additionalProperties: Type.String(),
|
||||
},
|
||||
)
|
||||
Ok(T, {
|
||||
x: 1,
|
||||
y: 2,
|
||||
z: 'hello',
|
||||
})
|
||||
Fail(T, {
|
||||
x: 1,
|
||||
y: 2,
|
||||
z: 3,
|
||||
})
|
||||
})
|
||||
|
||||
it('Should validate schema additional properties of array', () => {
|
||||
const T = Type.Object(
|
||||
{
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
},
|
||||
{
|
||||
additionalProperties: Type.Array(Type.Number()),
|
||||
},
|
||||
)
|
||||
Ok(T, {
|
||||
x: 1,
|
||||
y: 2,
|
||||
z: [0, 1, 2],
|
||||
})
|
||||
Fail(T, {
|
||||
x: 1,
|
||||
y: 2,
|
||||
z: 3,
|
||||
})
|
||||
})
|
||||
|
||||
it('Should validate schema additional properties of object', () => {
|
||||
const T = Type.Object(
|
||||
{
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
},
|
||||
{
|
||||
additionalProperties: Type.Object({
|
||||
z: Type.Number(),
|
||||
}),
|
||||
},
|
||||
)
|
||||
Ok(T, {
|
||||
x: 1,
|
||||
y: 2,
|
||||
z: { z: 1 },
|
||||
})
|
||||
Fail(T, {
|
||||
x: 1,
|
||||
y: 2,
|
||||
z: 3,
|
||||
})
|
||||
})
|
||||
})
|
||||
69
test/runtime/compiler-ajv/omit.ts
Normal file
69
test/runtime/compiler-ajv/omit.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
import { strictEqual } from 'assert'
|
||||
|
||||
describe('compiler-ajv/Omit', () => {
|
||||
it('Should omit properties on the source schema', () => {
|
||||
const A = Type.Object(
|
||||
{
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
z: Type.Number(),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
)
|
||||
const T = Type.Omit(A, ['z'])
|
||||
Ok(T, { x: 1, y: 1 })
|
||||
})
|
||||
it('Should remove required properties on the target schema', () => {
|
||||
const A = Type.Object(
|
||||
{
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
z: Type.Number(),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
)
|
||||
const T = Type.Omit(A, ['z'])
|
||||
strictEqual(T.required!.includes('z'), false)
|
||||
})
|
||||
it('Should delete the required property if no required properties remain', () => {
|
||||
const A = Type.Object(
|
||||
{
|
||||
x: Type.Optional(Type.Number()),
|
||||
y: Type.Readonly(Type.Optional(Type.Number())),
|
||||
z: Type.Number(),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
)
|
||||
const T = Type.Omit(A, ['z'])
|
||||
strictEqual(T.required, undefined)
|
||||
})
|
||||
it('Should inherit options from the source object', () => {
|
||||
const A = Type.Object(
|
||||
{
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
z: Type.Number(),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
)
|
||||
const T = Type.Omit(A, ['z'])
|
||||
strictEqual(A.additionalProperties, false)
|
||||
strictEqual(T.additionalProperties, false)
|
||||
})
|
||||
it('Should omit with keyof object', () => {
|
||||
const A = Type.Object({
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
z: Type.Number(),
|
||||
})
|
||||
const B = Type.Object({
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
})
|
||||
const T = Type.Omit(A, Type.KeyOf(B), { additionalProperties: false })
|
||||
Ok(T, { z: 0 })
|
||||
Fail(T, { x: 0, y: 0, z: 0 })
|
||||
})
|
||||
})
|
||||
27
test/runtime/compiler-ajv/optional.ts
Normal file
27
test/runtime/compiler-ajv/optional.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { strictEqual } from 'assert'
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok } from './validate'
|
||||
|
||||
describe('compiler-ajv/Optional', () => {
|
||||
it('Should validate object with optional', () => {
|
||||
const T = Type.Object(
|
||||
{
|
||||
a: Type.Optional(Type.String()),
|
||||
b: Type.String(),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
)
|
||||
Ok(T, { a: 'hello', b: 'world' })
|
||||
Ok(T, { b: 'world' })
|
||||
})
|
||||
it('Should remove required value from schema', () => {
|
||||
const T = Type.Object(
|
||||
{
|
||||
a: Type.Optional(Type.String()),
|
||||
b: Type.String(),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
)
|
||||
strictEqual(T.required!.includes('a'), false)
|
||||
})
|
||||
})
|
||||
52
test/runtime/compiler-ajv/partial.ts
Normal file
52
test/runtime/compiler-ajv/partial.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { Type, ReadonlyKind, OptionalKind } from '@sinclair/typebox'
|
||||
import { Ok } from './validate'
|
||||
import { Assert } from '../assert'
|
||||
|
||||
describe('compiler-ajv/Partial', () => {
|
||||
it('Should convert a required object into a partial.', () => {
|
||||
const A = Type.Object(
|
||||
{
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
z: Type.Number(),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
)
|
||||
const T = Type.Partial(A)
|
||||
Ok(T, { x: 1, y: 1, z: 1 })
|
||||
Ok(T, { x: 1, y: 1 })
|
||||
Ok(T, { x: 1 })
|
||||
Ok(T, {})
|
||||
})
|
||||
it('Should update modifier types correctly when converting to partial', () => {
|
||||
const A = Type.Object(
|
||||
{
|
||||
x: Type.Readonly(Type.Optional(Type.Number())),
|
||||
y: Type.Readonly(Type.Number()),
|
||||
z: Type.Optional(Type.Number()),
|
||||
w: Type.Number(),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
)
|
||||
const T = Type.Partial(A)
|
||||
Assert.IsEqual(T.properties.x[ReadonlyKind], 'Readonly')
|
||||
Assert.IsEqual(T.properties.x[OptionalKind], 'Optional')
|
||||
Assert.IsEqual(T.properties.y[ReadonlyKind], 'Readonly')
|
||||
Assert.IsEqual(T.properties.y[OptionalKind], 'Optional')
|
||||
Assert.IsEqual(T.properties.z[OptionalKind], 'Optional')
|
||||
Assert.IsEqual(T.properties.w[OptionalKind], 'Optional')
|
||||
})
|
||||
it('Should inherit options from the source object', () => {
|
||||
const A = Type.Object(
|
||||
{
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
z: Type.Number(),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
)
|
||||
const T = Type.Partial(A)
|
||||
Assert.IsEqual(A.additionalProperties, false)
|
||||
Assert.IsEqual(T.additionalProperties, false)
|
||||
})
|
||||
})
|
||||
70
test/runtime/compiler-ajv/pick.ts
Normal file
70
test/runtime/compiler-ajv/pick.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
import { Assert } from '../assert'
|
||||
|
||||
describe('compiler-ajv/Pick', () => {
|
||||
it('Should pick properties from the source schema', () => {
|
||||
const Vector3 = Type.Object(
|
||||
{
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
z: Type.Number(),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
)
|
||||
const T = Type.Pick(Vector3, ['x', 'y'])
|
||||
Ok(T, { x: 1, y: 1 })
|
||||
})
|
||||
it('Should remove required properties on the target schema', () => {
|
||||
const A = Type.Object(
|
||||
{
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
z: Type.Number(),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
)
|
||||
const T = Type.Pick(A, ['x', 'y'])
|
||||
Assert.IsEqual(T.required!.includes('z'), false)
|
||||
})
|
||||
it('Should delete the required property if no required properties remain', () => {
|
||||
const A = Type.Object(
|
||||
{
|
||||
x: Type.Optional(Type.Number()),
|
||||
y: Type.Readonly(Type.Optional(Type.Number())),
|
||||
z: Type.Number(),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
)
|
||||
const T = Type.Pick(A, ['x', 'y'])
|
||||
Assert.IsEqual(T.required, undefined)
|
||||
})
|
||||
it('Should inherit options from the source object', () => {
|
||||
const A = Type.Object(
|
||||
{
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
z: Type.Number(),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
)
|
||||
const T = Type.Pick(A, ['x', 'y'])
|
||||
Assert.IsEqual(A.additionalProperties, false)
|
||||
Assert.IsEqual(T.additionalProperties, false)
|
||||
})
|
||||
|
||||
it('Should pick with keyof object', () => {
|
||||
const A = Type.Object({
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
z: Type.Number(),
|
||||
})
|
||||
const B = Type.Object({
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
})
|
||||
const T = Type.Pick(A, Type.KeyOf(B), { additionalProperties: false })
|
||||
Ok(T, { x: 0, y: 0 })
|
||||
Fail(T, { x: 0, y: 0, z: 0 })
|
||||
})
|
||||
})
|
||||
27
test/runtime/compiler-ajv/readonly-optional.ts
Normal file
27
test/runtime/compiler-ajv/readonly-optional.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok } from './validate'
|
||||
import { Assert } from '../assert'
|
||||
|
||||
describe('compiler-ajv/ReadonlyOptional', () => {
|
||||
it('Should validate object with optional', () => {
|
||||
const T = Type.Object(
|
||||
{
|
||||
a: Type.Readonly(Type.Optional(Type.String())),
|
||||
b: Type.String(),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
)
|
||||
Ok(T, { a: 'hello', b: 'world' })
|
||||
Ok(T, { b: 'world' })
|
||||
})
|
||||
it('Should remove required value from schema', () => {
|
||||
const T = Type.Object(
|
||||
{
|
||||
a: Type.Readonly(Type.Optional(Type.String())),
|
||||
b: Type.String(),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
)
|
||||
Assert.IsEqual(T.required!.includes('a'), false)
|
||||
})
|
||||
})
|
||||
27
test/runtime/compiler-ajv/readonly.ts
Normal file
27
test/runtime/compiler-ajv/readonly.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok } from './validate'
|
||||
import { Assert } from '../assert'
|
||||
|
||||
describe('compiler-ajv/Readonly', () => {
|
||||
it('Should validate object with readonly', () => {
|
||||
const T = Type.Object(
|
||||
{
|
||||
a: Type.Readonly(Type.String()),
|
||||
b: Type.Readonly(Type.String()),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
)
|
||||
Ok(T, { a: 'hello', b: 'world' })
|
||||
})
|
||||
it('Should retain required array on object', () => {
|
||||
const T = Type.Object(
|
||||
{
|
||||
a: Type.Readonly(Type.String()),
|
||||
b: Type.Readonly(Type.String()),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
)
|
||||
Assert.IsEqual(T.required!.includes('a'), true)
|
||||
Assert.IsEqual(T.required!.includes('b'), true)
|
||||
})
|
||||
})
|
||||
293
test/runtime/compiler-ajv/record.ts
Normal file
293
test/runtime/compiler-ajv/record.ts
Normal file
@@ -0,0 +1,293 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler-ajv/Record', () => {
|
||||
it('Should validate when all property values are numbers', () => {
|
||||
const T = Type.Record(Type.String(), Type.Number())
|
||||
Ok(T, { a: 1, b: 2, c: 3 })
|
||||
})
|
||||
it('Should validate when all property keys are strings', () => {
|
||||
const T = Type.Record(Type.String(), Type.Number())
|
||||
Ok(T, { a: 1, b: 2, c: 3, '0': 4 })
|
||||
})
|
||||
it('Should not validate when below minProperties', () => {
|
||||
const T = Type.Record(Type.String(), Type.Number(), { minProperties: 4 })
|
||||
Ok(T, { a: 1, b: 2, c: 3, d: 4 })
|
||||
Fail(T, { a: 1, b: 2, c: 3 })
|
||||
})
|
||||
it('Should not validate when above maxProperties', () => {
|
||||
const T = Type.Record(Type.String(), Type.Number(), { maxProperties: 4 })
|
||||
Ok(T, { a: 1, b: 2, c: 3, d: 4 })
|
||||
Fail(T, { a: 1, b: 2, c: 3, d: 4, e: 5 })
|
||||
})
|
||||
it('Should not validate with illogical minProperties | maxProperties', () => {
|
||||
const T = Type.Record(Type.String(), Type.Number(), { minProperties: 5, maxProperties: 4 })
|
||||
Fail(T, { a: 1, b: 2, c: 3 })
|
||||
Fail(T, { a: 1, b: 2, c: 3, d: 4 })
|
||||
Fail(T, { a: 1, b: 2, c: 3, d: 4, e: 5 })
|
||||
})
|
||||
it('Should validate when specifying string union literals when additionalProperties is true', () => {
|
||||
const K = Type.Union([Type.Literal('a'), Type.Literal('b'), Type.Literal('c')])
|
||||
const T = Type.Record(K, Type.Number())
|
||||
Ok(T, { a: 1, b: 2, c: 3, d: 'hello' })
|
||||
})
|
||||
it('Should not validate when specifying string union literals when additionalProperties is false', () => {
|
||||
const K = Type.Union([Type.Literal('a'), Type.Literal('b'), Type.Literal('c')])
|
||||
const T = Type.Record(K, Type.Number(), { additionalProperties: false })
|
||||
Fail(T, { a: 1, b: 2, c: 3, d: 'hello' })
|
||||
})
|
||||
it('Should validate for keyof records', () => {
|
||||
const T = Type.Object({
|
||||
a: Type.String(),
|
||||
b: Type.Number(),
|
||||
c: Type.String(),
|
||||
})
|
||||
const R = Type.Record(Type.KeyOf(T), Type.Number())
|
||||
Ok(R, { a: 1, b: 2, c: 3 })
|
||||
})
|
||||
it('Should not validate for unknown key via keyof', () => {
|
||||
const T = Type.Object({
|
||||
a: Type.String(),
|
||||
b: Type.Number(),
|
||||
c: Type.String(),
|
||||
})
|
||||
const R = Type.Record(Type.KeyOf(T), Type.Number(), { additionalProperties: false })
|
||||
Fail(R, { a: 1, b: 2, c: 3, d: 4 })
|
||||
})
|
||||
it('Should validate when specifying regular expressions', () => {
|
||||
const K = Type.RegExp(/^op_.*$/)
|
||||
const T = Type.Record(K, Type.Number())
|
||||
Ok(T, {
|
||||
op_a: 1,
|
||||
op_b: 2,
|
||||
op_c: 3,
|
||||
})
|
||||
})
|
||||
it('Should not validate when specifying regular expressions and passing invalid property', () => {
|
||||
const K = Type.RegExp(/^op_.*$/)
|
||||
const T = Type.Record(K, Type.Number(), { additionalProperties: false })
|
||||
Fail(T, {
|
||||
op_a: 1,
|
||||
op_b: 2,
|
||||
aop_c: 3,
|
||||
})
|
||||
})
|
||||
it('Should validate with quoted string pattern', () => {
|
||||
const K = Type.String({ pattern: "'(a|b|c)" })
|
||||
const T = Type.Record(K, Type.Number())
|
||||
Ok(T, {
|
||||
"'a": 1,
|
||||
"'b": 2,
|
||||
"'c": 3,
|
||||
})
|
||||
})
|
||||
it('Should validate with forward-slash pattern', () => {
|
||||
const K = Type.String({ pattern: '/(a|b|c)' })
|
||||
const T = Type.Record(K, Type.Number())
|
||||
Ok(T, {
|
||||
'/a': 1,
|
||||
'/b': 2,
|
||||
'/c': 3,
|
||||
})
|
||||
})
|
||||
// ------------------------------------------------------------
|
||||
// Integer Keys
|
||||
// ------------------------------------------------------------
|
||||
it('Should validate when all property keys are integers', () => {
|
||||
const T = Type.Record(Type.Integer(), Type.Number())
|
||||
Ok(T, { '0': 1, '1': 2, '2': 3, '3': 4 })
|
||||
})
|
||||
it('Should validate when all property keys are integers, but one property is a string with varying type', () => {
|
||||
const T = Type.Record(Type.Integer(), Type.Number(), { additionalProperties: false })
|
||||
Fail(T, { '0': 1, '1': 2, '2': 3, '3': 4, a: 'hello' })
|
||||
})
|
||||
it('Should not validate if passing a leading zeros for integers keys', () => {
|
||||
const T = Type.Record(Type.Integer(), Type.Number(), { additionalProperties: false })
|
||||
Fail(T, {
|
||||
'00': 1,
|
||||
'01': 2,
|
||||
'02': 3,
|
||||
'03': 4,
|
||||
})
|
||||
})
|
||||
it('Should not validate if passing a signed integers keys', () => {
|
||||
const T = Type.Record(Type.Integer(), Type.Number(), { additionalProperties: false })
|
||||
Fail(T, {
|
||||
'-0': 1,
|
||||
'-1': 2,
|
||||
'-2': 3,
|
||||
'-3': 4,
|
||||
})
|
||||
})
|
||||
// ------------------------------------------------------------
|
||||
// Number Keys
|
||||
// ------------------------------------------------------------
|
||||
it('Should validate when all property keys are numbers', () => {
|
||||
const T = Type.Record(Type.Number(), Type.Number())
|
||||
Ok(T, { '0': 1, '1': 2, '2': 3, '3': 4 })
|
||||
})
|
||||
it('Should validate when all property keys are numbers, but one property is a string with varying type', () => {
|
||||
const T = Type.Record(Type.Number(), Type.Number(), { additionalProperties: false })
|
||||
Fail(T, { '0': 1, '1': 2, '2': 3, '3': 4, a: 'hello' })
|
||||
})
|
||||
it('Should not validate if passing a leading zeros for numeric keys', () => {
|
||||
const T = Type.Record(Type.Number(), Type.Number(), { additionalProperties: false })
|
||||
Fail(T, {
|
||||
'00': 1,
|
||||
'01': 2,
|
||||
'02': 3,
|
||||
'03': 4,
|
||||
})
|
||||
})
|
||||
it('Should not validate if passing a signed numeric keys', () => {
|
||||
const T = Type.Record(Type.Number(), Type.Number(), { additionalProperties: false })
|
||||
Fail(T, {
|
||||
'-0': 1,
|
||||
'-1': 2,
|
||||
'-2': 3,
|
||||
'-3': 4,
|
||||
})
|
||||
})
|
||||
it('Should not validate when all property keys are numbers, but one property is a string with varying type', () => {
|
||||
const T = Type.Record(Type.Number(), Type.Number(), { additionalProperties: false })
|
||||
Fail(T, { '0': 1, '1': 2, '2': 3, '3': 4, a: 'hello' })
|
||||
})
|
||||
// ------------------------------------------------------------
|
||||
// AdditionalProperties
|
||||
// ------------------------------------------------------------
|
||||
it('AdditionalProperties 1', () => {
|
||||
const T = Type.Record(Type.Number(), Type.String(), { additionalProperties: true })
|
||||
Ok(T, { 1: '', 2: '', x: 1, y: 2, z: 3 })
|
||||
})
|
||||
it('AdditionalProperties 2', () => {
|
||||
const T = Type.Record(Type.Number(), Type.String(), { additionalProperties: false })
|
||||
Ok(T, { 1: '', 2: '', 3: '' })
|
||||
})
|
||||
it('AdditionalProperties 3', () => {
|
||||
const T = Type.Record(Type.Number(), Type.String(), { additionalProperties: false })
|
||||
Fail(T, { 1: '', 2: '', x: '' })
|
||||
})
|
||||
it('AdditionalProperties 4', () => {
|
||||
const T = Type.Record(Type.Number(), Type.String(), { additionalProperties: Type.Boolean() })
|
||||
Fail(T, { 1: '', 2: '', x: '' })
|
||||
})
|
||||
it('AdditionalProperties 5', () => {
|
||||
const T = Type.Record(Type.Number(), Type.String(), { additionalProperties: Type.Boolean() })
|
||||
Ok(T, { 1: '', 2: '', x: true })
|
||||
})
|
||||
// ----------------------------------------------------------------
|
||||
// TemplateLiteral
|
||||
// ----------------------------------------------------------------
|
||||
it('TemplateLiteral 1', () => {
|
||||
const K = Type.TemplateLiteral('key${number}')
|
||||
const R = Type.Record(K, Type.Number(), { additionalProperties: false })
|
||||
Ok(R, {
|
||||
key0: 1,
|
||||
key1: 1,
|
||||
key2: 1,
|
||||
})
|
||||
})
|
||||
it('TemplateLiteral 2', () => {
|
||||
const K = Type.TemplateLiteral('key${number}')
|
||||
const R = Type.Record(K, Type.Number())
|
||||
Ok(R, { keyA: 0 })
|
||||
})
|
||||
it('TemplateLiteral 3', () => {
|
||||
const K = Type.TemplateLiteral('key${number}')
|
||||
const R = Type.Record(K, Type.Number(), { additionalProperties: false })
|
||||
Fail(R, { keyA: 0 })
|
||||
})
|
||||
it('TemplateLiteral 4', () => {
|
||||
const K = Type.TemplateLiteral('key${number}')
|
||||
const R = Type.Record(K, Type.Number())
|
||||
const T = Type.Object({ x: Type.Number(), y: Type.Number() })
|
||||
const I = Type.Intersect([R, T], { unevaluatedProperties: false })
|
||||
Ok(I, {
|
||||
x: 1,
|
||||
y: 2,
|
||||
key0: 1,
|
||||
key1: 1,
|
||||
key2: 1,
|
||||
})
|
||||
})
|
||||
it('TemplateLiteral 5', () => {
|
||||
const K = Type.TemplateLiteral('key${number}')
|
||||
const R = Type.Record(K, Type.Number())
|
||||
const T = Type.Object({ x: Type.Number(), y: Type.Number() })
|
||||
const I = Type.Intersect([R, T])
|
||||
Ok(I, {
|
||||
x: 1,
|
||||
y: 2,
|
||||
z: 3,
|
||||
key0: 1,
|
||||
key1: 1,
|
||||
key2: 1,
|
||||
})
|
||||
})
|
||||
it('TemplateLiteral 6', () => {
|
||||
const K = Type.TemplateLiteral('key${number}')
|
||||
const R = Type.Record(K, Type.Number())
|
||||
const T = Type.Object({ x: Type.Number(), y: Type.Number() })
|
||||
const I = Type.Intersect([R, T], { unevaluatedProperties: false })
|
||||
Fail(I, {
|
||||
x: 1,
|
||||
y: 2,
|
||||
z: 3,
|
||||
key0: 1,
|
||||
key1: 1,
|
||||
key2: 1,
|
||||
})
|
||||
})
|
||||
// ----------------------------------------------------------------
|
||||
// https://github.com/sinclairzx81/typebox/issues/916
|
||||
// ----------------------------------------------------------------
|
||||
it('Should validate for string keys', () => {
|
||||
const T = Type.Record(Type.String(), Type.Null(), {
|
||||
additionalProperties: false,
|
||||
})
|
||||
Ok(T, {
|
||||
a: null,
|
||||
b: null,
|
||||
0: null,
|
||||
1: null,
|
||||
})
|
||||
})
|
||||
it('Should validate for number keys', () => {
|
||||
const T = Type.Record(Type.Number(), Type.Null(), {
|
||||
additionalProperties: false,
|
||||
})
|
||||
Fail(T, {
|
||||
a: null,
|
||||
b: null,
|
||||
0: null,
|
||||
1: null,
|
||||
})
|
||||
Ok(T, {
|
||||
0: null,
|
||||
1: null,
|
||||
})
|
||||
})
|
||||
it('Should validate for any keys', () => {
|
||||
const T = Type.Record(Type.Any(), Type.Null(), {
|
||||
additionalProperties: false,
|
||||
})
|
||||
Ok(T, {
|
||||
a: null,
|
||||
b: null,
|
||||
0: null,
|
||||
1: null,
|
||||
})
|
||||
})
|
||||
it('Should validate for never keys', () => {
|
||||
const T = Type.Record(Type.Never(), Type.Null(), {
|
||||
additionalProperties: false,
|
||||
})
|
||||
Ok(T, {})
|
||||
Fail(T, {
|
||||
a: null,
|
||||
b: null,
|
||||
0: null,
|
||||
1: null,
|
||||
})
|
||||
})
|
||||
})
|
||||
79
test/runtime/compiler-ajv/recursive.ts
Normal file
79
test/runtime/compiler-ajv/recursive.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
import { Assert } from '../assert/index'
|
||||
|
||||
describe('compiler-ajv/Recursive', () => {
|
||||
it('Should generate default ordinal $id if not specified', () => {
|
||||
const Node = Type.Recursive((Node) =>
|
||||
Type.Object({
|
||||
id: Type.String(),
|
||||
nodes: Type.Array(Node),
|
||||
}),
|
||||
)
|
||||
Assert.IsFalse(Node.$id === undefined)
|
||||
})
|
||||
it('Should override default ordinal $id if specified', () => {
|
||||
const Node = Type.Recursive(
|
||||
(Node) =>
|
||||
Type.Object({
|
||||
id: Type.String(),
|
||||
nodes: Type.Array(Node),
|
||||
}),
|
||||
{ $id: 'Node' },
|
||||
)
|
||||
Assert.IsEqual(Node.$id === 'Node', true)
|
||||
})
|
||||
it('Should validate recursive node type', () => {
|
||||
const Node = Type.Recursive((This) =>
|
||||
Type.Object({
|
||||
id: Type.String(),
|
||||
nodes: Type.Array(This),
|
||||
}),
|
||||
)
|
||||
Ok(Node, {
|
||||
id: 'A',
|
||||
nodes: [
|
||||
{ id: 'B', nodes: [] },
|
||||
{ id: 'C', nodes: [] },
|
||||
],
|
||||
})
|
||||
})
|
||||
it('Should validate wrapped recursive node type', () => {
|
||||
const Node = Type.Tuple([
|
||||
Type.Recursive((This) =>
|
||||
Type.Object({
|
||||
id: Type.String(),
|
||||
nodes: Type.Array(This),
|
||||
}),
|
||||
),
|
||||
])
|
||||
Ok(Node, [
|
||||
{
|
||||
id: 'A',
|
||||
nodes: [
|
||||
{ id: 'B', nodes: [] },
|
||||
{ id: 'C', nodes: [] },
|
||||
],
|
||||
},
|
||||
])
|
||||
})
|
||||
it('Should not validate wrapped recursive node type with invalid id', () => {
|
||||
const Node = Type.Tuple([
|
||||
Type.Recursive((This) =>
|
||||
Type.Object({
|
||||
id: Type.String(),
|
||||
nodes: Type.Array(This),
|
||||
}),
|
||||
),
|
||||
])
|
||||
Fail(Node, [
|
||||
{
|
||||
id: 'A',
|
||||
nodes: [
|
||||
{ id: 1, nodes: [] },
|
||||
{ id: 'C', nodes: [] },
|
||||
],
|
||||
},
|
||||
])
|
||||
})
|
||||
})
|
||||
78
test/runtime/compiler-ajv/ref.ts
Normal file
78
test/runtime/compiler-ajv/ref.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler-ajv/Ref', () => {
|
||||
// ----------------------------------------------------------------
|
||||
// Deprecated
|
||||
// ----------------------------------------------------------------
|
||||
it('Should validate for Ref(Schema)', () => {
|
||||
const T = Type.Number({ $id: 'T' })
|
||||
const R = Type.Ref(T)
|
||||
Ok(R, 1234, [T])
|
||||
Fail(R, 'hello', [T])
|
||||
})
|
||||
// ----------------------------------------------------------------
|
||||
// Standard
|
||||
// ----------------------------------------------------------------
|
||||
it('Should should validate when referencing a type', () => {
|
||||
const T = Type.Object(
|
||||
{
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
z: Type.Number(),
|
||||
},
|
||||
{ $id: 'T' },
|
||||
)
|
||||
const R = Type.Ref(T.$id!)
|
||||
Ok(
|
||||
R,
|
||||
{
|
||||
x: 1,
|
||||
y: 2,
|
||||
z: 3,
|
||||
},
|
||||
[T],
|
||||
)
|
||||
})
|
||||
it('Should not validate when passing invalid data', () => {
|
||||
const T = Type.Object(
|
||||
{
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
z: Type.Number(),
|
||||
},
|
||||
{ $id: 'T' },
|
||||
)
|
||||
const R = Type.Ref(T.$id!)
|
||||
Fail(
|
||||
R,
|
||||
{
|
||||
x: 1,
|
||||
y: 2,
|
||||
},
|
||||
[T],
|
||||
)
|
||||
})
|
||||
it('Should de-reference object property schema', () => {
|
||||
const R = Type.Object(
|
||||
{
|
||||
name: Type.String(),
|
||||
},
|
||||
{ $id: 'R' },
|
||||
)
|
||||
|
||||
const T = Type.Object(
|
||||
{
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
z: Type.Number(),
|
||||
r: Type.Optional(Type.Ref(R.$id!)),
|
||||
},
|
||||
{ $id: 'T' },
|
||||
)
|
||||
Ok(T, { x: 1, y: 2, z: 3 }, [R])
|
||||
Ok(T, { x: 1, y: 2, z: 3, r: { name: 'hello' } }, [R])
|
||||
Fail(T, { x: 1, y: 2, z: 3, r: { name: 1 } }, [R])
|
||||
Fail(T, { x: 1, y: 2, z: 3, r: {} }, [R])
|
||||
})
|
||||
})
|
||||
55
test/runtime/compiler-ajv/required.ts
Normal file
55
test/runtime/compiler-ajv/required.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { Type, ReadonlyKind, OptionalKind } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
import { Assert } from '../assert'
|
||||
|
||||
describe('compiler-ajv/Required', () => {
|
||||
it('Should convert a partial object into a required object', () => {
|
||||
const A = Type.Object(
|
||||
{
|
||||
x: Type.Optional(Type.Number()),
|
||||
y: Type.Optional(Type.Number()),
|
||||
z: Type.Optional(Type.Number()),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
)
|
||||
const T = Type.Required(A)
|
||||
Ok(T, { x: 1, y: 1, z: 1 })
|
||||
Fail(T, { x: 1, y: 1 })
|
||||
Fail(T, { x: 1 })
|
||||
Fail(T, {})
|
||||
})
|
||||
it('Should update modifier types correctly when converting to required', () => {
|
||||
const A = Type.Object({
|
||||
x: Type.Readonly(Type.Optional(Type.Number())),
|
||||
y: Type.Readonly(Type.Number()),
|
||||
z: Type.Optional(Type.Number()),
|
||||
w: Type.Number(),
|
||||
})
|
||||
const T = Type.Required(A)
|
||||
Assert.IsEqual(T.properties.x[ReadonlyKind], 'Readonly')
|
||||
Assert.IsEqual(T.properties.y[ReadonlyKind], 'Readonly')
|
||||
Assert.IsEqual(T.properties.z[OptionalKind], undefined)
|
||||
Assert.IsEqual(T.properties.w[OptionalKind], undefined)
|
||||
})
|
||||
it('Should inherit options from the source object', () => {
|
||||
const A = Type.Object(
|
||||
{
|
||||
x: Type.Optional(Type.Number()),
|
||||
y: Type.Optional(Type.Number()),
|
||||
z: Type.Optional(Type.Number()),
|
||||
},
|
||||
{ additionalPropeties: false },
|
||||
)
|
||||
const T = Type.Required(A)
|
||||
Assert.IsEqual(A.additionalPropeties, false)
|
||||
Assert.IsEqual(T.additionalPropeties, false)
|
||||
})
|
||||
|
||||
// it('Should construct new object when targetting reference', () => {
|
||||
// const T = Type.Object({ a: Type.String(), b: Type.String() }, { $id: 'T' })
|
||||
// const R = Type.Ref(T)
|
||||
// const P = Type.Required(R)
|
||||
// strictEqual(P.properties.a.type, 'string')
|
||||
// strictEqual(P.properties.b.type, 'string')
|
||||
// })
|
||||
})
|
||||
65
test/runtime/compiler-ajv/string-pattern.ts
Normal file
65
test/runtime/compiler-ajv/string-pattern.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler-ajv/StringPattern', () => {
|
||||
//-----------------------------------------------------
|
||||
// Regular Expression
|
||||
//-----------------------------------------------------
|
||||
it('Should validate regular expression 1', () => {
|
||||
const T = Type.String({ pattern: /[012345]/.source })
|
||||
Ok(T, '0')
|
||||
Ok(T, '1')
|
||||
Ok(T, '2')
|
||||
Ok(T, '3')
|
||||
Ok(T, '4')
|
||||
Ok(T, '5')
|
||||
})
|
||||
it('Should validate regular expression 2', () => {
|
||||
const T = Type.String({ pattern: /true|false/.source })
|
||||
Ok(T, 'true')
|
||||
Ok(T, 'true')
|
||||
Ok(T, 'true')
|
||||
Ok(T, 'false')
|
||||
Ok(T, 'false')
|
||||
Ok(T, 'false')
|
||||
Fail(T, '6')
|
||||
})
|
||||
it('Should validate regular expression 3', () => {
|
||||
const T = Type.String({ pattern: /true|false/.source })
|
||||
Fail(T, 'unknown')
|
||||
})
|
||||
it('Should validate regular expression 4', () => {
|
||||
const T = Type.String({ pattern: /[\d]{5}/.source })
|
||||
Ok(T, '12345')
|
||||
})
|
||||
//-----------------------------------------------------
|
||||
// Regular Pattern
|
||||
//-----------------------------------------------------
|
||||
it('Should validate pattern 1', () => {
|
||||
const T = Type.String({ pattern: '[012345]' })
|
||||
Ok(T, '0')
|
||||
Ok(T, '1')
|
||||
Ok(T, '2')
|
||||
Ok(T, '3')
|
||||
Ok(T, '4')
|
||||
Ok(T, '5')
|
||||
})
|
||||
it('Should validate pattern 2', () => {
|
||||
const T = Type.String({ pattern: 'true|false' })
|
||||
Ok(T, 'true')
|
||||
Ok(T, 'true')
|
||||
Ok(T, 'true')
|
||||
Ok(T, 'false')
|
||||
Ok(T, 'false')
|
||||
Ok(T, 'false')
|
||||
Fail(T, '6')
|
||||
})
|
||||
it('Should validate pattern 3', () => {
|
||||
const T = Type.String({ pattern: 'true|false' })
|
||||
Fail(T, 'unknown')
|
||||
})
|
||||
it('Should validate pattern 4', () => {
|
||||
const T = Type.String({ pattern: '[\\d]{5}' })
|
||||
Ok(T, '12345')
|
||||
})
|
||||
})
|
||||
59
test/runtime/compiler-ajv/string.ts
Normal file
59
test/runtime/compiler-ajv/string.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler-ajv/String', () => {
|
||||
it('Should not validate number', () => {
|
||||
const T = Type.String()
|
||||
Fail(T, 1)
|
||||
})
|
||||
it('Should validate string', () => {
|
||||
const T = Type.String()
|
||||
Ok(T, 'hello')
|
||||
})
|
||||
it('Should not validate boolean', () => {
|
||||
const T = Type.String()
|
||||
Fail(T, true)
|
||||
})
|
||||
it('Should not validate array', () => {
|
||||
const T = Type.String()
|
||||
Fail(T, [1, 2, 3])
|
||||
})
|
||||
it('Should not validate object', () => {
|
||||
const T = Type.String()
|
||||
Fail(T, { a: 1, b: 2 })
|
||||
})
|
||||
it('Should not validate null', () => {
|
||||
const T = Type.String()
|
||||
Fail(T, null)
|
||||
})
|
||||
it('Should not validate undefined', () => {
|
||||
const T = Type.String()
|
||||
Fail(T, undefined)
|
||||
})
|
||||
it('Should validate string format as email', () => {
|
||||
const T = Type.String({ format: 'email' })
|
||||
Ok(T, 'name@domain.com')
|
||||
})
|
||||
it('Should validate string format as uuid', () => {
|
||||
const T = Type.String({ format: 'uuid' })
|
||||
Ok(T, '4a7a17c9-2492-4a53-8e13-06ea2d3f3bbf')
|
||||
})
|
||||
it('Should validate string format as iso8601 date', () => {
|
||||
const T = Type.String({ format: 'date-time' })
|
||||
Ok(T, '2021-06-11T20:30:00-04:00')
|
||||
})
|
||||
it('Should validate minLength', () => {
|
||||
const T = Type.String({ minLength: 4 })
|
||||
Ok(T, '....')
|
||||
Fail(T, '...')
|
||||
})
|
||||
it('Should validate maxLength', () => {
|
||||
const T = Type.String({ maxLength: 4 })
|
||||
Ok(T, '....')
|
||||
Fail(T, '.....')
|
||||
})
|
||||
it('Should pass numeric 5 digit test', () => {
|
||||
const T = Type.String({ pattern: '[\\d]{5}' })
|
||||
Ok(T, '12345')
|
||||
})
|
||||
})
|
||||
185
test/runtime/compiler-ajv/template-literal.ts
Normal file
185
test/runtime/compiler-ajv/template-literal.ts
Normal file
@@ -0,0 +1,185 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler-ajv/TemplateLiteral', () => {
|
||||
// --------------------------------------------------------
|
||||
// Finite
|
||||
// --------------------------------------------------------
|
||||
it('Should validate finite pattern 1', () => {
|
||||
// prettier-ignore
|
||||
const T = Type.TemplateLiteral([])
|
||||
Ok(T, '')
|
||||
Fail(T, 'X')
|
||||
})
|
||||
it('Should validate finite pattern 1', () => {
|
||||
// prettier-ignore
|
||||
const T = Type.TemplateLiteral([Type.Boolean()])
|
||||
Ok(T, 'true')
|
||||
Ok(T, 'false')
|
||||
Fail(T, 'X')
|
||||
})
|
||||
it('Should validate finite pattern 2', () => {
|
||||
// prettier-ignore
|
||||
const T = Type.TemplateLiteral([
|
||||
Type.Literal('A')
|
||||
])
|
||||
Ok(T, 'A')
|
||||
Fail(T, 'X')
|
||||
})
|
||||
it('Should validate finite pattern 3', () => {
|
||||
// prettier-ignore
|
||||
const T = Type.TemplateLiteral([
|
||||
Type.Literal('A'),
|
||||
Type.Literal('B')
|
||||
])
|
||||
Ok(T, 'AB')
|
||||
Fail(T, 'X')
|
||||
})
|
||||
it('Should validate finite pattern 4', () => {
|
||||
// prettier-ignore
|
||||
const T = Type.TemplateLiteral([
|
||||
Type.Literal('A'),
|
||||
Type.Union([
|
||||
Type.Literal('B'),
|
||||
Type.Literal('C')
|
||||
]),
|
||||
])
|
||||
Ok(T, 'AB')
|
||||
Ok(T, 'AC')
|
||||
Fail(T, 'X')
|
||||
})
|
||||
it('Should validate finite pattern 5', () => {
|
||||
// prettier-ignore
|
||||
const T = Type.TemplateLiteral([
|
||||
Type.Literal('A'),
|
||||
Type.Union([
|
||||
Type.Literal('B'),
|
||||
Type.Literal('C')
|
||||
]),
|
||||
Type.Literal('D'),
|
||||
])
|
||||
Ok(T, 'ABD')
|
||||
Ok(T, 'ACD')
|
||||
Fail(T, 'X')
|
||||
})
|
||||
it('Should validate finite pattern 6', () => {
|
||||
// prettier-ignore
|
||||
const T = Type.TemplateLiteral([
|
||||
Type.Union([
|
||||
Type.Literal('0'),
|
||||
Type.Literal('1')
|
||||
]),
|
||||
Type.Union([
|
||||
Type.Literal('0'),
|
||||
Type.Literal('1')
|
||||
]),
|
||||
])
|
||||
Ok(T, '00')
|
||||
Ok(T, '01')
|
||||
Ok(T, '10')
|
||||
Ok(T, '11')
|
||||
Fail(T, 'X')
|
||||
})
|
||||
// --------------------------------------------------------
|
||||
// Infinite
|
||||
// --------------------------------------------------------
|
||||
it('Should validate infinite pattern 1', () => {
|
||||
// prettier-ignore
|
||||
const T = Type.TemplateLiteral([
|
||||
Type.Number()
|
||||
])
|
||||
Ok(T, '1')
|
||||
Ok(T, '22')
|
||||
Ok(T, '333')
|
||||
Ok(T, '4444')
|
||||
Fail(T, 'X')
|
||||
})
|
||||
it('Should validate infinite pattern 2', () => {
|
||||
// prettier-ignore
|
||||
const T = Type.TemplateLiteral([
|
||||
Type.Integer()
|
||||
])
|
||||
Ok(T, '1')
|
||||
Ok(T, '22')
|
||||
Ok(T, '333')
|
||||
Ok(T, '4444')
|
||||
Fail(T, 'X')
|
||||
})
|
||||
it('Should validate infinite pattern 3', () => {
|
||||
// prettier-ignore
|
||||
const T = Type.TemplateLiteral([
|
||||
Type.BigInt()
|
||||
])
|
||||
Ok(T, '1')
|
||||
Ok(T, '22')
|
||||
Ok(T, '333')
|
||||
Ok(T, '4444')
|
||||
Fail(T, 'X')
|
||||
})
|
||||
it('Should validate infinite pattern 4', () => {
|
||||
// prettier-ignore
|
||||
const T = Type.TemplateLiteral([
|
||||
Type.String()
|
||||
])
|
||||
Ok(T, '1')
|
||||
Ok(T, '22')
|
||||
Ok(T, '333')
|
||||
Ok(T, '4444')
|
||||
Ok(T, 'a')
|
||||
Ok(T, 'bb')
|
||||
Ok(T, 'ccc')
|
||||
Ok(T, 'dddd')
|
||||
})
|
||||
it('Should validate infinite pattern 5', () => {
|
||||
// prettier-ignore
|
||||
const T = Type.TemplateLiteral([
|
||||
Type.Literal('A'),
|
||||
Type.Number()
|
||||
])
|
||||
Ok(T, 'A1')
|
||||
Ok(T, 'A22')
|
||||
Ok(T, 'A333')
|
||||
Ok(T, 'A4444')
|
||||
Fail(T, 'X')
|
||||
})
|
||||
it('Should validate infinite pattern 6', () => {
|
||||
// prettier-ignore
|
||||
const T = Type.TemplateLiteral([
|
||||
Type.Literal('A'),
|
||||
Type.Integer()
|
||||
])
|
||||
Ok(T, 'A1')
|
||||
Ok(T, 'A22')
|
||||
Ok(T, 'A333')
|
||||
Ok(T, 'A4444')
|
||||
Fail(T, 'X')
|
||||
})
|
||||
it('Should validate infinite pattern 7', () => {
|
||||
// prettier-ignore
|
||||
const T = Type.TemplateLiteral([
|
||||
Type.Literal('A'),
|
||||
Type.BigInt()
|
||||
])
|
||||
Ok(T, 'A1')
|
||||
Ok(T, 'A22')
|
||||
Ok(T, 'A333')
|
||||
Ok(T, 'A4444')
|
||||
Fail(T, 'X')
|
||||
})
|
||||
it('Should validate infinite pattern 8', () => {
|
||||
// prettier-ignore
|
||||
const T = Type.TemplateLiteral([
|
||||
Type.Literal('A'),
|
||||
Type.String()
|
||||
])
|
||||
Ok(T, 'A1')
|
||||
Ok(T, 'A22')
|
||||
Ok(T, 'A333')
|
||||
Ok(T, 'A4444')
|
||||
Ok(T, 'Aa')
|
||||
Ok(T, 'Abb')
|
||||
Ok(T, 'Accc')
|
||||
Ok(T, 'Adddd')
|
||||
Fail(T, 'X')
|
||||
})
|
||||
})
|
||||
53
test/runtime/compiler-ajv/tuple.ts
Normal file
53
test/runtime/compiler-ajv/tuple.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler-ajv/Tuple', () => {
|
||||
it('Should validate tuple of [string, number]', () => {
|
||||
const A = Type.String()
|
||||
const B = Type.Number()
|
||||
const T = Type.Tuple([A, B])
|
||||
Ok(T, ['hello', 42])
|
||||
})
|
||||
it('Should not validate tuple of [string, number] when reversed', () => {
|
||||
const A = Type.String()
|
||||
const B = Type.Number()
|
||||
const T = Type.Tuple([A, B])
|
||||
Fail(T, [42, 'hello'])
|
||||
})
|
||||
it('Should validate with empty tuple', () => {
|
||||
const T = Type.Tuple([])
|
||||
Ok(T, [])
|
||||
})
|
||||
it('Should not validate with empty tuple with more items', () => {
|
||||
const T = Type.Tuple([])
|
||||
Fail(T, [1])
|
||||
})
|
||||
it('Should not validate with empty tuple with less items', () => {
|
||||
const T = Type.Tuple([Type.Number(), Type.Number()])
|
||||
Fail(T, [1])
|
||||
})
|
||||
it('Should validate tuple of objects', () => {
|
||||
const A = Type.Object({ a: Type.String() })
|
||||
const B = Type.Object({ b: Type.Number() })
|
||||
const T = Type.Tuple([A, B])
|
||||
Ok(T, [{ a: 'hello' }, { b: 42 }])
|
||||
})
|
||||
it('Should not validate tuple of objects when reversed', () => {
|
||||
const A = Type.Object({ a: Type.String() })
|
||||
const B = Type.Object({ b: Type.Number() })
|
||||
const T = Type.Tuple([A, B])
|
||||
Fail(T, [{ b: 42 }, { a: 'hello' }])
|
||||
})
|
||||
it('Should not validate tuple when array is less than tuple length', () => {
|
||||
const A = Type.Object({ a: Type.String() })
|
||||
const B = Type.Object({ b: Type.Number() })
|
||||
const T = Type.Tuple([A, B])
|
||||
Fail(T, [{ a: 'hello' }])
|
||||
})
|
||||
it('Should not validate tuple when array is greater than tuple length', () => {
|
||||
const A = Type.Object({ a: Type.String() })
|
||||
const B = Type.Object({ b: Type.Number() })
|
||||
const T = Type.Tuple([A, B])
|
||||
Fail(T, [{ a: 'hello' }, { b: 42 }, { b: 42 }])
|
||||
})
|
||||
})
|
||||
49
test/runtime/compiler-ajv/uint8array.ts
Normal file
49
test/runtime/compiler-ajv/uint8array.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
// ---------------------------------------------------
|
||||
// No Longer Supported
|
||||
// ---------------------------------------------------
|
||||
// import { Type } from '@sinclair/typebox'
|
||||
// import { Ok, Fail } from './validate'
|
||||
// describe('compiler-ajv/Uint8Array', () => {
|
||||
// it('Should not validate number', () => {
|
||||
// const T = Type.Uint8Array()
|
||||
// Fail(T, 1)
|
||||
// })
|
||||
// it('Should not validate string', () => {
|
||||
// const T = Type.Uint8Array()
|
||||
// Fail(T, 'hello')
|
||||
// })
|
||||
// it('Should not validate boolean', () => {
|
||||
// const T = Type.Uint8Array()
|
||||
// Fail(T, true)
|
||||
// })
|
||||
// it('Should not validate array', () => {
|
||||
// const T = Type.Uint8Array()
|
||||
// Fail(T, [1, 2, 3])
|
||||
// })
|
||||
// it('Should not validate object', () => {
|
||||
// const T = Type.Uint8Array()
|
||||
// Fail(T, { a: 1, b: 2 })
|
||||
// })
|
||||
// it('Should not validate null', () => {
|
||||
// const T = Type.Uint8Array()
|
||||
// Fail(T, null)
|
||||
// })
|
||||
// it('Should not validate undefined', () => {
|
||||
// const T = Type.Uint8Array()
|
||||
// Fail(T, undefined)
|
||||
// })
|
||||
// it('Should validate Uint8Array', () => {
|
||||
// const T = Type.Uint8Array()
|
||||
// Ok(T, new Uint8Array(100))
|
||||
// })
|
||||
// it('Should validate minByteLength', () => {
|
||||
// const T = Type.Uint8Array({ minByteLength: 4 })
|
||||
// Ok(T, new Uint8Array(4))
|
||||
// Fail(T, new Uint8Array(3))
|
||||
// })
|
||||
// it('Should validate maxByteLength', () => {
|
||||
// const T = Type.Uint8Array({ maxByteLength: 4 })
|
||||
// Ok(T, new Uint8Array(4))
|
||||
// Fail(T, new Uint8Array(5))
|
||||
// })
|
||||
// })
|
||||
56
test/runtime/compiler-ajv/union.ts
Normal file
56
test/runtime/compiler-ajv/union.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler-ajv/Union', () => {
|
||||
it('Should validate union of string, number and boolean', () => {
|
||||
const A = Type.String()
|
||||
const B = Type.Number()
|
||||
const C = Type.Boolean()
|
||||
const T = Type.Union([A, B, C])
|
||||
Ok(T, 'hello')
|
||||
Ok(T, true)
|
||||
Ok(T, 42)
|
||||
})
|
||||
it('Should validate union of objects', () => {
|
||||
const A = Type.Object({ a: Type.String() }, { additionalProperties: false })
|
||||
const B = Type.Object({ b: Type.String() }, { additionalProperties: false })
|
||||
const T = Type.Union([A, B])
|
||||
Ok(T, { a: 'hello' })
|
||||
Ok(T, { b: 'world' })
|
||||
})
|
||||
it('Should fail to validate for descriminated union types', () => {
|
||||
const A = Type.Object({ kind: Type.Literal('A'), value: Type.String() })
|
||||
const B = Type.Object({ kind: Type.Literal('B'), value: Type.Number() })
|
||||
const T = Type.Union([A, B])
|
||||
Fail(T, { kind: 'A', value: 42 }) // expect { kind: 'A', value: string }
|
||||
Fail(T, { kind: 'B', value: 'hello' }) // expect { kind: 'B', value: number }
|
||||
})
|
||||
it('Should validate union of objects where properties overlap', () => {
|
||||
const A = Type.Object({ a: Type.String() }, { additionalProperties: false })
|
||||
const B = Type.Object({ a: Type.String(), b: Type.String() }, { additionalProperties: false })
|
||||
const T = Type.Union([A, B])
|
||||
Ok(T, { a: 'hello' }) // A
|
||||
Ok(T, { a: 'hello', b: 'world' }) // B
|
||||
})
|
||||
it('Should validate union of overlapping property of varying type', () => {
|
||||
const A = Type.Object({ a: Type.String(), b: Type.Number() }, { additionalProperties: false })
|
||||
const B = Type.Object({ a: Type.String(), b: Type.String() }, { additionalProperties: false })
|
||||
const T = Type.Union([A, B])
|
||||
Ok(T, { a: 'hello', b: 42 }) // A
|
||||
Ok(T, { a: 'hello', b: 'world' }) // B
|
||||
})
|
||||
it('Should validate union of literal strings', () => {
|
||||
const A = Type.Literal('hello')
|
||||
const B = Type.Literal('world')
|
||||
const T = Type.Union([A, B])
|
||||
Ok(T, 'hello') // A
|
||||
Ok(T, 'world') // B
|
||||
})
|
||||
it('Should not validate union of literal strings for unknown string', () => {
|
||||
const A = Type.Literal('hello')
|
||||
const B = Type.Literal('world')
|
||||
const T = Type.Union([A, B])
|
||||
Fail(T, 'foo') // A
|
||||
Fail(T, 'bar') // B
|
||||
})
|
||||
})
|
||||
33
test/runtime/compiler-ajv/unknown.ts
Normal file
33
test/runtime/compiler-ajv/unknown.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok } from './validate'
|
||||
|
||||
describe('compiler-ajv/Unknown', () => {
|
||||
it('Should validate number', () => {
|
||||
const T = Type.Any()
|
||||
Ok(T, 1)
|
||||
})
|
||||
it('Should validate string', () => {
|
||||
const T = Type.Any()
|
||||
Ok(T, 'hello')
|
||||
})
|
||||
it('Should validate boolean', () => {
|
||||
const T = Type.Any()
|
||||
Ok(T, true)
|
||||
})
|
||||
it('Should validate array', () => {
|
||||
const T = Type.Any()
|
||||
Ok(T, [1, 2, 3])
|
||||
})
|
||||
it('Should validate object', () => {
|
||||
const T = Type.Any()
|
||||
Ok(T, { a: 1, b: 2 })
|
||||
})
|
||||
it('Should validate null', () => {
|
||||
const T = Type.Any()
|
||||
Ok(T, null)
|
||||
})
|
||||
it('Should validate undefined', () => {
|
||||
const T = Type.Any()
|
||||
Ok(T, undefined)
|
||||
})
|
||||
})
|
||||
18
test/runtime/compiler-ajv/unsafe.ts
Normal file
18
test/runtime/compiler-ajv/unsafe.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler-ajv/Unsafe', () => {
|
||||
it('Should validate an unsafe type', () => {
|
||||
const T = Type.Unsafe({
|
||||
type: 'object',
|
||||
properties: {
|
||||
x: { type: 'number' },
|
||||
y: { type: 'number' },
|
||||
z: { type: 'number' },
|
||||
},
|
||||
additionalProperties: false,
|
||||
})
|
||||
Ok(T, { x: 1, y: 2, z: 3 })
|
||||
Fail(T, { x: 1, y: 2, z: 3, w: 4 })
|
||||
})
|
||||
})
|
||||
91
test/runtime/compiler-ajv/validate.ts
Normal file
91
test/runtime/compiler-ajv/validate.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
import { TypeGuard } from '@sinclair/typebox'
|
||||
import { Value } from '@sinclair/typebox/value'
|
||||
|
||||
import { TSchema } from '@sinclair/typebox'
|
||||
import addFormats from 'ajv-formats'
|
||||
import Ajv, { AnySchema } from 'ajv/dist/2019'
|
||||
|
||||
function schemaOf(schemaOf: string, value: unknown, schema: unknown) {
|
||||
switch (schemaOf) {
|
||||
case 'Constructor':
|
||||
return TypeGuard.IsConstructor(schema) && Value.Check(schema, value)
|
||||
case 'Function':
|
||||
return TypeGuard.IsFunction(schema) && Value.Check(schema, value)
|
||||
case 'Date':
|
||||
return TypeGuard.IsDate(schema) && Value.Check(schema, value)
|
||||
case 'Promise':
|
||||
return TypeGuard.IsPromise(schema) && Value.Check(schema, value)
|
||||
case 'Uint8Array':
|
||||
return TypeGuard.IsUint8Array(schema) && Value.Check(schema, value)
|
||||
case 'Undefined':
|
||||
return TypeGuard.IsUndefined(schema) && Value.Check(schema, value)
|
||||
case 'Void':
|
||||
return TypeGuard.IsVoid(schema) && Value.Check(schema, value)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
export function createAjv(references: AnySchema[]) {
|
||||
return addFormats(new Ajv({}), ['date-time', 'time', 'date', 'email', 'hostname', 'ipv4', 'ipv6', 'uri', 'uri-reference', 'uuid', 'uri-template', 'json-pointer', 'relative-json-pointer', 'regex'])
|
||||
.addKeyword({ type: 'object', keyword: 'instanceOf', validate: schemaOf })
|
||||
.addKeyword({ type: 'null', keyword: 'typeOf', validate: schemaOf })
|
||||
.addKeyword('exclusiveMinimumTimestamp')
|
||||
.addKeyword('exclusiveMaximumTimestamp')
|
||||
.addKeyword('minimumTimestamp')
|
||||
.addKeyword('maximumTimestamp')
|
||||
.addKeyword('minByteLength')
|
||||
.addKeyword('maxByteLength')
|
||||
.addSchema(references)
|
||||
}
|
||||
export function Ok<T extends TSchema>(type: T, data: unknown, additional: AnySchema[] = []) {
|
||||
const ajv = createAjv(additional)
|
||||
function execute() {
|
||||
// required as ajv will throw if referenced schema is not found
|
||||
try {
|
||||
return ajv.validate(type, data) as boolean
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if (execute() === false) {
|
||||
console.log('---------------------------')
|
||||
console.log('type')
|
||||
console.log('---------------------------')
|
||||
console.log(JSON.stringify(type, null, 2))
|
||||
console.log('---------------------------')
|
||||
console.log('data')
|
||||
console.log('---------------------------')
|
||||
console.log(JSON.stringify(data, null, 2))
|
||||
console.log('---------------------------')
|
||||
console.log('errors')
|
||||
console.log('---------------------------')
|
||||
console.log(ajv.errorsText(ajv.errors))
|
||||
throw Error('expected ok')
|
||||
}
|
||||
}
|
||||
export function Fail<T extends TSchema>(type: T, data: unknown, additional: AnySchema[] = []) {
|
||||
const ajv = createAjv(additional)
|
||||
function execute() {
|
||||
// required as ajv will throw if referenced schema is not found
|
||||
try {
|
||||
return ajv.validate(type, data) as boolean
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if (execute() === true) {
|
||||
console.log('---------------------------')
|
||||
console.log('type')
|
||||
console.log('---------------------------')
|
||||
console.log(JSON.stringify(type, null, 2))
|
||||
console.log('---------------------------')
|
||||
console.log('data')
|
||||
console.log('---------------------------')
|
||||
console.log(JSON.stringify(data, null, 2))
|
||||
console.log('---------------------------')
|
||||
console.log('errors')
|
||||
console.log('---------------------------')
|
||||
console.log(ajv.errorsText(ajv.errors))
|
||||
throw Error('expected ok')
|
||||
}
|
||||
}
|
||||
33
test/runtime/compiler-ajv/void.ts
Normal file
33
test/runtime/compiler-ajv/void.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler-ajv/Void', () => {
|
||||
it('Should not validate number', () => {
|
||||
const T = Type.Void()
|
||||
Fail(T, 1)
|
||||
})
|
||||
it('Should not validate string', () => {
|
||||
const T = Type.Void()
|
||||
Fail(T, 'hello')
|
||||
})
|
||||
it('Should not validate boolean', () => {
|
||||
const T = Type.Void()
|
||||
Fail(T, true)
|
||||
})
|
||||
it('Should not validate array', () => {
|
||||
const T = Type.Void()
|
||||
Fail(T, [1, 2, 3])
|
||||
})
|
||||
it('Should not validate object', () => {
|
||||
const T = Type.Void()
|
||||
Fail(T, { a: 1, b: 2 })
|
||||
})
|
||||
it('Should validate null', () => {
|
||||
const T = Type.Null()
|
||||
Ok(T, null)
|
||||
})
|
||||
it('Should not validate undefined', () => {
|
||||
const T = Type.Void()
|
||||
Fail(T, undefined)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user