Publish
This commit is contained in:
57
test/runtime/assert/assert.ts
Normal file
57
test/runtime/assert/assert.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import * as assert from 'assert'
|
||||
|
||||
export namespace Assert {
|
||||
export function HasProperty<K extends PropertyKey>(value: unknown, key: K): asserts value is Record<K, unknown> {
|
||||
if (typeof value === 'object' && value !== null && key in value) return
|
||||
throw new Error(`Expected value to have property '${key as string}'`)
|
||||
}
|
||||
export function IsTrue(value: boolean): asserts value is true {
|
||||
return assert.strictEqual(value, true)
|
||||
}
|
||||
export function IsFalse(value: boolean): asserts value is false {
|
||||
return assert.strictEqual(value, false)
|
||||
}
|
||||
export function IsEqual(actual: unknown, expect: unknown) {
|
||||
if (actual instanceof Uint8Array && expect instanceof Uint8Array) {
|
||||
assert.equal(actual.length, expect.length)
|
||||
for (let i = 0; i < actual.length; i++) assert.equal(actual[i], expect[i])
|
||||
}
|
||||
return assert.deepStrictEqual(actual, expect)
|
||||
}
|
||||
export function NotEqual(actual: unknown, expect: unknown) {
|
||||
return assert.notEqual(actual, expect)
|
||||
}
|
||||
/** Asserts a numeric value is within range of the expected */
|
||||
export function InRange(value: number, expect: number, range: number) {
|
||||
if (Math.abs(value - expect) <= range) return
|
||||
throw Error('Expected value to be in range')
|
||||
}
|
||||
let nextIdOrdinal = 0
|
||||
export function NextId() {
|
||||
return `$id-${nextIdOrdinal++}`
|
||||
}
|
||||
export function Throws(callback: Function) {
|
||||
try {
|
||||
callback()
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
throw Error('Expected throw')
|
||||
}
|
||||
export async function ThrowsAsync(callback: Function) {
|
||||
try {
|
||||
await callback()
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
throw Error('Expected throw')
|
||||
}
|
||||
export function IsInstanceOf<T extends new (...args: any[]) => any>(value: any, constructor: T): asserts value is InstanceType<T> {
|
||||
if (value instanceof constructor) return
|
||||
throw Error(`Value is not instance of ${constructor}`)
|
||||
}
|
||||
export function IsTypeOf<T extends 'string' | 'boolean' | 'number' | 'bigint' | 'symbol' | 'object' | 'function'>(value: any, type: T) {
|
||||
if (typeof value === type) return
|
||||
throw Error(`Value is not typeof ${type}`)
|
||||
}
|
||||
}
|
||||
1
test/runtime/assert/index.ts
Normal file
1
test/runtime/assert/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './assert'
|
||||
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)
|
||||
})
|
||||
})
|
||||
20
test/runtime/compiler/__members.ts
Normal file
20
test/runtime/compiler/__members.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { TypeCompiler } from '@sinclair/typebox/compiler'
|
||||
import { Type, TypeGuard, ValueGuard } from '@sinclair/typebox'
|
||||
import { Assert } from '../assert/index'
|
||||
|
||||
describe('compiler/TypeCheckMembers', () => {
|
||||
it('Should return Schema', () => {
|
||||
const A = TypeCompiler.Compile(Type.Number(), [Type.String(), Type.Boolean()])
|
||||
Assert.IsTrue(TypeGuard.IsNumber(A.Schema()))
|
||||
})
|
||||
it('Should return References', () => {
|
||||
const A = TypeCompiler.Compile(Type.Number(), [Type.String(), Type.Boolean()])
|
||||
Assert.IsTrue(TypeGuard.IsNumber(A.Schema()))
|
||||
Assert.IsTrue(TypeGuard.IsString(A.References()[0]))
|
||||
Assert.IsTrue(TypeGuard.IsBoolean(A.References()[1]))
|
||||
})
|
||||
it('Should return Code', () => {
|
||||
const A = TypeCompiler.Compile(Type.Number(), [Type.String(), Type.Boolean()])
|
||||
Assert.IsTrue(ValueGuard.IsString(A.Code()))
|
||||
})
|
||||
})
|
||||
41
test/runtime/compiler/any.ts
Normal file
41
test/runtime/compiler/any.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok } from './validate'
|
||||
|
||||
describe('compiler/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)
|
||||
})
|
||||
it('Should validate bigint', () => {
|
||||
const T = Type.Any()
|
||||
Ok(T, BigInt(1))
|
||||
})
|
||||
it('Should validate symbol', () => {
|
||||
const T = Type.Any()
|
||||
Ok(T, Symbol(1))
|
||||
})
|
||||
})
|
||||
41
test/runtime/compiler/argument.ts
Normal file
41
test/runtime/compiler/argument.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok } from './validate'
|
||||
|
||||
describe('compiler/Argument', () => {
|
||||
it('Should validate number', () => {
|
||||
const T = Type.Argument(0)
|
||||
Ok(T, 1)
|
||||
})
|
||||
it('Should validate string', () => {
|
||||
const T = Type.Argument(0)
|
||||
Ok(T, 'hello')
|
||||
})
|
||||
it('Should validate boolean', () => {
|
||||
const T = Type.Argument(0)
|
||||
Ok(T, true)
|
||||
})
|
||||
it('Should validate array', () => {
|
||||
const T = Type.Argument(0)
|
||||
Ok(T, [1, 2, 3])
|
||||
})
|
||||
it('Should validate object', () => {
|
||||
const T = Type.Argument(0)
|
||||
Ok(T, { a: 1, b: 2 })
|
||||
})
|
||||
it('Should validate null', () => {
|
||||
const T = Type.Argument(0)
|
||||
Ok(T, null)
|
||||
})
|
||||
it('Should validate undefined', () => {
|
||||
const T = Type.Argument(0)
|
||||
Ok(T, undefined)
|
||||
})
|
||||
it('Should validate bigint', () => {
|
||||
const T = Type.Argument(0)
|
||||
Ok(T, BigInt(1))
|
||||
})
|
||||
it('Should validate symbol', () => {
|
||||
const T = Type.Argument(0)
|
||||
Ok(T, Symbol(1))
|
||||
})
|
||||
})
|
||||
186
test/runtime/compiler/array.ts
Normal file
186
test/runtime/compiler/array.ts
Normal file
@@ -0,0 +1,186 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler/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 },
|
||||
],
|
||||
})
|
||||
})
|
||||
})
|
||||
20
test/runtime/compiler/async-iterator.ts
Normal file
20
test/runtime/compiler/async-iterator.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler/AsyncIterator', () => {
|
||||
it('Should validate a async iterator 1', () => {
|
||||
async function* f() {}
|
||||
const T = Type.AsyncIterator(Type.Any())
|
||||
Ok(T, f())
|
||||
})
|
||||
it('Should validate a async iterator 2', () => {
|
||||
const T = Type.AsyncIterator(Type.Any())
|
||||
Ok(T, {
|
||||
[Symbol.asyncIterator]: () => {},
|
||||
})
|
||||
})
|
||||
it('Should not validate a async iterator', () => {
|
||||
const T = Type.AsyncIterator(Type.Any())
|
||||
Fail(T, {})
|
||||
})
|
||||
})
|
||||
80
test/runtime/compiler/bigint.ts
Normal file
80
test/runtime/compiler/bigint.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler/BigInt', () => {
|
||||
it('Should not validate number', () => {
|
||||
const T = Type.BigInt()
|
||||
Fail(T, 3.14)
|
||||
})
|
||||
it('Should not validate NaN', () => {
|
||||
const T = Type.BigInt()
|
||||
Fail(T, NaN)
|
||||
})
|
||||
it('Should not validate +Infinity', () => {
|
||||
const T = Type.BigInt()
|
||||
Fail(T, Infinity)
|
||||
})
|
||||
it('Should not validate -Infinity', () => {
|
||||
const T = Type.BigInt()
|
||||
Fail(T, -Infinity)
|
||||
})
|
||||
it('Should not validate integer', () => {
|
||||
const T = Type.BigInt()
|
||||
Fail(T, 1)
|
||||
})
|
||||
it('Should not validate string', () => {
|
||||
const T = Type.BigInt()
|
||||
Fail(T, 'hello')
|
||||
})
|
||||
it('Should not validate boolean', () => {
|
||||
const T = Type.BigInt()
|
||||
Fail(T, true)
|
||||
})
|
||||
it('Should not validate array', () => {
|
||||
const T = Type.BigInt()
|
||||
Fail(T, [1, 2, 3])
|
||||
})
|
||||
it('Should not validate object', () => {
|
||||
const T = Type.BigInt()
|
||||
Fail(T, { a: 1, b: 2 })
|
||||
})
|
||||
it('Should not validate null', () => {
|
||||
const T = Type.BigInt()
|
||||
Fail(T, null)
|
||||
})
|
||||
it('Should not validate undefined', () => {
|
||||
const T = Type.BigInt()
|
||||
Fail(T, undefined)
|
||||
})
|
||||
it('Should not validate symbol', () => {
|
||||
const T = Type.BigInt()
|
||||
Fail(T, Symbol())
|
||||
})
|
||||
it('Should validate bigint', () => {
|
||||
const T = Type.BigInt()
|
||||
Ok(T, BigInt(1))
|
||||
})
|
||||
it('Should validate minimum', () => {
|
||||
const T = Type.BigInt({ minimum: BigInt(10) })
|
||||
Fail(T, BigInt(9))
|
||||
Ok(T, BigInt(10))
|
||||
})
|
||||
it('Should validate maximum', () => {
|
||||
const T = Type.BigInt({ maximum: BigInt(10) })
|
||||
Ok(T, BigInt(10))
|
||||
Fail(T, BigInt(11))
|
||||
})
|
||||
it('Should validate Date exclusiveMinimum', () => {
|
||||
const T = Type.BigInt({ exclusiveMinimum: BigInt(10) })
|
||||
Fail(T, BigInt(10))
|
||||
Ok(T, BigInt(11))
|
||||
})
|
||||
it('Should validate Date exclusiveMaximum', () => {
|
||||
const T = Type.BigInt({ exclusiveMaximum: BigInt(10) })
|
||||
Ok(T, BigInt(9))
|
||||
Fail(T, BigInt(10))
|
||||
})
|
||||
it('Should not validate NaN', () => {
|
||||
Fail(Type.Number(), NaN)
|
||||
})
|
||||
})
|
||||
42
test/runtime/compiler/boolean.ts
Normal file
42
test/runtime/compiler/boolean.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler/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)
|
||||
})
|
||||
it('Should not validate bigint', () => {
|
||||
const T = Type.Boolean()
|
||||
Fail(T, BigInt(1))
|
||||
})
|
||||
it('Should not validate symbol', () => {
|
||||
const T = Type.Boolean()
|
||||
Fail(T, Symbol(1))
|
||||
})
|
||||
})
|
||||
101
test/runtime/compiler/composite.ts
Normal file
101
test/runtime/compiler/composite.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler/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 } })
|
||||
})
|
||||
// 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/const.ts
Normal file
39
test/runtime/compiler/const.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok } from './validate'
|
||||
|
||||
describe('compiler/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 },
|
||||
})
|
||||
})
|
||||
})
|
||||
80
test/runtime/compiler/constructor.ts
Normal file
80
test/runtime/compiler/constructor.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler/Constructor', () => {
|
||||
it('Should validate constructor 1', () => {
|
||||
const T = Type.Constructor([], Type.Object({}))
|
||||
Ok(T, class {})
|
||||
})
|
||||
it('Should validate constructor 2', () => {
|
||||
const T = Type.Constructor([Type.Number()], Type.Object({}))
|
||||
// note: constructor arguments are non-checkable
|
||||
Ok(T, class {})
|
||||
})
|
||||
it('Should validate constructor 3', () => {
|
||||
const T = Type.Constructor(
|
||||
[Type.Number()],
|
||||
Type.Object({
|
||||
method: Type.Function([], Type.Void()),
|
||||
}),
|
||||
)
|
||||
Ok(
|
||||
T,
|
||||
class {
|
||||
method() {}
|
||||
},
|
||||
)
|
||||
})
|
||||
it('Should validate constructor 4', () => {
|
||||
const T = Type.Constructor(
|
||||
[Type.Number()],
|
||||
Type.Object({
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
z: Type.Number(),
|
||||
}),
|
||||
)
|
||||
Ok(
|
||||
T,
|
||||
class {
|
||||
get x() {
|
||||
return 1
|
||||
}
|
||||
get y() {
|
||||
return 1
|
||||
}
|
||||
get z() {
|
||||
return 1
|
||||
}
|
||||
},
|
||||
)
|
||||
})
|
||||
it('Should not validate constructor 1', () => {
|
||||
const T = Type.Constructor([Type.Number()], Type.Object({}))
|
||||
Fail(T, 1)
|
||||
})
|
||||
it('Should not validate constructor 2', () => {
|
||||
const T = Type.Constructor(
|
||||
[Type.Number()],
|
||||
Type.Object({
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
z: Type.Number(),
|
||||
}),
|
||||
)
|
||||
Fail(
|
||||
T,
|
||||
class {
|
||||
get x() {
|
||||
return null
|
||||
}
|
||||
get y() {
|
||||
return null
|
||||
}
|
||||
get z() {
|
||||
return null
|
||||
}
|
||||
},
|
||||
)
|
||||
})
|
||||
})
|
||||
74
test/runtime/compiler/date.ts
Normal file
74
test/runtime/compiler/date.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler/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 bigint', () => {
|
||||
const T = Type.Date()
|
||||
Fail(T, BigInt(1))
|
||||
})
|
||||
it('Should not validate symbol', () => {
|
||||
const T = Type.Date()
|
||||
Fail(T, Symbol(1))
|
||||
})
|
||||
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 })
|
||||
Fail(T, new Date(11))
|
||||
Ok(T, new Date(10))
|
||||
})
|
||||
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 })
|
||||
Fail(T, new Date(10))
|
||||
Ok(T, new Date(9))
|
||||
})
|
||||
it('Should validate Date multipleOfTimestamp', () => {
|
||||
const T = Type.Date({ multipleOfTimestamp: 2 })
|
||||
Fail(T, new Date(1))
|
||||
Ok(T, new Date(2))
|
||||
})
|
||||
})
|
||||
53
test/runtime/compiler/enum.ts
Normal file
53
test/runtime/compiler/enum.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler/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)
|
||||
})
|
||||
})
|
||||
25
test/runtime/compiler/function.ts
Normal file
25
test/runtime/compiler/function.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler/Function', () => {
|
||||
it('Should validate function 1', () => {
|
||||
const T = Type.Function([Type.Number()], Type.Number())
|
||||
Ok(T, function () {})
|
||||
})
|
||||
it('Should validate function 2', () => {
|
||||
const T = Type.Function([Type.Number()], Type.Number())
|
||||
// note: validation only checks typeof 'function'
|
||||
Ok(T, function (a: string, b: string, c: string, d: string) {})
|
||||
})
|
||||
it('Should validate function 3', () => {
|
||||
const T = Type.Function([Type.Number()], Type.Number())
|
||||
// note: validation only checks typeof 'function'
|
||||
Ok(T, function () {
|
||||
return 'not-a-number'
|
||||
})
|
||||
})
|
||||
it('Should not validate function', () => {
|
||||
const T = Type.Function([Type.Number()], Type.Number())
|
||||
Fail(T, 1)
|
||||
})
|
||||
})
|
||||
46
test/runtime/compiler/index.ts
Normal file
46
test/runtime/compiler/index.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import './__members'
|
||||
import './any'
|
||||
import './argument'
|
||||
import './array'
|
||||
import './async-iterator'
|
||||
import './bigint'
|
||||
import './boolean'
|
||||
import './composite'
|
||||
import './const'
|
||||
import './constructor'
|
||||
import './date'
|
||||
import './unicode'
|
||||
import './enum'
|
||||
import './function'
|
||||
import './integer'
|
||||
import './intersect'
|
||||
import './iterator'
|
||||
import './keyof'
|
||||
import './kind'
|
||||
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 './regexp'
|
||||
import './required'
|
||||
import './string-pattern'
|
||||
import './string'
|
||||
import './symbol'
|
||||
import './template-literal'
|
||||
import './tuple'
|
||||
import './uint8array'
|
||||
import './union'
|
||||
import './unknown'
|
||||
import './void'
|
||||
77
test/runtime/compiler/integer.ts
Normal file
77
test/runtime/compiler/integer.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler/Integer', () => {
|
||||
it('Should not validate number', () => {
|
||||
const T = Type.Integer()
|
||||
Fail(T, 3.14)
|
||||
})
|
||||
it('Should not validate NaN', () => {
|
||||
const T = Type.Integer()
|
||||
Fail(T, NaN)
|
||||
})
|
||||
it('Should not validate +Infinity', () => {
|
||||
const T = Type.Integer()
|
||||
Fail(T, Infinity)
|
||||
})
|
||||
it('Should not validate -Infinity', () => {
|
||||
const T = Type.Integer()
|
||||
Fail(T, -Infinity)
|
||||
})
|
||||
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 not validate bigint', () => {
|
||||
const T = Type.Integer()
|
||||
Fail(T, BigInt(1))
|
||||
})
|
||||
it('Should not validate symbol', () => {
|
||||
const T = Type.Integer()
|
||||
Fail(T, Symbol(1))
|
||||
})
|
||||
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)
|
||||
})
|
||||
})
|
||||
225
test/runtime/compiler/intersect.ts
Normal file
225
test/runtime/compiler/intersect.ts
Normal file
@@ -0,0 +1,225 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler/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 nested objects and allow unevaluated properties of number', () => {
|
||||
const A = Type.Object({ x: Type.Number() })
|
||||
const B = Type.Object({ y: Type.Number() })
|
||||
const T = Type.Object({ nested: Type.Intersect([A, B], { unevaluatedProperties: Type.Number() }) })
|
||||
Ok(T, { nested: { x: 1, y: 2, z: 3 } })
|
||||
Fail(T, { nested: { 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 })
|
||||
})
|
||||
})
|
||||
20
test/runtime/compiler/iterator.ts
Normal file
20
test/runtime/compiler/iterator.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler/Iterator', () => {
|
||||
it('Should validate a iterator 1', () => {
|
||||
function* f() {}
|
||||
const T = Type.Iterator(Type.Any())
|
||||
Ok(T, f())
|
||||
})
|
||||
it('Should validate a iterator 2', () => {
|
||||
const T = Type.Iterator(Type.Any())
|
||||
Ok(T, {
|
||||
[Symbol.iterator]: () => {},
|
||||
})
|
||||
})
|
||||
it('Should not validate a iterator', () => {
|
||||
const T = Type.Iterator(Type.Any())
|
||||
Fail(T, {})
|
||||
})
|
||||
})
|
||||
48
test/runtime/compiler/keyof.ts
Normal file
48
test/runtime/compiler/keyof.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler/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')
|
||||
})
|
||||
})
|
||||
123
test/runtime/compiler/kind.ts
Normal file
123
test/runtime/compiler/kind.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
import { TypeRegistry, Type, Kind, TSchema } from '@sinclair/typebox'
|
||||
import { TypeCompiler } from '@sinclair/typebox/compiler'
|
||||
import { Ok, Fail } from './validate'
|
||||
import { Assert } from '../assert'
|
||||
|
||||
describe('compiler/Kind', () => {
|
||||
// ------------------------------------------------------------
|
||||
// Fixtures
|
||||
// ------------------------------------------------------------
|
||||
beforeEach(() => TypeRegistry.Set('PI', (_, value) => value === Math.PI))
|
||||
afterEach(() => TypeRegistry.Delete('PI'))
|
||||
// ------------------------------------------------------------
|
||||
// Tests
|
||||
// ------------------------------------------------------------
|
||||
it('Should validate', () => {
|
||||
const T = Type.Unsafe({ [Kind]: 'PI' })
|
||||
Ok(T, Math.PI)
|
||||
})
|
||||
it('Should not validate', () => {
|
||||
const T = Type.Unsafe({ [Kind]: 'PI' })
|
||||
Fail(T, Math.PI * 2)
|
||||
})
|
||||
it('Should validate in object', () => {
|
||||
const T = Type.Object({
|
||||
x: Type.Unsafe({ [Kind]: 'PI' }),
|
||||
})
|
||||
Ok(T, { x: Math.PI })
|
||||
})
|
||||
it('Should not validate in object', () => {
|
||||
const T = Type.Object({
|
||||
x: Type.Unsafe({ [Kind]: 'PI' }),
|
||||
})
|
||||
Fail(T, { x: Math.PI * 2 })
|
||||
})
|
||||
it('Should validate in array', () => {
|
||||
const T = Type.Array(Type.Unsafe({ [Kind]: 'PI' }))
|
||||
Ok(T, [Math.PI])
|
||||
})
|
||||
it('Should not validate in array', () => {
|
||||
const T = Type.Array(Type.Unsafe({ [Kind]: 'PI' }))
|
||||
Fail(T, [Math.PI * 2])
|
||||
})
|
||||
it('Should validate in tuple', () => {
|
||||
const T = Type.Tuple([Type.Unsafe({ [Kind]: 'PI' })])
|
||||
Ok(T, [Math.PI])
|
||||
})
|
||||
it('Should not validate in tuple', () => {
|
||||
const T = Type.Tuple([Type.Unsafe({ [Kind]: 'PI' })])
|
||||
Fail(T, [Math.PI * 2])
|
||||
})
|
||||
// ------------------------------------------------------------
|
||||
// Instances
|
||||
// ------------------------------------------------------------
|
||||
it('Should receive kind instance on registry callback', () => {
|
||||
const stack: string[] = []
|
||||
TypeRegistry.Set('Kind', (schema: unknown) => {
|
||||
// prettier-ignore
|
||||
return (typeof schema === 'object' && schema !== null && Kind in schema && schema[Kind] === 'Kind' && '$id' in schema && typeof schema.$id === 'string')
|
||||
? (() => { stack.push(schema.$id); return true })()
|
||||
: false
|
||||
})
|
||||
const A = { [Kind]: 'Kind', $id: 'A' } as TSchema
|
||||
const B = { [Kind]: 'Kind', $id: 'B' } as TSchema
|
||||
const T = Type.Object({ a: A, b: B })
|
||||
const C = TypeCompiler.Compile(T)
|
||||
const R = C.Check({ a: null, b: null })
|
||||
Assert.IsTrue(R)
|
||||
Assert.IsEqual(stack[0], 'A')
|
||||
Assert.IsEqual(stack[1], 'B')
|
||||
TypeRegistry.Delete('Kind')
|
||||
})
|
||||
// ------------------------------------------------------------
|
||||
// Instances Retain
|
||||
// ------------------------------------------------------------
|
||||
it('Should retain kind instances on subsequent compile', () => {
|
||||
let stack: string[] = []
|
||||
TypeRegistry.Set('Kind', (schema: unknown) => {
|
||||
// prettier-ignore
|
||||
return (typeof schema === 'object' && schema !== null && Kind in schema && schema[Kind] === 'Kind' && '$id' in schema && typeof schema.$id === 'string')
|
||||
? (() => { stack.push(schema.$id); return true })()
|
||||
: false
|
||||
})
|
||||
const A = { [Kind]: 'Kind', $id: 'A' } as TSchema
|
||||
const B = { [Kind]: 'Kind', $id: 'B' } as TSchema
|
||||
const C = { [Kind]: 'Kind', $id: 'C' } as TSchema
|
||||
const D = { [Kind]: 'Kind', $id: 'D' } as TSchema
|
||||
const T1 = Type.Object({ a: A, b: B })
|
||||
const T2 = Type.Object({ a: C, b: D })
|
||||
|
||||
// Compile T1 and run check, expect A and B
|
||||
const C1 = TypeCompiler.Compile(T1)
|
||||
const R1 = C1.Check({ a: null, b: null })
|
||||
Assert.IsTrue(R1)
|
||||
Assert.IsEqual(stack.length, 2)
|
||||
Assert.IsEqual(stack[0], 'A')
|
||||
Assert.IsEqual(stack[1], 'B')
|
||||
stack = []
|
||||
// compile T2 and force instance.clear()
|
||||
const C2 = TypeCompiler.Compile(T2)
|
||||
// run T1 check
|
||||
const R2 = C1.Check({ a: null, b: null })
|
||||
Assert.IsTrue(R2)
|
||||
Assert.IsEqual(stack.length, 2)
|
||||
Assert.IsEqual(stack[0], 'A')
|
||||
Assert.IsEqual(stack[1], 'B')
|
||||
stack = []
|
||||
// run T2 check
|
||||
const R3 = C2.Check({ a: null, b: null })
|
||||
Assert.IsTrue(R3)
|
||||
Assert.IsEqual(stack.length, 2)
|
||||
Assert.IsEqual(stack[0], 'C')
|
||||
Assert.IsEqual(stack[1], 'D')
|
||||
stack = []
|
||||
// run T1 check
|
||||
const R4 = C1.Check({ a: null, b: null })
|
||||
Assert.IsTrue(R4)
|
||||
Assert.IsEqual(stack.length, 2)
|
||||
Assert.IsEqual(stack[0], 'A')
|
||||
Assert.IsEqual(stack[1], 'B')
|
||||
stack = []
|
||||
TypeRegistry.Delete('Kind')
|
||||
})
|
||||
})
|
||||
50
test/runtime/compiler/literal.ts
Normal file
50
test/runtime/compiler/literal.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler/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/module.ts
Normal file
144
test/runtime/compiler/module.ts
Normal file
@@ -0,0 +1,144 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler/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')
|
||||
})
|
||||
})
|
||||
41
test/runtime/compiler/never.ts
Normal file
41
test/runtime/compiler/never.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Fail } from './validate'
|
||||
|
||||
describe('compiler/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 not validate undefined', () => {
|
||||
const T = Type.Never()
|
||||
Fail(T, undefined)
|
||||
})
|
||||
it('Should not validate bigint', () => {
|
||||
const T = Type.Never()
|
||||
Fail(T, BigInt(1))
|
||||
})
|
||||
it('Should not validate symbol', () => {
|
||||
const T = Type.Never()
|
||||
Fail(T, Symbol(1))
|
||||
})
|
||||
})
|
||||
45
test/runtime/compiler/not.ts
Normal file
45
test/runtime/compiler/not.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler/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 })
|
||||
})
|
||||
})
|
||||
41
test/runtime/compiler/null.ts
Normal file
41
test/runtime/compiler/null.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler/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)
|
||||
})
|
||||
it('Should not validate bigint', () => {
|
||||
const T = Type.Null()
|
||||
Fail(T, BigInt(1))
|
||||
})
|
||||
it('Should not validate symbol', () => {
|
||||
const T = Type.Null()
|
||||
Fail(T, Symbol(1))
|
||||
})
|
||||
})
|
||||
77
test/runtime/compiler/number.ts
Normal file
77
test/runtime/compiler/number.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler/Number', () => {
|
||||
it('Should validate number', () => {
|
||||
const T = Type.Number()
|
||||
Ok(T, 3.14)
|
||||
})
|
||||
it('Should not validate NaN', () => {
|
||||
const T = Type.Number()
|
||||
Fail(T, NaN)
|
||||
})
|
||||
it('Should not validate +Infinity', () => {
|
||||
const T = Type.Number()
|
||||
Fail(T, Infinity)
|
||||
})
|
||||
it('Should not validate -Infinity', () => {
|
||||
const T = Type.Number()
|
||||
Fail(T, -Infinity)
|
||||
})
|
||||
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 not validate bigint', () => {
|
||||
const T = Type.Number()
|
||||
Fail(T, BigInt(1))
|
||||
})
|
||||
it('Should not validate symbol', () => {
|
||||
const T = Type.Number()
|
||||
Fail(T, Symbol(1))
|
||||
})
|
||||
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)
|
||||
})
|
||||
})
|
||||
390
test/runtime/compiler/object.ts
Normal file
390
test/runtime/compiler/object.ts
Normal file
@@ -0,0 +1,390 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler/Object', () => {
|
||||
// -----------------------------------------------------
|
||||
// TypeCompiler Only
|
||||
// -----------------------------------------------------
|
||||
it('Should handle extends undefined check 1', () => {
|
||||
const T = Type.Object({
|
||||
A: Type.Not(Type.Number()),
|
||||
B: Type.Union([Type.Number(), Type.Undefined()]),
|
||||
C: Type.Intersect([Type.Undefined(), Type.Undefined()]),
|
||||
})
|
||||
Ok(T, {
|
||||
A: undefined,
|
||||
B: undefined,
|
||||
C: undefined,
|
||||
})
|
||||
})
|
||||
// https://github.com/sinclairzx81/typebox/issues/437
|
||||
it('Should handle extends undefined check 2', () => {
|
||||
const T = Type.Object({
|
||||
A: Type.Not(Type.Null()),
|
||||
})
|
||||
Ok(T, { A: undefined })
|
||||
Fail(T, { A: null })
|
||||
Fail(T, {})
|
||||
})
|
||||
// -----------------------------------------------------
|
||||
// Standard Checks
|
||||
// -----------------------------------------------------
|
||||
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 undefined', () => {
|
||||
const T = Type.Object({})
|
||||
Fail(T, undefined)
|
||||
})
|
||||
it('Should not validate bigint', () => {
|
||||
const T = Type.Object({})
|
||||
Fail(T, BigInt(1))
|
||||
})
|
||||
it('Should not validate symbol', () => {
|
||||
const T = Type.Object({})
|
||||
Fail(T, Symbol(1))
|
||||
})
|
||||
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),
|
||||
'node-mirror:release:0': Type.Literal(5), // issue: 353
|
||||
'node-mirror:release:1': Type.Optional(Type.Literal(6)), // issue: 356
|
||||
'node-mirror:release:2': Type.Union([Type.Literal(7), Type.Undefined()]), // key known
|
||||
"a'a": Type.Literal(8),
|
||||
'@onlyAtSymbol': Type.Literal(9),
|
||||
})
|
||||
Ok(T, {
|
||||
'with-hyphen': 1,
|
||||
'0-leading': 2,
|
||||
'$-leading': 3,
|
||||
'!@#$%^&*(': 4,
|
||||
'node-mirror:release:0': 5,
|
||||
'node-mirror:release:1': 6,
|
||||
'node-mirror:release:2': 7,
|
||||
"a'a": 8,
|
||||
'@onlyAtSymbol': 9,
|
||||
})
|
||||
})
|
||||
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,
|
||||
})
|
||||
})
|
||||
it('Should validate nested schema additional properties of string', () => {
|
||||
const T = Type.Object({
|
||||
nested: Type.Object(
|
||||
{
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
},
|
||||
{
|
||||
additionalProperties: Type.String(),
|
||||
},
|
||||
),
|
||||
})
|
||||
Ok(T, {
|
||||
nested: {
|
||||
x: 1,
|
||||
y: 2,
|
||||
z: 'hello',
|
||||
},
|
||||
})
|
||||
Fail(T, {
|
||||
nested: {
|
||||
x: 1,
|
||||
y: 2,
|
||||
z: 3,
|
||||
},
|
||||
})
|
||||
})
|
||||
it('Should validate nested schema additional properties of array', () => {
|
||||
const T = Type.Object({
|
||||
nested: Type.Object(
|
||||
{
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
},
|
||||
{
|
||||
additionalProperties: Type.Array(Type.Number()),
|
||||
},
|
||||
),
|
||||
})
|
||||
Ok(T, {
|
||||
nested: {
|
||||
x: 1,
|
||||
y: 2,
|
||||
z: [0, 1, 2],
|
||||
},
|
||||
})
|
||||
Fail(T, {
|
||||
nested: {
|
||||
x: 1,
|
||||
y: 2,
|
||||
z: 3,
|
||||
},
|
||||
})
|
||||
})
|
||||
it('Should validate nested schema additional properties of object', () => {
|
||||
const T = Type.Object({
|
||||
nested: Type.Object(
|
||||
{
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
},
|
||||
{
|
||||
additionalProperties: Type.Object({
|
||||
z: Type.Number(),
|
||||
}),
|
||||
},
|
||||
),
|
||||
})
|
||||
Ok(T, {
|
||||
nested: {
|
||||
x: 1,
|
||||
y: 2,
|
||||
z: { z: 1 },
|
||||
},
|
||||
})
|
||||
Fail(T, {
|
||||
nested: {
|
||||
x: 1,
|
||||
y: 2,
|
||||
z: 3,
|
||||
},
|
||||
})
|
||||
})
|
||||
it('Should check for property key if property type is undefined', () => {
|
||||
const T = Type.Object({ x: Type.Undefined() })
|
||||
Ok(T, { x: undefined })
|
||||
Fail(T, {})
|
||||
})
|
||||
it('Should check for property key if property type extends undefined', () => {
|
||||
const T = Type.Object({ x: Type.Union([Type.Number(), Type.Undefined()]) })
|
||||
Ok(T, { x: 1 })
|
||||
Ok(T, { x: undefined })
|
||||
Fail(T, {})
|
||||
})
|
||||
it('Should not check for property key if property type is undefined and optional', () => {
|
||||
const T = Type.Object({ x: Type.Optional(Type.Undefined()) })
|
||||
Ok(T, { x: undefined })
|
||||
Ok(T, {})
|
||||
})
|
||||
it('Should not check for property key if property type extends undefined and optional', () => {
|
||||
const T = Type.Object({ x: Type.Optional(Type.Union([Type.Number(), Type.Undefined()])) })
|
||||
Ok(T, { x: 1 })
|
||||
Ok(T, { x: undefined })
|
||||
Ok(T, {})
|
||||
})
|
||||
it('Should check undefined for optional property of number', () => {
|
||||
const T = Type.Object({ x: Type.Optional(Type.Number()) })
|
||||
Ok(T, { x: 1 })
|
||||
Ok(T, { x: undefined }) // allowed by default
|
||||
Ok(T, {})
|
||||
})
|
||||
it('Should check undefined for optional property of undefined', () => {
|
||||
const T = Type.Object({ x: Type.Optional(Type.Undefined()) })
|
||||
Fail(T, { x: 1 })
|
||||
Ok(T, { x: undefined })
|
||||
Ok(T, {})
|
||||
})
|
||||
it('Should check for required property of any', () => {
|
||||
const T = Type.Object({ x: Type.Any() })
|
||||
Fail(T, {})
|
||||
Ok(T, { x: undefined })
|
||||
Ok(T, { x: 1 })
|
||||
Ok(T, { x: true })
|
||||
})
|
||||
it('Should check for required property of unknown', () => {
|
||||
const T = Type.Object({ x: Type.Unknown() })
|
||||
Fail(T, {})
|
||||
Ok(T, { x: undefined })
|
||||
Ok(T, { x: 1 })
|
||||
Ok(T, { x: true })
|
||||
})
|
||||
it('Should check for required property of any (when optional)', () => {
|
||||
const T = Type.Object({ x: Type.Optional(Type.Any()) })
|
||||
Ok(T, {})
|
||||
Ok(T, { x: undefined })
|
||||
Ok(T, { x: 1 })
|
||||
Ok(T, { x: true })
|
||||
})
|
||||
it('Should check for required property of unknown (when optional)', () => {
|
||||
const T = Type.Object({ x: Type.Optional(Type.Unknown()) })
|
||||
Ok(T, {})
|
||||
Ok(T, { x: undefined })
|
||||
Ok(T, { x: 1 })
|
||||
Ok(T, { x: true })
|
||||
})
|
||||
})
|
||||
83
test/runtime/compiler/omit.ts
Normal file
83
test/runtime/compiler/omit.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import { Type, Kind } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
import { deepEqual, strictEqual } from 'assert'
|
||||
|
||||
describe('compiler/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 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 })
|
||||
})
|
||||
it('Should support Omit of Literal', () => {
|
||||
const A = Type.Object({
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
z: Type.Number(),
|
||||
})
|
||||
const T = Type.Omit(A, Type.Literal('x'), {
|
||||
additionalProperties: false,
|
||||
})
|
||||
Ok(T, { y: 1, z: 1 })
|
||||
Fail(T, { x: 1, y: 1, z: 1 })
|
||||
})
|
||||
it('Should support Omit of Never', () => {
|
||||
const A = Type.Object(
|
||||
{
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
z: Type.Number(),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
)
|
||||
const T = Type.Omit(A, Type.Never())
|
||||
Fail(T, { y: 1, z: 1 })
|
||||
Ok(T, { x: 1, y: 1, z: 1 })
|
||||
})
|
||||
})
|
||||
27
test/runtime/compiler/optional.ts
Normal file
27
test/runtime/compiler/optional.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { strictEqual } from 'assert'
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok } from './validate'
|
||||
|
||||
describe('compiler/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)
|
||||
})
|
||||
})
|
||||
53
test/runtime/compiler/partial.ts
Normal file
53
test/runtime/compiler/partial.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { TypeSystem } from '@sinclair/typebox/system'
|
||||
import { Type, OptionalKind, ReadonlyKind } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
import { strictEqual } from 'assert'
|
||||
|
||||
describe('compiler/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)
|
||||
strictEqual(T.properties.x[ReadonlyKind], 'Readonly')
|
||||
strictEqual(T.properties.x[OptionalKind], 'Optional')
|
||||
strictEqual(T.properties.y[ReadonlyKind], 'Readonly')
|
||||
strictEqual(T.properties.y[OptionalKind], 'Optional')
|
||||
strictEqual(T.properties.z[OptionalKind], 'Optional')
|
||||
strictEqual(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)
|
||||
strictEqual(A.additionalProperties, false)
|
||||
strictEqual(T.additionalProperties, false)
|
||||
})
|
||||
})
|
||||
82
test/runtime/compiler/pick.ts
Normal file
82
test/runtime/compiler/pick.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
import { strictEqual } from 'assert'
|
||||
|
||||
describe('compiler/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'])
|
||||
strictEqual(T.required!.includes('z'), false)
|
||||
})
|
||||
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'])
|
||||
strictEqual(A.additionalProperties, false)
|
||||
strictEqual(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 })
|
||||
})
|
||||
it('Should support Pick of Literal', () => {
|
||||
const A = Type.Object({
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
z: Type.Number(),
|
||||
})
|
||||
const T = Type.Pick(A, Type.Literal('x'), {
|
||||
additionalProperties: false,
|
||||
})
|
||||
Ok(T, { x: 1 })
|
||||
Fail(T, { x: 1, y: 1, z: 1 })
|
||||
})
|
||||
it('Should support Pick of Never', () => {
|
||||
const A = Type.Object({
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
z: Type.Number(),
|
||||
})
|
||||
const T = Type.Pick(A, Type.Never(), {
|
||||
additionalProperties: false,
|
||||
})
|
||||
Fail(T, { x: 1, y: 1, z: 1 })
|
||||
Ok(T, {})
|
||||
})
|
||||
})
|
||||
27
test/runtime/compiler/readonly-optional.ts
Normal file
27
test/runtime/compiler/readonly-optional.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
import { strictEqual } from 'assert'
|
||||
|
||||
describe('compiler/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 },
|
||||
)
|
||||
strictEqual(T.required!.includes('a'), false)
|
||||
})
|
||||
})
|
||||
27
test/runtime/compiler/readonly.ts
Normal file
27
test/runtime/compiler/readonly.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { deepStrictEqual, strictEqual } from 'assert'
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler/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 },
|
||||
)
|
||||
strictEqual(T.required!.includes('a'), true)
|
||||
strictEqual(T.required!.includes('b'), true)
|
||||
})
|
||||
})
|
||||
324
test/runtime/compiler/record.ts
Normal file
324
test/runtime/compiler/record.ts
Normal file
@@ -0,0 +1,324 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler/Record', () => {
|
||||
// -------------------------------------------------------------
|
||||
// Issues
|
||||
// -------------------------------------------------------------
|
||||
it('Issue: https://github.com/sinclairzx81/typebox/issues/402', () => {
|
||||
const T = Type.Object({
|
||||
foo: Type.Object({
|
||||
bar: Type.Record(Type.String(), Type.Number()),
|
||||
}),
|
||||
})
|
||||
Ok(T, { foo: { bar: { x: 42 } } })
|
||||
Ok(T, { foo: { bar: {} } })
|
||||
Fail(T, { foo: { bar: { x: '42' } } })
|
||||
Fail(T, { foo: { bar: [] } })
|
||||
Fail(T, { foo: {} })
|
||||
Fail(T, { foo: [] })
|
||||
Fail(T, {})
|
||||
})
|
||||
// -------------------------------------------------------------
|
||||
// TypeBox Only: Date and Record
|
||||
// -------------------------------------------------------------
|
||||
it('Should fail record with Date', () => {
|
||||
const T = Type.Record(Type.String(), Type.String())
|
||||
Fail(T, new Date())
|
||||
})
|
||||
it('Should fail record with Uint8Array', () => {
|
||||
const T = Type.Record(Type.String(), Type.String())
|
||||
Fail(T, new Uint8Array())
|
||||
})
|
||||
// -------------------------------------------------------------
|
||||
// Standard Assertions
|
||||
// -------------------------------------------------------------
|
||||
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/recursive.ts
Normal file
79
test/runtime/compiler/recursive.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Assert } from '../assert/index'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler/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.IsEqual(Node.$id === undefined, false)
|
||||
})
|
||||
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: [] },
|
||||
],
|
||||
},
|
||||
])
|
||||
})
|
||||
})
|
||||
89
test/runtime/compiler/ref.ts
Normal file
89
test/runtime/compiler/ref.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
import { Assert } from '../assert/index'
|
||||
|
||||
describe('compiler/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: Assert.NextId() },
|
||||
)
|
||||
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: Assert.NextId() },
|
||||
)
|
||||
const R = Type.Ref(T.$id!)
|
||||
Fail(
|
||||
R,
|
||||
{
|
||||
x: 1,
|
||||
y: 2,
|
||||
},
|
||||
[T],
|
||||
)
|
||||
})
|
||||
it('Should de-reference object property schema', () => {
|
||||
const T = Type.Object(
|
||||
{
|
||||
name: Type.String(),
|
||||
},
|
||||
{ $id: 'R' },
|
||||
)
|
||||
const R = Type.Object(
|
||||
{
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
z: Type.Number(),
|
||||
r: Type.Optional(Type.Ref(T.$id!)),
|
||||
},
|
||||
{ $id: 'T' },
|
||||
)
|
||||
Ok(R, { x: 1, y: 2, z: 3 }, [T])
|
||||
Ok(R, { x: 1, y: 2, z: 3, r: { name: 'hello' } }, [T])
|
||||
Fail(R, { x: 1, y: 2, z: 3, r: { name: 1 } }, [T])
|
||||
Fail(R, { x: 1, y: 2, z: 3, r: {} }, [T])
|
||||
})
|
||||
it('Should reference recursive schema', () => {
|
||||
const T = Type.Recursive((Node) =>
|
||||
Type.Object({
|
||||
id: Type.String(),
|
||||
nodes: Type.Array(Node),
|
||||
}),
|
||||
)
|
||||
const R = Type.Ref(T.$id!)
|
||||
Ok(R, { id: '', nodes: [{ id: '', nodes: [] }] }, [T])
|
||||
Fail(R, { id: '', nodes: [{ id: 1, nodes: [] }] }, [T])
|
||||
})
|
||||
})
|
||||
30
test/runtime/compiler/regexp.ts
Normal file
30
test/runtime/compiler/regexp.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler/RegExp', () => {
|
||||
it('Should validate regular expression 1', () => {
|
||||
const T = Type.RegExp(/foo/i)
|
||||
Ok(T, 'foo')
|
||||
Ok(T, 'Foo')
|
||||
Ok(T, 'fOO')
|
||||
Fail(T, 'bar')
|
||||
})
|
||||
it('Should validate regular expression 2', () => {
|
||||
const T = Type.RegExp(/<a?:.+?:\d{18}>|\p{Extended_Pictographic}/gu)
|
||||
Ok(T, '♥️♦️♠️♣️')
|
||||
})
|
||||
it('Should validate with minLength constraint', () => {
|
||||
const T = Type.RegExp(/(.*)/, {
|
||||
minLength: 3,
|
||||
})
|
||||
Ok(T, 'xxx')
|
||||
Fail(T, 'xx')
|
||||
})
|
||||
it('Should validate with maxLength constraint', () => {
|
||||
const T = Type.RegExp(/(.*)/, {
|
||||
maxLength: 3,
|
||||
})
|
||||
Ok(T, 'xxx')
|
||||
Fail(T, 'xxxx')
|
||||
})
|
||||
})
|
||||
55
test/runtime/compiler/required.ts
Normal file
55
test/runtime/compiler/required.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { Type, ReadonlyKind, OptionalKind } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
import { strictEqual } from 'assert'
|
||||
|
||||
describe('compiler/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)
|
||||
strictEqual(T.properties.x[ReadonlyKind], 'Readonly')
|
||||
strictEqual(T.properties.y[ReadonlyKind], 'Readonly')
|
||||
strictEqual(T.properties.z[OptionalKind], undefined)
|
||||
strictEqual(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)
|
||||
strictEqual(A.additionalPropeties, false)
|
||||
strictEqual(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/string-pattern.ts
Normal file
65
test/runtime/compiler/string-pattern.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler/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')
|
||||
})
|
||||
})
|
||||
71
test/runtime/compiler/string.ts
Normal file
71
test/runtime/compiler/string.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler/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 not validate bigint', () => {
|
||||
const T = Type.String()
|
||||
Fail(T, BigInt(1))
|
||||
})
|
||||
it('Should not validate symbol', () => {
|
||||
const T = Type.String()
|
||||
Fail(T, Symbol(1))
|
||||
})
|
||||
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')
|
||||
})
|
||||
it('Should should escape characters in the pattern', () => {
|
||||
const T = Type.String({ pattern: '/a/' })
|
||||
Ok(T, '/a/')
|
||||
})
|
||||
})
|
||||
42
test/runtime/compiler/symbol.ts
Normal file
42
test/runtime/compiler/symbol.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler/Symbol', () => {
|
||||
it('Should not validate a boolean', () => {
|
||||
const T = Type.Symbol()
|
||||
Fail(T, true)
|
||||
Fail(T, false)
|
||||
})
|
||||
it('Should not validate a number', () => {
|
||||
const T = Type.Symbol()
|
||||
Fail(T, 1)
|
||||
})
|
||||
it('Should not validate a string', () => {
|
||||
const T = Type.Symbol()
|
||||
Fail(T, 'true')
|
||||
})
|
||||
it('Should not validate an array', () => {
|
||||
const T = Type.Symbol()
|
||||
Fail(T, [true])
|
||||
})
|
||||
it('Should not validate an object', () => {
|
||||
const T = Type.Symbol()
|
||||
Fail(T, {})
|
||||
})
|
||||
it('Should not validate an null', () => {
|
||||
const T = Type.Symbol()
|
||||
Fail(T, null)
|
||||
})
|
||||
it('Should not validate an undefined', () => {
|
||||
const T = Type.Symbol()
|
||||
Fail(T, undefined)
|
||||
})
|
||||
it('Should not validate bigint', () => {
|
||||
const T = Type.Symbol()
|
||||
Fail(T, BigInt(1))
|
||||
})
|
||||
it('Should not validate symbol', () => {
|
||||
const T = Type.Symbol()
|
||||
Ok(T, Symbol(1))
|
||||
})
|
||||
})
|
||||
209
test/runtime/compiler/template-literal.ts
Normal file
209
test/runtime/compiler/template-literal.ts
Normal file
@@ -0,0 +1,209 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler/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')
|
||||
})
|
||||
it('Should validate enum (implicit)', () => {
|
||||
enum E {
|
||||
A,
|
||||
B,
|
||||
C,
|
||||
}
|
||||
const T = Type.TemplateLiteral([Type.Literal('hello'), Type.Enum(E)])
|
||||
Ok(T, 'hello0')
|
||||
Ok(T, 'hello1')
|
||||
Ok(T, 'hello2')
|
||||
Fail(T, 'hello3')
|
||||
})
|
||||
it('Should validate enum (explicit)', () => {
|
||||
enum E {
|
||||
A,
|
||||
B = 'B',
|
||||
C = 'C',
|
||||
}
|
||||
const T = Type.TemplateLiteral([Type.Literal('hello'), Type.Enum(E)])
|
||||
Ok(T, 'hello0')
|
||||
Ok(T, 'helloB')
|
||||
Ok(T, 'helloC')
|
||||
Fail(T, 'helloD')
|
||||
})
|
||||
})
|
||||
53
test/runtime/compiler/tuple.ts
Normal file
53
test/runtime/compiler/tuple.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler/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 }])
|
||||
})
|
||||
})
|
||||
47
test/runtime/compiler/uint8array.ts
Normal file
47
test/runtime/compiler/uint8array.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler/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))
|
||||
})
|
||||
})
|
||||
84
test/runtime/compiler/unicode.ts
Normal file
84
test/runtime/compiler/unicode.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok } from './validate'
|
||||
|
||||
describe('compiler/Unicode', () => {
|
||||
// ---------------------------------------------------------
|
||||
// Identifiers
|
||||
// ---------------------------------------------------------
|
||||
it('Should support unicode identifiers', () => {
|
||||
const T = Type.Object(
|
||||
{
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
},
|
||||
{
|
||||
$id: '식별자',
|
||||
},
|
||||
)
|
||||
Ok(T, {
|
||||
x: 1,
|
||||
y: 2,
|
||||
})
|
||||
})
|
||||
it('Should support unicode identifier references', () => {
|
||||
const R = Type.Object(
|
||||
{
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
},
|
||||
{
|
||||
$id: '식별자',
|
||||
},
|
||||
)
|
||||
const T = Type.Object({
|
||||
vector: Type.Ref(R.$id!),
|
||||
})
|
||||
Ok(
|
||||
T,
|
||||
{
|
||||
vector: {
|
||||
x: 1,
|
||||
y: 2,
|
||||
},
|
||||
},
|
||||
[R],
|
||||
)
|
||||
})
|
||||
it('Should support unicode identifier recursion', () => {
|
||||
const Node = Type.Recursive(
|
||||
(Node) =>
|
||||
Type.Object({
|
||||
id: Type.String(),
|
||||
nodes: Type.Array(Node),
|
||||
}),
|
||||
{
|
||||
$id: '식별자',
|
||||
},
|
||||
)
|
||||
Ok(Node, {
|
||||
id: 'A',
|
||||
nodes: [
|
||||
{
|
||||
id: 'B',
|
||||
nodes: [
|
||||
{
|
||||
id: 'C',
|
||||
nodes: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
// ---------------------------------------------------------
|
||||
// Properties
|
||||
// ---------------------------------------------------------
|
||||
it('Should support unicode properties', () => {
|
||||
const T = Type.Object({
|
||||
이름: Type.String(),
|
||||
})
|
||||
Ok(T, {
|
||||
이름: 'dave',
|
||||
})
|
||||
})
|
||||
})
|
||||
56
test/runtime/compiler/union.ts
Normal file
56
test/runtime/compiler/union.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler/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/unknown.ts
Normal file
33
test/runtime/compiler/unknown.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok } from './validate'
|
||||
|
||||
describe('compiler/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)
|
||||
})
|
||||
})
|
||||
119
test/runtime/compiler/validate.ts
Normal file
119
test/runtime/compiler/validate.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
import { TypeCompiler } from '@sinclair/typebox/compiler'
|
||||
import { Value } from '@sinclair/typebox/value'
|
||||
import { TSchema, FormatRegistry } from '@sinclair/typebox'
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Test Formats: https://github.com/ajv-validator/ajv-formats/blob/master/src/formats.ts
|
||||
//
|
||||
// - date-time
|
||||
// - email
|
||||
// - uuid
|
||||
//
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
const EMAIL = /^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i
|
||||
const UUID = /^(?:urn:uuid:)?[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/i
|
||||
const DATE_TIME_SEPARATOR = /t|\s/i
|
||||
const TIME = /^(\d\d):(\d\d):(\d\d(?:\.\d+)?)(z|([+-])(\d\d)(?::?(\d\d))?)?$/i
|
||||
const DATE = /^(\d\d\d\d)-(\d\d)-(\d\d)$/
|
||||
const DAYS = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
|
||||
|
||||
function isLeapYear(year: number): boolean {
|
||||
return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0)
|
||||
}
|
||||
function isDate(str: string): boolean {
|
||||
const matches: string[] | null = DATE.exec(str)
|
||||
if (!matches) return false
|
||||
const year: number = +matches[1]
|
||||
const month: number = +matches[2]
|
||||
const day: number = +matches[3]
|
||||
return month >= 1 && month <= 12 && day >= 1 && day <= (month === 2 && isLeapYear(year) ? 29 : DAYS[month])
|
||||
}
|
||||
function isTime(str: string, strictTimeZone?: boolean): boolean {
|
||||
const matches: string[] | null = TIME.exec(str)
|
||||
if (!matches) return false
|
||||
const hr: number = +matches[1]
|
||||
const min: number = +matches[2]
|
||||
const sec: number = +matches[3]
|
||||
const tz: string | undefined = matches[4]
|
||||
const tzSign: number = matches[5] === '-' ? -1 : 1
|
||||
const tzH: number = +(matches[6] || 0)
|
||||
const tzM: number = +(matches[7] || 0)
|
||||
if (tzH > 23 || tzM > 59 || (strictTimeZone && !tz)) return false
|
||||
if (hr <= 23 && min <= 59 && sec < 60) return true
|
||||
// leap second
|
||||
const utcMin = min - tzM * tzSign
|
||||
const utcHr = hr - tzH * tzSign - (utcMin < 0 ? 1 : 0)
|
||||
return (utcHr === 23 || utcHr === -1) && (utcMin === 59 || utcMin === -1) && sec < 61
|
||||
}
|
||||
function isDateTime(str: string, strictTimeZone?: boolean): boolean {
|
||||
const dateTime: string[] = str.split(DATE_TIME_SEPARATOR)
|
||||
return dateTime.length === 2 && isDate(dateTime[0]) && isTime(dateTime[1], strictTimeZone)
|
||||
}
|
||||
// -------------------------------------------------------------------------
|
||||
// Use Formats
|
||||
// -------------------------------------------------------------------------
|
||||
FormatRegistry.Set('email', (value) => EMAIL.test(value))
|
||||
FormatRegistry.Set('uuid', (value) => UUID.test(value))
|
||||
FormatRegistry.Set('date-time', (value) => isDateTime(value, true))
|
||||
export function Ok<T extends TSchema>(schema: T, data: unknown, references: any[] = []) {
|
||||
const C = TypeCompiler.Compile(schema, references)
|
||||
const result = C.Check(data)
|
||||
|
||||
if (result !== Value.Check(schema, references, data)) {
|
||||
throw Error('Compiler and Value Check disparity')
|
||||
}
|
||||
if (result === false) {
|
||||
const errors = [...Value.Errors(schema, references, data)]
|
||||
if (errors.length === 0) throw Error('expected at least 1 error')
|
||||
}
|
||||
if (result === true) {
|
||||
const errors = [...Value.Errors(schema, references, data)]
|
||||
if (errors.length > 0) throw Error('expected no errors')
|
||||
}
|
||||
if (!result) {
|
||||
console.log('---------------------------')
|
||||
console.log('type')
|
||||
console.log('---------------------------')
|
||||
console.log(JSON.stringify(schema, 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(result)
|
||||
throw Error('expected ok')
|
||||
}
|
||||
}
|
||||
export function Fail<T extends TSchema>(schema: T, data: unknown, references: any[] = []) {
|
||||
const C = TypeCompiler.Compile(schema, references)
|
||||
const result = C.Check(data)
|
||||
if (result !== Value.Check(schema, references, data)) {
|
||||
throw Error('Compiler and Value Check disparity')
|
||||
}
|
||||
if (result === false) {
|
||||
const errors = [...Value.Errors(schema, references, data)]
|
||||
if (errors.length === 0) throw Error('expected at least 1 error')
|
||||
}
|
||||
if (result === true) {
|
||||
const errors = [...Value.Errors(schema, references, data)]
|
||||
if (errors.length > 0) throw Error('expected no errors')
|
||||
}
|
||||
if (result) {
|
||||
console.log('---------------------------')
|
||||
console.log('type')
|
||||
console.log('---------------------------')
|
||||
console.log(JSON.stringify(schema, 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('none')
|
||||
throw Error('expected ok')
|
||||
}
|
||||
}
|
||||
37
test/runtime/compiler/void.ts
Normal file
37
test/runtime/compiler/void.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler/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.Void()
|
||||
Fail(T, null)
|
||||
})
|
||||
it('Should validate undefined', () => {
|
||||
const T = Type.Void()
|
||||
Ok(T, undefined)
|
||||
})
|
||||
it('Should validate void 0', () => {
|
||||
const T = Type.Void()
|
||||
Ok(T, void 0)
|
||||
})
|
||||
})
|
||||
2
test/runtime/errors/index.ts
Normal file
2
test/runtime/errors/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
import './iterator/index'
|
||||
import './types/index'
|
||||
1
test/runtime/errors/iterator/index.ts
Normal file
1
test/runtime/errors/iterator/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
import './iterator'
|
||||
32
test/runtime/errors/iterator/iterator.ts
Normal file
32
test/runtime/errors/iterator/iterator.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Errors } from '@sinclair/typebox/errors'
|
||||
import { Assert } from '../../assert'
|
||||
|
||||
describe('errors/ValueErrorIterator', () => {
|
||||
it('Should return undefined for non error', () => {
|
||||
const R = Errors(Type.Number(), [], 1).First()
|
||||
Assert.IsEqual(R, undefined)
|
||||
})
|
||||
it('Should return a value error when error', () => {
|
||||
const { type, path, message } = Errors(Type.Number(), [], '').First()!
|
||||
Assert.IsTypeOf(type, 'number')
|
||||
Assert.IsTypeOf(path, 'string')
|
||||
Assert.IsTypeOf(message, 'string')
|
||||
})
|
||||
it('Should yield empty array for non error', () => {
|
||||
const R = [...Errors(Type.Number(), [], 1)]
|
||||
Assert.IsEqual(R.length, 0)
|
||||
})
|
||||
it('Should yield array with 1 error when error', () => {
|
||||
const R = [...Errors(Type.Number(), [], 'foo')]
|
||||
Assert.IsEqual(R.length, 1)
|
||||
})
|
||||
it('Should yield array with N errors when error', () => {
|
||||
// prettier-ignore
|
||||
const R = [...Errors(Type.Object({
|
||||
x: Type.Number(),
|
||||
y: Type.Number()
|
||||
}), [], {})] // require object to invoke internal check
|
||||
Assert.IsEqual(R.length > 1, true)
|
||||
})
|
||||
})
|
||||
17
test/runtime/errors/types/array-contains.ts
Normal file
17
test/runtime/errors/types/array-contains.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { ValueErrorType } from '@sinclair/typebox/errors'
|
||||
import { Resolve } from './resolve'
|
||||
import { Assert } from '../../assert'
|
||||
|
||||
describe('errors/type/ArrayMaxContainsItems', () => {
|
||||
const T = Type.Array(Type.Any(), { contains: Type.Literal(1) })
|
||||
it('Should pass 0', () => {
|
||||
const R = Resolve(T, [1])
|
||||
Assert.IsEqual(R.length, 0)
|
||||
})
|
||||
it('Should pass 1', () => {
|
||||
const R = Resolve(T, [2])
|
||||
Assert.IsEqual(R.length, 1)
|
||||
Assert.IsEqual(R[0].type, ValueErrorType.ArrayContains)
|
||||
})
|
||||
})
|
||||
36
test/runtime/errors/types/array-max-contains.ts
Normal file
36
test/runtime/errors/types/array-max-contains.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { ValueErrorType } from '@sinclair/typebox/errors'
|
||||
import { Resolve } from './resolve'
|
||||
import { Assert } from '../../assert'
|
||||
|
||||
describe('errors/type/ArrayMaxContainsItems', () => {
|
||||
const T = Type.Array(Type.Any(), { contains: Type.Literal(1), maxContains: 4 })
|
||||
it('Should pass 0', () => {
|
||||
const R = Resolve(T, [1, 1, 1, 1])
|
||||
Assert.IsEqual(R.length, 0)
|
||||
})
|
||||
it('Should pass 1', () => {
|
||||
const R = Resolve(T, null)
|
||||
Assert.IsEqual(R.length, 1)
|
||||
Assert.IsEqual(R[0].type, ValueErrorType.Array)
|
||||
})
|
||||
it('Should pass 2', () => {
|
||||
const R = Resolve(T, [1, 1, 1, 1, 1])
|
||||
Assert.IsEqual(R.length, 1)
|
||||
Assert.IsEqual(R[0].type, ValueErrorType.ArrayMaxContains)
|
||||
})
|
||||
it('Should pass 3', () => {
|
||||
const R = Resolve(T, [])
|
||||
Assert.IsEqual(R.length, 1)
|
||||
Assert.IsEqual(R[0].type, ValueErrorType.ArrayContains)
|
||||
})
|
||||
it('Should pass 4', () => {
|
||||
const R = Resolve(T, [1, 2, 3, 4])
|
||||
Assert.IsEqual(R.length, 0)
|
||||
})
|
||||
it('Should pass 5', () => {
|
||||
const R = Resolve(T, [2, 3, 4])
|
||||
Assert.IsEqual(R.length, 1)
|
||||
Assert.IsEqual(R[0].type, ValueErrorType.ArrayContains)
|
||||
})
|
||||
})
|
||||
22
test/runtime/errors/types/array-max-items.ts
Normal file
22
test/runtime/errors/types/array-max-items.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { ValueErrorType } from '@sinclair/typebox/errors'
|
||||
import { Resolve } from './resolve'
|
||||
import { Assert } from '../../assert'
|
||||
|
||||
describe('errors/type/ArrayMaxItems', () => {
|
||||
const T = Type.Array(Type.Any(), { maxItems: 4 })
|
||||
it('Should pass 0', () => {
|
||||
const R = Resolve(T, [1, 2, 3, 4])
|
||||
Assert.IsEqual(R.length, 0)
|
||||
})
|
||||
it('Should pass 1', () => {
|
||||
const R = Resolve(T, null)
|
||||
Assert.IsEqual(R.length, 1)
|
||||
Assert.IsEqual(R[0].type, ValueErrorType.Array)
|
||||
})
|
||||
it('Should pass 2', () => {
|
||||
const R = Resolve(T, [1, 2, 3, 4, 5])
|
||||
Assert.IsEqual(R.length, 1)
|
||||
Assert.IsEqual(R[0].type, ValueErrorType.ArrayMaxItems)
|
||||
})
|
||||
})
|
||||
28
test/runtime/errors/types/array-min-contains.ts
Normal file
28
test/runtime/errors/types/array-min-contains.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { ValueErrorType } from '@sinclair/typebox/errors'
|
||||
import { Resolve } from './resolve'
|
||||
import { Assert } from '../../assert'
|
||||
|
||||
describe('errors/type/ArrayMinContainsItems', () => {
|
||||
const T = Type.Array(Type.Any(), { contains: Type.Literal(1), minContains: 4 })
|
||||
it('Should pass 0', () => {
|
||||
const R = Resolve(T, [1, 1, 1, 1])
|
||||
Assert.IsEqual(R.length, 0)
|
||||
})
|
||||
it('Should pass 1', () => {
|
||||
const R = Resolve(T, null)
|
||||
Assert.IsEqual(R.length, 1)
|
||||
Assert.IsEqual(R[0].type, ValueErrorType.Array)
|
||||
})
|
||||
it('Should pass 2', () => {
|
||||
const R = Resolve(T, [1])
|
||||
Assert.IsEqual(R.length, 1)
|
||||
Assert.IsEqual(R[0].type, ValueErrorType.ArrayMinContains)
|
||||
})
|
||||
it('Should pass 3', () => {
|
||||
const R = Resolve(T, [])
|
||||
Assert.IsEqual(R.length, 2)
|
||||
Assert.IsEqual(R[0].type, ValueErrorType.ArrayContains)
|
||||
Assert.IsEqual(R[1].type, ValueErrorType.ArrayMinContains)
|
||||
})
|
||||
})
|
||||
22
test/runtime/errors/types/array-min-items.ts
Normal file
22
test/runtime/errors/types/array-min-items.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { ValueErrorType } from '@sinclair/typebox/errors'
|
||||
import { Resolve } from './resolve'
|
||||
import { Assert } from '../../assert'
|
||||
|
||||
describe('errors/type/ArrayMinItems', () => {
|
||||
const T = Type.Array(Type.Any(), { minItems: 4 })
|
||||
it('Should pass 0', () => {
|
||||
const R = Resolve(T, [1, 2, 3, 4])
|
||||
Assert.IsEqual(R.length, 0)
|
||||
})
|
||||
it('Should pass 1', () => {
|
||||
const R = Resolve(T, null)
|
||||
Assert.IsEqual(R.length, 1)
|
||||
Assert.IsEqual(R[0].type, ValueErrorType.Array)
|
||||
})
|
||||
it('Should pass 2', () => {
|
||||
const R = Resolve(T, [])
|
||||
Assert.IsEqual(R.length, 1)
|
||||
Assert.IsEqual(R[0].type, ValueErrorType.ArrayMinItems)
|
||||
})
|
||||
})
|
||||
21
test/runtime/errors/types/array-unique-items.ts
Normal file
21
test/runtime/errors/types/array-unique-items.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { ValueErrorType } from '@sinclair/typebox/errors'
|
||||
import { Resolve } from './resolve'
|
||||
import { Assert } from '../../assert'
|
||||
|
||||
describe('errors/type/ArrayUniqueItems', () => {
|
||||
const T = Type.Array(Type.Any(), { uniqueItems: true })
|
||||
it('Should pass 0', () => {
|
||||
const R = Resolve(T, [1, 2, 3, 4])
|
||||
Assert.IsEqual(R.length, 0)
|
||||
})
|
||||
it('Should pass 2', () => {
|
||||
const R = Resolve(T, [])
|
||||
Assert.IsEqual(R.length, 0)
|
||||
})
|
||||
it('Should pass 3', () => {
|
||||
const R = Resolve(T, [1, 1, 3, 4])
|
||||
Assert.IsEqual(R.length, 1)
|
||||
Assert.IsEqual(R[0].type, ValueErrorType.ArrayUniqueItems)
|
||||
})
|
||||
})
|
||||
27
test/runtime/errors/types/array.ts
Normal file
27
test/runtime/errors/types/array.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { ValueErrorType } from '@sinclair/typebox/errors'
|
||||
import { Resolve } from './resolve'
|
||||
import { Assert } from '../../assert'
|
||||
|
||||
describe('errors/type/Array', () => {
|
||||
const T = Type.Array(Type.Any())
|
||||
it('Should pass 0', () => {
|
||||
const R = Resolve(T, [1, 2, 3])
|
||||
Assert.IsEqual(R.length, 0)
|
||||
})
|
||||
it('Should pass 1', () => {
|
||||
const R = Resolve(T, 1)
|
||||
Assert.IsEqual(R.length, 1)
|
||||
Assert.IsEqual(R[0].type, ValueErrorType.Array)
|
||||
})
|
||||
it('Should pass 2', () => {
|
||||
const R = Resolve(T, {})
|
||||
Assert.IsEqual(R.length, 1)
|
||||
Assert.IsEqual(R[0].type, ValueErrorType.Array)
|
||||
})
|
||||
it('Should pass 3', () => {
|
||||
const R = Resolve(T, null)
|
||||
Assert.IsEqual(R.length, 1)
|
||||
Assert.IsEqual(R[0].type, ValueErrorType.Array)
|
||||
})
|
||||
})
|
||||
17
test/runtime/errors/types/async-iterator.ts
Normal file
17
test/runtime/errors/types/async-iterator.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { ValueErrorType } from '@sinclair/typebox/errors'
|
||||
import { Resolve } from './resolve'
|
||||
import { Assert } from '../../assert'
|
||||
|
||||
describe('errors/type/AsyncIterator', () => {
|
||||
const T = Type.AsyncIterator(Type.Any())
|
||||
it('Should pass 0', () => {
|
||||
const R = Resolve(T, (async function* (): any {})())
|
||||
Assert.IsEqual(R.length, 0)
|
||||
})
|
||||
it('Should pass 1', () => {
|
||||
const R = Resolve(T, 1)
|
||||
Assert.IsEqual(R.length, 1)
|
||||
Assert.IsEqual(R[0].type, ValueErrorType.AsyncIterator)
|
||||
})
|
||||
})
|
||||
17
test/runtime/errors/types/bigint-exclusive-maximum.ts
Normal file
17
test/runtime/errors/types/bigint-exclusive-maximum.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { ValueErrorType } from '@sinclair/typebox/errors'
|
||||
import { Resolve } from './resolve'
|
||||
import { Assert } from '../../assert'
|
||||
|
||||
describe('errors/type/BigIntExclusiveMaximum', () => {
|
||||
const T = Type.BigInt({ exclusiveMaximum: 4n })
|
||||
it('Should pass 0', () => {
|
||||
const R = Resolve(T, 0n)
|
||||
Assert.IsEqual(R.length, 0)
|
||||
})
|
||||
it('Should pass 1', () => {
|
||||
const R = Resolve(T, 4n)
|
||||
Assert.IsEqual(R.length, 1)
|
||||
Assert.IsEqual(R[0].type, ValueErrorType.BigIntExclusiveMaximum)
|
||||
})
|
||||
})
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user