724 lines
22 KiB
TypeScript
724 lines
22 KiB
TypeScript
import { assert, describe, it } from "@effect/vitest"
|
|
import { type JsonSchema, Schema } from "effect"
|
|
import { TestSchema } from "effect/testing"
|
|
import { toCodecOpenAI } from "effect/unstable/ai/OpenAiStructuredOutput"
|
|
import * as Tool from "effect/unstable/ai/Tool"
|
|
|
|
function assertJsonSchema(schema: Schema.Constraint, expected: JsonSchema.JsonSchema) {
|
|
assert.deepStrictEqual(toCodecOpenAI(schema).jsonSchema, expected)
|
|
}
|
|
|
|
function assertError(schema: Schema.Constraint, message: string) {
|
|
assert.throws(() => toCodecOpenAI(schema), message)
|
|
}
|
|
|
|
describe("toCodecOpenAI", () => {
|
|
describe("Unsupported", () => {
|
|
it("Undefined", () => {
|
|
assertError(Schema.Undefined, "Unsupported AST Undefined")
|
|
})
|
|
|
|
it("Literal with unsupported type", () => {
|
|
assertError(Schema.Literal(1n), "Unsupported literal type bigint")
|
|
})
|
|
|
|
describe("Arrays", () => {
|
|
it("post-rest elements", () => {
|
|
assertError(
|
|
Schema.TupleWithRest(Schema.Tuple([]), [Schema.String, Schema.String]),
|
|
"Post-rest elements are not supported for arrays"
|
|
)
|
|
})
|
|
})
|
|
|
|
describe("Objects", () => {
|
|
it("non-string property signature name", () => {
|
|
assertError(
|
|
Schema.Struct({ [Symbol.for("effect/Schema/test/a")]: Schema.String }),
|
|
"Property names must be strings"
|
|
)
|
|
})
|
|
})
|
|
})
|
|
|
|
describe("Suspend", () => {
|
|
it("no-transformation recursive schema", () => {
|
|
interface A {
|
|
readonly a: string
|
|
readonly as: ReadonlyArray<A>
|
|
}
|
|
const schema = Schema.Struct({
|
|
a: Schema.String.check(Schema.isStartsWith("a")),
|
|
as: Schema.Array(Schema.suspend((): Schema.Codec<A> => schema))
|
|
})
|
|
assertJsonSchema(schema, {
|
|
"type": "object",
|
|
"properties": {
|
|
"a": {
|
|
"type": "string",
|
|
"description": "a string starting with \"a\"",
|
|
"pattern": "^a"
|
|
},
|
|
"as": {
|
|
"type": "array",
|
|
"items": { "$ref": "#/$defs/Suspend_" }
|
|
}
|
|
},
|
|
"required": ["a", "as"],
|
|
"additionalProperties": false,
|
|
"$defs": {
|
|
"Suspend_": {
|
|
"type": "object",
|
|
"properties": {
|
|
"a": {
|
|
"type": "string",
|
|
"description": "a string starting with \"a\"",
|
|
"pattern": "^a"
|
|
},
|
|
"as": {
|
|
"type": "array",
|
|
"items": { "$ref": "#/$defs/Suspend_" }
|
|
}
|
|
},
|
|
"required": ["a", "as"],
|
|
"additionalProperties": false
|
|
}
|
|
}
|
|
})
|
|
})
|
|
|
|
it("transformation recursive schema", () => {
|
|
interface A {
|
|
readonly a: string
|
|
readonly as: readonly [A]
|
|
}
|
|
const schema = Schema.Struct({
|
|
a: Schema.String.check(Schema.isStartsWith("a")),
|
|
as: Schema.Tuple([Schema.suspend((): Schema.Codec<A> => schema)])
|
|
})
|
|
assertJsonSchema(schema, {
|
|
"type": "object",
|
|
"properties": {
|
|
"a": {
|
|
"type": "string",
|
|
"description": "a string starting with \"a\"",
|
|
"pattern": "^a"
|
|
},
|
|
"as": {
|
|
"type": "object",
|
|
"description":
|
|
"Tuple encoded as an object with numeric string keys ('0', '1', ...). If present, '__rest__' contains remaining elements",
|
|
"properties": {
|
|
"0": {
|
|
"$ref": "#/$defs/Suspend_"
|
|
}
|
|
},
|
|
"required": ["0"],
|
|
"additionalProperties": false
|
|
}
|
|
},
|
|
"required": ["a", "as"],
|
|
"additionalProperties": false,
|
|
"$defs": {
|
|
"Suspend_": {
|
|
"type": "object",
|
|
"properties": {
|
|
"a": {
|
|
"type": "string",
|
|
"description": "a string starting with \"a\"",
|
|
"pattern": "^a"
|
|
},
|
|
"as": {
|
|
"type": "object",
|
|
"description":
|
|
"Tuple encoded as an object with numeric string keys ('0', '1', ...). If present, '__rest__' contains remaining elements",
|
|
"properties": {
|
|
"0": {
|
|
"$ref": "#/$defs/Suspend_"
|
|
}
|
|
},
|
|
"required": ["0"],
|
|
"additionalProperties": false
|
|
}
|
|
},
|
|
"required": ["a", "as"],
|
|
"additionalProperties": false
|
|
}
|
|
}
|
|
})
|
|
})
|
|
})
|
|
|
|
it("Null", () => {
|
|
assertJsonSchema(Schema.Null, {
|
|
"type": "null"
|
|
})
|
|
})
|
|
|
|
describe("String", () => {
|
|
it("String", () => {
|
|
assertJsonSchema(Schema.String, {
|
|
"type": "string"
|
|
})
|
|
assertJsonSchema(Schema.String.annotate({ description: "description" }), {
|
|
"type": "string",
|
|
"description": "description"
|
|
})
|
|
})
|
|
|
|
it("String + supported format", () => {
|
|
assertJsonSchema(Schema.String.annotate({ format: "date-time" }), {
|
|
"type": "string",
|
|
"format": "date-time"
|
|
})
|
|
})
|
|
|
|
it("String + unsupported format", () => {
|
|
assertJsonSchema(Schema.String.annotate({ format: "int32" }), {
|
|
"type": "string",
|
|
"description": "a value with a format of int32"
|
|
})
|
|
})
|
|
|
|
it("String + isMinLength", () => {
|
|
assertJsonSchema(Schema.String.check(Schema.isMinLength(1)), {
|
|
"type": "string",
|
|
"description": "a value with a length of at least 1"
|
|
})
|
|
})
|
|
|
|
it("String + isMinLength + isMaxLength", () => {
|
|
assertJsonSchema(Schema.String.check(Schema.isMinLength(1), Schema.isMaxLength(2)), {
|
|
"type": "string",
|
|
"description": "a value with a length of at least 1 and a value with a length of at most 2"
|
|
})
|
|
})
|
|
|
|
it("String + startsWith", () => {
|
|
assertJsonSchema(Schema.String.check(Schema.isStartsWith("a")), {
|
|
"type": "string",
|
|
"description": `a string starting with "a"`,
|
|
"pattern": "^a"
|
|
})
|
|
})
|
|
|
|
it("String + startsWith + endsWith", () => {
|
|
assertJsonSchema(Schema.String.check(Schema.isStartsWith("a"), Schema.isEndsWith("b")), {
|
|
"type": "string",
|
|
"description": `a string starting with "a" and a string ending with "b"`,
|
|
"pattern": "^(?=[\\s\\S]*?(?:^a))(?=[\\s\\S]*?(?:b$))"
|
|
})
|
|
})
|
|
|
|
it("String + pattern", () => {
|
|
assertJsonSchema(Schema.String.check(Schema.isPattern(/^a/)), {
|
|
"type": "string",
|
|
"description": `a string matching the RegExp ^a`,
|
|
"pattern": "^a"
|
|
})
|
|
})
|
|
})
|
|
|
|
describe("Number", () => {
|
|
it("Number", () => {
|
|
assertJsonSchema(Schema.Number, {
|
|
"anyOf": [
|
|
{ "type": "number" },
|
|
{ "type": "string", "enum": ["NaN"] },
|
|
{ "type": "string", "enum": ["Infinity"] },
|
|
{ "type": "string", "enum": ["-Infinity"] }
|
|
]
|
|
})
|
|
})
|
|
|
|
describe("Finite", () => {
|
|
it("Finite", () => {
|
|
assertJsonSchema(Schema.Finite, {
|
|
"type": "number"
|
|
})
|
|
assertJsonSchema(Schema.Finite.annotate({ description: "description" }), {
|
|
"type": "number",
|
|
"description": "description"
|
|
})
|
|
})
|
|
|
|
it("Finite + supported format", () => {
|
|
assertJsonSchema(Schema.Finite.annotate({ format: "duration" }), {
|
|
"type": "number",
|
|
"format": "duration"
|
|
})
|
|
})
|
|
|
|
it("Finite + unsupported format", () => {
|
|
assertJsonSchema(Schema.Finite.annotate({ format: "int32" }), {
|
|
"type": "number",
|
|
"description": "a value with a format of int32"
|
|
})
|
|
})
|
|
|
|
it("Finite + isGreaterThan", () => {
|
|
assertJsonSchema(Schema.Finite.check(Schema.isGreaterThan(1)), {
|
|
"type": "number",
|
|
"description": "a value greater than 1",
|
|
"exclusiveMinimum": 1
|
|
})
|
|
})
|
|
|
|
it("Finite + isGreaterThanOrEqualTo", () => {
|
|
assertJsonSchema(Schema.Finite.check(Schema.isGreaterThanOrEqualTo(1)), {
|
|
"type": "number",
|
|
"description": "a value greater than or equal to 1",
|
|
"minimum": 1
|
|
})
|
|
})
|
|
|
|
it("Finite + isLessThan", () => {
|
|
assertJsonSchema(Schema.Finite.check(Schema.isLessThan(2)), {
|
|
"type": "number",
|
|
"description": "a value less than 2",
|
|
"exclusiveMaximum": 2
|
|
})
|
|
})
|
|
|
|
it("Finite + isLessThanOrEqualTo", () => {
|
|
assertJsonSchema(Schema.Finite.check(Schema.isLessThanOrEqualTo(2)), {
|
|
"type": "number",
|
|
"description": "a value less than or equal to 2",
|
|
"maximum": 2
|
|
})
|
|
})
|
|
|
|
it("Finite + isBetween", () => {
|
|
assertJsonSchema(Schema.Finite.check(Schema.isBetween({ minimum: 1, maximum: 2 })), {
|
|
"type": "number",
|
|
"description": "a value between 1 and 2",
|
|
"minimum": 1,
|
|
"maximum": 2
|
|
})
|
|
})
|
|
|
|
it("Finite + isMultipleOf", () => {
|
|
assertJsonSchema(Schema.Finite.check(Schema.isMultipleOf(2)), {
|
|
"type": "number",
|
|
"description": "a value that is a multiple of 2",
|
|
"multipleOf": 2
|
|
})
|
|
})
|
|
|
|
it("Finite + isGreaterThan + isLessThan", () => {
|
|
assertJsonSchema(Schema.Finite.check(Schema.isGreaterThan(1), Schema.isLessThan(2)), {
|
|
"type": "number",
|
|
"description": "a value greater than 1 and a value less than 2",
|
|
"exclusiveMinimum": 1,
|
|
"exclusiveMaximum": 2
|
|
})
|
|
})
|
|
})
|
|
|
|
describe("Int", () => {
|
|
it("Int", () => {
|
|
assertJsonSchema(Schema.Int, {
|
|
"type": "integer"
|
|
})
|
|
assertJsonSchema(Schema.Int.annotate({ description: "description" }), {
|
|
"type": "integer",
|
|
"description": "description"
|
|
})
|
|
})
|
|
|
|
it("Int + supported format", () => {
|
|
assertJsonSchema(Schema.Int.annotate({ format: "duration" }), {
|
|
"type": "integer",
|
|
"format": "duration"
|
|
})
|
|
})
|
|
|
|
it("Int + unsupported format", () => {
|
|
assertJsonSchema(Schema.Int.annotate({ format: "int32" }), {
|
|
"type": "integer",
|
|
"description": "a value with a format of int32"
|
|
})
|
|
})
|
|
|
|
it("Int + isGreaterThan", () => {
|
|
assertJsonSchema(Schema.Int.check(Schema.isGreaterThan(1)), {
|
|
"type": "integer",
|
|
"description": "a value greater than 1",
|
|
"exclusiveMinimum": 1
|
|
})
|
|
})
|
|
|
|
it("Int + isGreaterThan + isLessThan", () => {
|
|
assertJsonSchema(Schema.Int.check(Schema.isGreaterThan(1), Schema.isLessThan(2)), {
|
|
"type": "integer",
|
|
"description": "a value greater than 1 and a value less than 2",
|
|
"exclusiveMinimum": 1,
|
|
"exclusiveMaximum": 2
|
|
})
|
|
})
|
|
})
|
|
})
|
|
|
|
describe("Literal", () => {
|
|
it("Literal(string)", () => {
|
|
assertJsonSchema(Schema.Literal("a"), {
|
|
"type": "string",
|
|
"enum": ["a"]
|
|
})
|
|
})
|
|
|
|
it("Literal(number)", () => {
|
|
assertJsonSchema(Schema.Literal(1), {
|
|
"type": "number",
|
|
"enum": [1]
|
|
})
|
|
})
|
|
|
|
it("Literal(boolean)", () => {
|
|
assertJsonSchema(Schema.Literal(true), {
|
|
"type": "boolean",
|
|
"enum": [true]
|
|
})
|
|
})
|
|
})
|
|
|
|
describe("Union", () => {
|
|
it("oneOf", () => {
|
|
assertJsonSchema(Schema.Union([Schema.String, Schema.Finite], { mode: "oneOf" }), {
|
|
"anyOf": [
|
|
{ "type": "string" },
|
|
{ "type": "number" }
|
|
]
|
|
})
|
|
})
|
|
|
|
it("Union(NonEmptyString, Int)", () => {
|
|
assertJsonSchema(Schema.Union([Schema.NonEmptyString, Schema.Int]), {
|
|
"anyOf": [
|
|
{ "type": "string", "description": "a value with a length of at least 1" },
|
|
{ "type": "integer" }
|
|
]
|
|
})
|
|
})
|
|
})
|
|
|
|
describe("Array", () => {
|
|
it("Array(String)", () => {
|
|
assertJsonSchema(Schema.Array(Schema.String), {
|
|
"type": "array",
|
|
"items": { "type": "string" }
|
|
})
|
|
assertJsonSchema(Schema.Array(Schema.String).annotate({ description: "description" }), {
|
|
"type": "array",
|
|
"items": { "type": "string" },
|
|
"description": "description"
|
|
})
|
|
})
|
|
|
|
it("Array(String) + isMinLength", () => {
|
|
assertJsonSchema(Schema.Array(Schema.String).check(Schema.isMinLength(1)), {
|
|
"type": "array",
|
|
"items": { "type": "string" },
|
|
"description": "a value with a length of at least 1",
|
|
"minItems": 1
|
|
})
|
|
})
|
|
|
|
it("Array(String) + isMinLength + isMaxLength", () => {
|
|
assertJsonSchema(Schema.Array(Schema.String).check(Schema.isMinLength(1), Schema.isMaxLength(2)), {
|
|
"type": "array",
|
|
"items": { "type": "string" },
|
|
"description": "a value with a length of at least 1 and a value with a length of at most 2",
|
|
"minItems": 1,
|
|
"maxItems": 2
|
|
})
|
|
})
|
|
})
|
|
|
|
describe("Tuple", () => {
|
|
it("Tuple([])", () => {
|
|
assertJsonSchema(Schema.Tuple([]), {
|
|
"type": "array",
|
|
"items": false
|
|
})
|
|
})
|
|
|
|
it("Tuple([String, Finite])", async () => {
|
|
const schema = Schema.Tuple([Schema.String, Schema.Finite])
|
|
assertJsonSchema(schema, {
|
|
"type": "object",
|
|
"description":
|
|
"Tuple encoded as an object with numeric string keys ('0', '1', ...). If present, '__rest__' contains remaining elements",
|
|
"properties": {
|
|
"0": { "type": "string" },
|
|
"1": { "type": "number" }
|
|
},
|
|
"required": ["0", "1"],
|
|
"additionalProperties": false
|
|
})
|
|
|
|
const codec = toCodecOpenAI(schema).codec
|
|
const asserts = new TestSchema.Asserts(codec)
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(["a", 1], { "0": "a", "1": 1 })
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed({ "0": "a", "1": 1 }, ["a", 1])
|
|
})
|
|
|
|
it("Tuple([String, Finite]) + description", () => {
|
|
const schema = Schema.Tuple([Schema.String, Schema.Finite]).annotate({ description: "description" })
|
|
assertJsonSchema(schema, {
|
|
"type": "object",
|
|
"description":
|
|
"Tuple encoded as an object with numeric string keys ('0', '1', ...). If present, '__rest__' contains remaining elements; description",
|
|
"properties": {
|
|
"0": { "type": "string" },
|
|
"1": { "type": "number" }
|
|
},
|
|
"required": ["0", "1"],
|
|
"additionalProperties": false
|
|
})
|
|
})
|
|
|
|
it("Tuple([String, Finite?])", async () => {
|
|
const schema = Schema.Tuple([Schema.String, Schema.optionalKey(Schema.Finite)])
|
|
assertJsonSchema(schema, {
|
|
"type": "object",
|
|
"description":
|
|
"Tuple encoded as an object with numeric string keys ('0', '1', ...). If present, '__rest__' contains remaining elements",
|
|
"properties": {
|
|
"0": { "type": "string" },
|
|
"1": {
|
|
"anyOf": [
|
|
{ "type": "number" },
|
|
{ "type": "null" }
|
|
]
|
|
}
|
|
},
|
|
"required": ["0", "1"],
|
|
"additionalProperties": false
|
|
})
|
|
|
|
const codec = toCodecOpenAI(schema).codec
|
|
const asserts = new TestSchema.Asserts(codec)
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(["a", 1], { "0": "a", "1": 1 })
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed({ "0": "a", "1": 1 }, ["a", 1])
|
|
})
|
|
})
|
|
|
|
describe("TupleWithRest", () => {
|
|
it("TupleWithRest([String, Finite], Boolean)", async () => {
|
|
const schema = Schema.TupleWithRest(
|
|
Schema.Tuple([Schema.String, Schema.Finite]),
|
|
[Schema.Boolean]
|
|
)
|
|
assertJsonSchema(schema, {
|
|
"type": "object",
|
|
"description":
|
|
"Tuple encoded as an object with numeric string keys ('0', '1', ...). If present, '__rest__' contains remaining elements",
|
|
"properties": {
|
|
"0": { "type": "string" },
|
|
"1": { "type": "number" },
|
|
"__rest__": { "type": "array", "items": { "type": "boolean" } }
|
|
},
|
|
"required": ["0", "1", "__rest__"],
|
|
"additionalProperties": false
|
|
})
|
|
const codec = toCodecOpenAI(schema).codec
|
|
const asserts = new TestSchema.Asserts(codec)
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(["a", 1], { "0": "a", "1": 1, "__rest__": [] })
|
|
await encoding.succeed(["a", 1, true], { "0": "a", "1": 1, "__rest__": [true] })
|
|
await encoding.succeed(["a", 1, true, false], { "0": "a", "1": 1, "__rest__": [true, false] })
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed({ "0": "a", "1": 1, "__rest__": [] }, ["a", 1])
|
|
await decoding.succeed({ "0": "a", "1": 1, "__rest__": [true] }, ["a", 1, true])
|
|
await decoding.succeed({ "0": "a", "1": 1, "__rest__": [true, false] }, ["a", 1, true, false])
|
|
})
|
|
})
|
|
|
|
describe("Struct", () => {
|
|
it("required properties", () => {
|
|
assertJsonSchema(Schema.Struct({ a: Schema.String }), {
|
|
"type": "object",
|
|
"properties": {
|
|
"a": { "type": "string" }
|
|
},
|
|
"required": ["a"],
|
|
"additionalProperties": false
|
|
})
|
|
})
|
|
|
|
it("optional properties", async () => {
|
|
const schema = Schema.Struct({
|
|
a: Schema.String,
|
|
b: Schema.optionalKey(Schema.Finite)
|
|
})
|
|
assertJsonSchema(
|
|
schema,
|
|
{
|
|
"type": "object",
|
|
"properties": {
|
|
"a": { "type": "string" },
|
|
"b": {
|
|
"anyOf": [
|
|
{ "type": "number" },
|
|
{ "type": "null" }
|
|
]
|
|
}
|
|
},
|
|
"required": ["a", "b"],
|
|
"additionalProperties": false
|
|
}
|
|
)
|
|
const codec = toCodecOpenAI(schema).codec
|
|
const asserts = new TestSchema.Asserts(codec)
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed({ a: "a", b: 1 })
|
|
await encoding.succeed({ a: "a" }, { a: "a", b: null })
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed({ "a": "a", "b": 1 })
|
|
await decoding.succeed({ "a": "a", "b": null }, { a: "a" })
|
|
})
|
|
})
|
|
|
|
it("Class", () => {
|
|
class Person extends Schema.Class<Person>("Person")({
|
|
name: Schema.String
|
|
}) {}
|
|
|
|
assertJsonSchema(Person, {
|
|
"type": "object",
|
|
"properties": {
|
|
"name": { "type": "string" }
|
|
},
|
|
"required": ["name"],
|
|
"additionalProperties": false,
|
|
"$defs": {
|
|
"Person": {
|
|
"type": "object",
|
|
"properties": {
|
|
"name": { "type": "string" }
|
|
},
|
|
"required": ["name"],
|
|
"additionalProperties": false
|
|
}
|
|
}
|
|
})
|
|
})
|
|
|
|
describe("Record", () => {
|
|
it("EmptyParams", () => {
|
|
assertJsonSchema(Tool.EmptyParams, {
|
|
"type": "object",
|
|
"properties": {},
|
|
"additionalProperties": false
|
|
})
|
|
assertJsonSchema(Schema.Record(Schema.String, Schema.Never), {
|
|
"type": "object",
|
|
"properties": {},
|
|
"additionalProperties": false
|
|
})
|
|
})
|
|
|
|
it("Record(String, Finite)", async () => {
|
|
const schema = Schema.Record(Schema.String, Schema.Finite)
|
|
assertJsonSchema(schema, {
|
|
"type": "array",
|
|
"description": "Object encoded as array of [key, value] pairs. Apply object constraints to the decoded object",
|
|
"items": {
|
|
"type": "object",
|
|
"description":
|
|
"Tuple encoded as an object with numeric string keys ('0', '1', ...). If present, '__rest__' contains remaining elements",
|
|
"properties": {
|
|
"0": { "type": "string" },
|
|
"1": { "type": "number" }
|
|
},
|
|
"required": ["0", "1"],
|
|
"additionalProperties": false
|
|
}
|
|
})
|
|
const codec = toCodecOpenAI(schema).codec
|
|
const asserts = new TestSchema.Asserts(codec)
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed({ "a": 1, "b": 2 }, [{ 0: "a", 1: 1 }, { 0: "b", 1: 2 }])
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed([{ 0: "a", 1: 1 }, { 0: "b", 1: 2 }], { "a": 1, "b": 2 })
|
|
})
|
|
|
|
it("Record(String, Finite) + description", () => {
|
|
const schema = Schema.Record(Schema.String, Schema.Finite).annotate({ description: "description" })
|
|
assertJsonSchema(schema, {
|
|
"type": "array",
|
|
"description":
|
|
"Object encoded as array of [key, value] pairs. Apply object constraints to the decoded object; description",
|
|
"items": {
|
|
"type": "object",
|
|
"description":
|
|
"Tuple encoded as an object with numeric string keys ('0', '1', ...). If present, '__rest__' contains remaining elements",
|
|
"properties": {
|
|
"0": { "type": "string" },
|
|
"1": { "type": "number" }
|
|
},
|
|
"required": ["0", "1"],
|
|
"additionalProperties": false
|
|
}
|
|
})
|
|
})
|
|
|
|
it("Record(String, Finite) + isMinProperties", () => {
|
|
const schema = Schema.Record(Schema.String, Schema.Finite).check(Schema.isMinProperties(2))
|
|
assertJsonSchema(schema, {
|
|
"type": "array",
|
|
"description":
|
|
"Object encoded as array of [key, value] pairs. Apply object constraints to the decoded object; a value with at least 2 entries",
|
|
"items": {
|
|
"type": "object",
|
|
"description":
|
|
"Tuple encoded as an object with numeric string keys ('0', '1', ...). If present, '__rest__' contains remaining elements",
|
|
"properties": {
|
|
"0": { "type": "string" },
|
|
"1": { "type": "number" }
|
|
},
|
|
"required": ["0", "1"],
|
|
"additionalProperties": false
|
|
}
|
|
})
|
|
})
|
|
|
|
it("Record(String, Finite) + isMinProperties + description", () => {
|
|
const schema = Schema.Record(Schema.String, Schema.Finite).check(
|
|
Schema.isMinProperties(2, { description: "description" })
|
|
)
|
|
assertJsonSchema(schema, {
|
|
"type": "array",
|
|
"description":
|
|
"Object encoded as array of [key, value] pairs. Apply object constraints to the decoded object; description",
|
|
"items": {
|
|
"type": "object",
|
|
"description":
|
|
"Tuple encoded as an object with numeric string keys ('0', '1', ...). If present, '__rest__' contains remaining elements",
|
|
"properties": {
|
|
"0": { "type": "string" },
|
|
"1": { "type": "number" }
|
|
},
|
|
"required": ["0", "1"],
|
|
"additionalProperties": false
|
|
}
|
|
})
|
|
})
|
|
})
|
|
})
|