3194 lines
108 KiB
TypeScript
3194 lines
108 KiB
TypeScript
import {
|
|
Cause,
|
|
DateTime,
|
|
Duration,
|
|
Effect,
|
|
HashMap,
|
|
Option,
|
|
Redacted,
|
|
Result,
|
|
Schema,
|
|
SchemaGetter,
|
|
SchemaIssue,
|
|
SchemaParser,
|
|
SchemaTransformation
|
|
} from "effect"
|
|
import { TestSchema } from "effect/testing"
|
|
import { describe, it } from "vitest"
|
|
import { assertTrue, deepStrictEqual, strictEqual, throws } from "../utils/assert.ts"
|
|
|
|
const isDeno = "Deno" in globalThis
|
|
|
|
const FiniteFromDate = Schema.Date.pipe(Schema.decodeTo(
|
|
Schema.Number,
|
|
SchemaTransformation.transform({
|
|
decode: (date) => date.getTime(),
|
|
encode: (n) => new Date(n)
|
|
})
|
|
))
|
|
|
|
describe("Serializers", () => {
|
|
describe("toCodecJson", () => {
|
|
it("exposes the source schema", () => {
|
|
const schema = Schema.FiniteFromString
|
|
const serializer = Schema.toCodecJson(schema)
|
|
strictEqual(serializer.schema, schema)
|
|
})
|
|
|
|
it("should reorder the types in the Union based on the encoded side", async () => {
|
|
const schema = Schema.Union([
|
|
Schema.String,
|
|
Schema.String.pipe(Schema.encodeTo(Schema.BigInt, {
|
|
decode: SchemaGetter.transform((n: bigint) => String(n) + "a"),
|
|
encode: SchemaGetter.transform(() => 0n)
|
|
}))
|
|
])
|
|
const serializer = Schema.toCodecJson(schema)
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(serializer))
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed("1", "1a")
|
|
})
|
|
|
|
describe("schemas without encoding", () => {
|
|
describe("Unsupported schemas", () => {
|
|
it("Struct with Symbol property name", () => {
|
|
const a = Symbol.for("a")
|
|
const schema = Schema.Struct({
|
|
[a]: Schema.String
|
|
})
|
|
throws(
|
|
() => Schema.toCodecJson(schema),
|
|
"Objects property names must be strings"
|
|
)
|
|
})
|
|
})
|
|
|
|
describe("Declaration", () => {
|
|
it("instanceOf without annotations", async () => {
|
|
const schema = Schema.instanceOf(URL)
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(new URL("https://effect.website"), null)
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.fail("https://effect.website/", `Expected null, got "https://effect.website/"`)
|
|
})
|
|
|
|
describe("instanceOf with annotation", () => {
|
|
it("arg: message: string", async () => {
|
|
class MyError extends Error {
|
|
constructor(message?: string) {
|
|
super(message)
|
|
this.name = "MyError"
|
|
Object.setPrototypeOf(this, MyError.prototype)
|
|
}
|
|
}
|
|
|
|
const schema = Schema.instanceOf(
|
|
MyError,
|
|
{
|
|
title: "MyError",
|
|
toCodec: () =>
|
|
Schema.link<MyError>()(
|
|
Schema.String,
|
|
SchemaTransformation.transform({
|
|
decode: (message) => new MyError(message),
|
|
encode: (e) => e.message
|
|
})
|
|
)
|
|
}
|
|
)
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(new MyError("a"), "a")
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed("a", new MyError("a"))
|
|
})
|
|
|
|
it("arg: struct", async () => {
|
|
class MyError extends Error {
|
|
static Props = Schema.Struct({
|
|
message: Schema.String,
|
|
cause: Schema.String
|
|
})
|
|
|
|
constructor(props: typeof MyError.Props["Type"]) {
|
|
super(props.message, { cause: props.cause })
|
|
this.name = "MyError"
|
|
Object.setPrototypeOf(this, MyError.prototype)
|
|
}
|
|
|
|
static schema = Schema.instanceOf(
|
|
MyError,
|
|
{
|
|
title: "MyError",
|
|
toCodec: () =>
|
|
Schema.link<MyError>()(
|
|
MyError.Props,
|
|
SchemaTransformation.transform({
|
|
decode: (props) => new MyError(props),
|
|
encode: (e) => ({
|
|
message: e.message,
|
|
cause: typeof e.cause === "string" ? e.cause : String(e.cause)
|
|
})
|
|
})
|
|
)
|
|
}
|
|
)
|
|
}
|
|
|
|
const schema = MyError.schema
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(new MyError({ message: "a", cause: "b" }), {
|
|
message: "a",
|
|
cause: "b"
|
|
})
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed({ message: "a", cause: "b" }, new MyError({ message: "a", cause: "b" }))
|
|
})
|
|
})
|
|
})
|
|
|
|
describe("should return the same reference if nothing changed", () => {
|
|
it("Struct", async () => {
|
|
const schema = Schema.Struct({
|
|
a: Schema.String,
|
|
b: Schema.Boolean
|
|
})
|
|
const serializer = Schema.toCodecJson(schema)
|
|
strictEqual(serializer.ast, schema.ast)
|
|
})
|
|
|
|
it("Record", async () => {
|
|
const schema = Schema.Record(Schema.String, Schema.Boolean)
|
|
const serializer = Schema.toCodecJson(schema)
|
|
strictEqual(serializer.ast, schema.ast)
|
|
})
|
|
|
|
it("Tuple", async () => {
|
|
const schema = Schema.Tuple([Schema.String, Schema.Boolean])
|
|
const serializer = Schema.toCodecJson(schema)
|
|
strictEqual(serializer.ast, schema.ast)
|
|
})
|
|
|
|
it("Array", async () => {
|
|
const schema = Schema.Array(Schema.String)
|
|
const serializer = Schema.toCodecJson(schema)
|
|
strictEqual(serializer.ast, schema.ast)
|
|
})
|
|
|
|
it("Union", async () => {
|
|
const schema = Schema.Union([Schema.String, Schema.Boolean])
|
|
const serializer = Schema.toCodecJson(schema)
|
|
strictEqual(serializer.ast, schema.ast)
|
|
})
|
|
})
|
|
|
|
it("should apply the construction process to the provided link in the serializer annotation", async () => {
|
|
const schema = Schema.Struct({
|
|
a: Schema.Date.annotate({
|
|
toCodec: () =>
|
|
Schema.link<Date>()(
|
|
Schema.Date,
|
|
SchemaTransformation.passthrough()
|
|
)
|
|
}),
|
|
b: Schema.Number
|
|
})
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed({ a: new Date("2021-01-01"), b: 1 }, {
|
|
a: "2021-01-01T00:00:00.000Z",
|
|
b: 1
|
|
})
|
|
})
|
|
|
|
it("Never", async () => {
|
|
const schema = Schema.Never
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.fail({}, "Expected never, got {}")
|
|
})
|
|
|
|
it("Any", async () => {
|
|
const schema = Schema.Any
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(() => {})
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(() => {})
|
|
})
|
|
|
|
it("Unknown", async () => {
|
|
const schema = Schema.Unknown
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed("a")
|
|
await encoding.succeed(1)
|
|
await encoding.succeed(true)
|
|
await encoding.succeed(null)
|
|
await encoding.succeed({ a: "a", b: 1, c: true })
|
|
await encoding.succeed(["a", 1, true])
|
|
await encoding.fail({ a: 1n }, `Expected JSON value, got {"a":1n}`)
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed("a")
|
|
await decoding.succeed(1)
|
|
await decoding.succeed(true)
|
|
await decoding.succeed(null)
|
|
await decoding.succeed({ a: "a", b: 1, c: true })
|
|
await decoding.succeed(["a", 1, true])
|
|
await decoding.fail({ a: 1n }, `Expected JSON value, got {"a":1n}`)
|
|
})
|
|
|
|
it("ObjectKeyword", async () => {
|
|
const schema = Schema.ObjectKeyword
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed({ a: "a", b: 1, c: true })
|
|
await encoding.succeed(["a", 1, true])
|
|
await encoding.fail("a", `Expected object | array | function, got "a"`)
|
|
await encoding.fail({ a: 1n }, `Expected JSON value, got {"a":1n}`)
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed({ a: "a", b: 1, c: true })
|
|
await decoding.succeed(["a", 1, true])
|
|
await decoding.fail("a", `Expected object | array | function, got "a"`)
|
|
await decoding.fail({ a: 1n }, `Expected JSON value, got {"a":1n}`)
|
|
})
|
|
|
|
it("Undefined", async () => {
|
|
const schema = Schema.Undefined
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(undefined, null)
|
|
})
|
|
|
|
it("Void", async () => {
|
|
const schema = Schema.Void
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(undefined, null)
|
|
})
|
|
|
|
it("Null", async () => {
|
|
const schema = Schema.Null
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(null)
|
|
})
|
|
|
|
it("String", async () => {
|
|
const schema = Schema.String
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed("a")
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed("a", "a")
|
|
})
|
|
|
|
describe("Number", () => {
|
|
it("Number", async () => {
|
|
const schema = Schema.Number
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(1)
|
|
await encoding.succeed(-1)
|
|
await encoding.succeed(1.2)
|
|
await encoding.succeed(Infinity, "Infinity")
|
|
await encoding.succeed(-Infinity, "-Infinity")
|
|
await encoding.succeed(NaN, "NaN")
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(1)
|
|
await decoding.succeed(-1)
|
|
await decoding.succeed(1.2)
|
|
await decoding.succeed("Infinity", Infinity)
|
|
await decoding.succeed("-Infinity", -Infinity)
|
|
await decoding.succeed("NaN", NaN)
|
|
await decoding.succeed(Infinity)
|
|
await decoding.succeed(-Infinity)
|
|
await decoding.succeed(NaN)
|
|
await decoding.fail(null, `Expected number | "Infinity" | "-Infinity" | "NaN", got null`)
|
|
await decoding.fail("a", `Expected "Infinity" | "-Infinity" | "NaN", got "a"`)
|
|
})
|
|
|
|
describe("checks", () => {
|
|
it("Finite", async () => {
|
|
const schema = Schema.Finite
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(1)
|
|
await encoding.succeed(-1)
|
|
await encoding.succeed(1.2)
|
|
await encoding.fail(Infinity, "Expected a finite number, got Infinity")
|
|
await encoding.fail(-Infinity, "Expected a finite number, got -Infinity")
|
|
await encoding.fail(NaN, "Expected a finite number, got NaN")
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(1)
|
|
await decoding.succeed(-1)
|
|
await decoding.succeed(1.2)
|
|
await decoding.fail("Infinity", `Expected number, got "Infinity"`)
|
|
await decoding.fail("-Infinity", `Expected number, got "-Infinity"`)
|
|
await decoding.fail("NaN", `Expected number, got "NaN"`)
|
|
await decoding.fail(Infinity, `Expected a finite number, got Infinity`)
|
|
await decoding.fail(-Infinity, `Expected a finite number, got -Infinity`)
|
|
await decoding.fail(NaN, `Expected a finite number, got NaN`)
|
|
await decoding.fail(null, `Expected number, got null`)
|
|
await decoding.fail("a", `Expected number, got "a"`)
|
|
})
|
|
|
|
it("Int", async () => {
|
|
const schema = Schema.Int
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(1)
|
|
await encoding.succeed(-1)
|
|
await encoding.fail(1.2, `Expected an integer, got 1.2`)
|
|
await encoding.fail(Infinity, "Expected an integer, got Infinity")
|
|
await encoding.fail(-Infinity, "Expected an integer, got -Infinity")
|
|
await encoding.fail(NaN, "Expected an integer, got NaN")
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(1)
|
|
await decoding.succeed(-1)
|
|
await decoding.fail(1.2, `Expected an integer, got 1.2`)
|
|
await decoding.fail("Infinity", `Expected number, got "Infinity"`)
|
|
await decoding.fail("-Infinity", `Expected number, got "-Infinity"`)
|
|
await decoding.fail("NaN", `Expected number, got "NaN"`)
|
|
await decoding.fail(Infinity, `Expected an integer, got Infinity`)
|
|
await decoding.fail(-Infinity, `Expected an integer, got -Infinity`)
|
|
await decoding.fail(NaN, `Expected an integer, got NaN`)
|
|
await decoding.fail(null, `Expected number, got null`)
|
|
await decoding.fail("a", `Expected number, got "a"`)
|
|
})
|
|
|
|
it("isGreaterThanOrEqualTo", async () => {
|
|
const schema = Schema.Number.check(Schema.isGreaterThanOrEqualTo(1))
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(1)
|
|
await encoding.fail(-1, `Expected a value greater than or equal to 1, got -1`)
|
|
await encoding.succeed(1.2)
|
|
await encoding.succeed(Infinity, "Infinity")
|
|
await encoding.fail(-Infinity, "Expected a value greater than or equal to 1, got -Infinity")
|
|
await encoding.fail(NaN, "Expected a value greater than or equal to 1, got NaN")
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(1)
|
|
await encoding.fail(-1, `Expected a value greater than or equal to 1, got -1`)
|
|
await decoding.succeed(1.2)
|
|
await decoding.succeed("Infinity", Infinity)
|
|
await decoding.fail("-Infinity", `Expected a value greater than or equal to 1, got -Infinity`)
|
|
await decoding.fail("NaN", `Expected a value greater than or equal to 1, got NaN`)
|
|
await decoding.succeed(Infinity)
|
|
await decoding.fail(-Infinity, `Expected a value greater than or equal to 1, got -Infinity`)
|
|
await decoding.fail(NaN, `Expected a value greater than or equal to 1, got NaN`)
|
|
await decoding.fail(null, `Expected number | "Infinity" | "-Infinity" | "NaN", got null`)
|
|
await decoding.fail("a", `Expected "Infinity" | "-Infinity" | "NaN", got "a"`)
|
|
})
|
|
})
|
|
})
|
|
|
|
it("Boolean", async () => {
|
|
const schema = Schema.Boolean
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(true)
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(true, true)
|
|
})
|
|
|
|
it("Symbol", async () => {
|
|
const schema = Schema.Symbol
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(Symbol.for("a"), "Symbol(a)")
|
|
await encoding.fail(
|
|
Symbol("a"),
|
|
"cannot serialize to string, Symbol is not registered"
|
|
)
|
|
await encoding.fail(
|
|
Symbol(),
|
|
"cannot serialize to string, Symbol is not registered"
|
|
)
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed("Symbol(a)", Symbol.for("a"))
|
|
})
|
|
|
|
it("UniqueSymbol", async () => {
|
|
const schema = Schema.UniqueSymbol(Symbol.for("a"))
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(Symbol.for("a"), "Symbol(a)")
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed("Symbol(a)", Symbol.for("a"))
|
|
})
|
|
|
|
it("BigInt", async () => {
|
|
const schema = Schema.BigInt
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(1n, "1")
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed("1", 1n)
|
|
})
|
|
|
|
it("PropertyKey", async () => {
|
|
const schema = Schema.PropertyKey
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed("a")
|
|
await encoding.succeed(1)
|
|
await encoding.succeed(Symbol.for("a"), "Symbol(a)")
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed("a")
|
|
await decoding.succeed(1)
|
|
await decoding.succeed("Symbol(a)", Symbol.for("a"))
|
|
})
|
|
|
|
describe("Literal", () => {
|
|
it("string", async () => {
|
|
const schema = Schema.Literal("a")
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed("a")
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed("a")
|
|
})
|
|
|
|
it("number", async () => {
|
|
const schema = Schema.Literal(1)
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(1)
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(1)
|
|
})
|
|
|
|
it("boolean", async () => {
|
|
const schema = Schema.Literal(true)
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(true)
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(true)
|
|
})
|
|
|
|
it("bigint", async () => {
|
|
const schema = Schema.Literal(1n)
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(1n, "1")
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed("1", 1n)
|
|
})
|
|
})
|
|
|
|
it("Literals", async () => {
|
|
const schema = Schema.Literals(["a", 1, 2n, true])
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.fail(
|
|
"-",
|
|
`Expected "a" | 1 | "2" | true, got "-"`
|
|
)
|
|
})
|
|
|
|
describe("TemplateLiteral", () => {
|
|
it("1n + string", async () => {
|
|
const schema = Schema.TemplateLiteral([1n, Schema.String])
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed("1a")
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed("1a")
|
|
})
|
|
|
|
it(`"a" + bigint`, async () => {
|
|
const schema = Schema.TemplateLiteral(["a", Schema.BigInt])
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed("a1")
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed("a1")
|
|
})
|
|
})
|
|
|
|
it("Enum", async () => {
|
|
enum Fruits {
|
|
Apple,
|
|
Banana
|
|
}
|
|
const schema = Schema.Enum(Fruits)
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(Fruits.Apple, 0)
|
|
await encoding.succeed(Fruits.Banana, 1)
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(0, Fruits.Apple)
|
|
await decoding.succeed(1, Fruits.Banana)
|
|
})
|
|
|
|
describe("Struct", () => {
|
|
it("Date", async () => {
|
|
const schema = Schema.Struct({
|
|
a: Schema.Date
|
|
})
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(
|
|
{ a: new Date("2021-01-01") },
|
|
{ a: "2021-01-01T00:00:00.000Z" }
|
|
)
|
|
})
|
|
|
|
it("UndefinedOr(Date)", async () => {
|
|
const schema = Schema.Struct({
|
|
a: Schema.UndefinedOr(Schema.Date)
|
|
})
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed({ a: new Date("2021-01-01") }, {
|
|
a: "2021-01-01T00:00:00.000Z"
|
|
})
|
|
await encoding.succeed({ a: undefined }, { a: null })
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed({ a: "2021-01-01T00:00:00.000Z" }, {
|
|
a: new Date("2021-01-01")
|
|
})
|
|
await decoding.succeed({ a: null }, { a: undefined })
|
|
})
|
|
|
|
it("NullOr(Date)", async () => {
|
|
const schema = Schema.Struct({
|
|
a: Schema.NullOr(Schema.Date)
|
|
})
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed({ a: new Date("2021-01-01") }, {
|
|
a: "2021-01-01T00:00:00.000Z"
|
|
})
|
|
await encoding.succeed({ a: null }, { a: null })
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed({ a: "2021-01-01T00:00:00.000Z" }, {
|
|
a: new Date("2021-01-01")
|
|
})
|
|
await decoding.succeed({ a: null }, { a: null })
|
|
})
|
|
|
|
it("optionalKey(Date)", async () => {
|
|
const schema = Schema.Struct({
|
|
a: Schema.optionalKey(Schema.Date)
|
|
})
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed({ a: new Date("2021-01-01") }, {
|
|
a: "2021-01-01T00:00:00.000Z"
|
|
})
|
|
await encoding.succeed({}, {})
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed({ a: "2021-01-01T00:00:00.000Z" }, {
|
|
a: new Date("2021-01-01")
|
|
})
|
|
await decoding.succeed({}, {})
|
|
})
|
|
|
|
it("optional(Date)", async () => {
|
|
const schema = Schema.Struct({
|
|
a: Schema.optional(Schema.Date)
|
|
})
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed({ a: new Date("2021-01-01") }, {
|
|
a: "2021-01-01T00:00:00.000Z"
|
|
})
|
|
await encoding.succeed({}, {})
|
|
await encoding.succeed({ a: undefined }, { a: null })
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed({ a: "2021-01-01T00:00:00.000Z" }, {
|
|
a: new Date("2021-01-01")
|
|
})
|
|
await decoding.succeed({}, {})
|
|
await decoding.succeed({ a: null }, { a: undefined })
|
|
})
|
|
})
|
|
|
|
it("Record(Symbol, Date)", async () => {
|
|
const schema = Schema.Record(Schema.Symbol, Schema.Date)
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(
|
|
{ [Symbol.for("a")]: new Date("2021-01-01"), [Symbol.for("b")]: new Date("2021-01-01") },
|
|
{ "Symbol(a)": "2021-01-01T00:00:00.000Z", "Symbol(b)": "2021-01-01T00:00:00.000Z" }
|
|
)
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(
|
|
{ "Symbol(a)": "2021-01-01T00:00:00.000Z", "Symbol(b)": "2021-01-01T00:00:00.000Z" },
|
|
{ [Symbol.for("a")]: new Date("2021-01-01"), [Symbol.for("b")]: new Date("2021-01-01") }
|
|
)
|
|
})
|
|
|
|
describe("Tuple", () => {
|
|
it("Date", async () => {
|
|
const schema = Schema.Tuple([Schema.Date])
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(
|
|
[new Date("2021-01-01")],
|
|
["2021-01-01T00:00:00.000Z"]
|
|
)
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(
|
|
["2021-01-01T00:00:00.000Z"],
|
|
[new Date("2021-01-01")]
|
|
)
|
|
})
|
|
|
|
it("UndefinedOr(Date)", async () => {
|
|
const schema = Schema.Tuple([Schema.UndefinedOr(Schema.Date)])
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(
|
|
[new Date("2021-01-01")],
|
|
["2021-01-01T00:00:00.000Z"]
|
|
)
|
|
await encoding.succeed([undefined], [null])
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(["2021-01-01T00:00:00.000Z"], [
|
|
new Date("2021-01-01")
|
|
])
|
|
await decoding.succeed([null], [undefined])
|
|
})
|
|
|
|
it("NullOr(Date)", async () => {
|
|
const schema = Schema.Tuple([Schema.NullOr(Schema.Date)])
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed([new Date("2021-01-01")], [
|
|
"2021-01-01T00:00:00.000Z"
|
|
])
|
|
await encoding.succeed([null], [null])
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(["2021-01-01T00:00:00.000Z"], [
|
|
new Date("2021-01-01")
|
|
])
|
|
await decoding.succeed([null], [null])
|
|
})
|
|
|
|
it("optionalKey(Date)", async () => {
|
|
const schema = Schema.Tuple([Schema.optionalKey(Schema.Date)])
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed([new Date("2021-01-01")], [
|
|
"2021-01-01T00:00:00.000Z"
|
|
])
|
|
await encoding.succeed([], [])
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(["2021-01-01T00:00:00.000Z"], [
|
|
new Date("2021-01-01")
|
|
])
|
|
await decoding.succeed([], [])
|
|
})
|
|
|
|
it("optional(Date)", async () => {
|
|
const schema = Schema.Tuple([Schema.optional(Schema.Date)])
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed([new Date("2021-01-01")], [
|
|
"2021-01-01T00:00:00.000Z"
|
|
])
|
|
await encoding.succeed([], [])
|
|
await encoding.succeed([undefined], [null])
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(["2021-01-01T00:00:00.000Z"], [
|
|
new Date("2021-01-01")
|
|
])
|
|
await decoding.succeed([], [])
|
|
await decoding.succeed([null], [undefined])
|
|
})
|
|
})
|
|
|
|
it("Array(Date)", async () => {
|
|
const schema = Schema.Array(Schema.Date)
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(
|
|
[new Date("2021-01-01"), new Date("2021-01-02")],
|
|
["2021-01-01T00:00:00.000Z", "2021-01-02T00:00:00.000Z"]
|
|
)
|
|
})
|
|
|
|
describe("Union", () => {
|
|
it("NullOr(String)", async () => {
|
|
const schema = Schema.NullOr(Schema.String)
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed("a")
|
|
await encoding.succeed(null)
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(null)
|
|
await decoding.succeed("a")
|
|
})
|
|
|
|
it("NullOr(Number)", async () => {
|
|
const schema = Schema.NullOr(Schema.Number)
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(1)
|
|
await encoding.succeed(null)
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(null)
|
|
await decoding.succeed(1)
|
|
})
|
|
|
|
it("Array(NullOr(Number))", async () => {
|
|
const schema = Schema.Array(Schema.NullOr(Schema.Number))
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed([1, null])
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed([1, null])
|
|
})
|
|
|
|
it("Union(Schema.Date, Finite)", async () => {
|
|
const schema = Schema.Union([Schema.Date, Schema.Finite])
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(
|
|
new Date("2021-01-01"),
|
|
"2021-01-01T00:00:00.000Z"
|
|
)
|
|
await encoding.succeed(0)
|
|
})
|
|
})
|
|
|
|
it("Suspend", async () => {
|
|
interface Category {
|
|
readonly a: number
|
|
readonly categories: ReadonlyArray<Category>
|
|
}
|
|
|
|
const schema = Schema.Struct({
|
|
a: Schema.Finite.check(Schema.isGreaterThan(0)),
|
|
categories: Schema.Array(Schema.suspend((): Schema.Codec<Category> => schema))
|
|
})
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed({ a: 1, categories: [] }, {
|
|
a: 1,
|
|
categories: []
|
|
})
|
|
await encoding.succeed({
|
|
a: 1,
|
|
categories: [{ a: 2, categories: [] }]
|
|
}, {
|
|
a: 1,
|
|
categories: [
|
|
{ a: 2, categories: [] }
|
|
]
|
|
})
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed({
|
|
a: 1,
|
|
categories: []
|
|
}, { a: 1, categories: [] })
|
|
await decoding.succeed({
|
|
a: 1,
|
|
categories: [
|
|
{ a: 2, categories: [] }
|
|
]
|
|
}, { a: 1, categories: [{ a: 2, categories: [] }] })
|
|
})
|
|
|
|
it("Class", async () => {
|
|
class A extends Schema.Class<A>("A")(Schema.Struct({
|
|
a: Schema.Finite
|
|
})) {}
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(Schema.toType(A)))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(new A({ a: 0 }), { a: 0 })
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed({ a: 0 }, new A({ a: 0 }))
|
|
})
|
|
|
|
it("ErrorClass", async () => {
|
|
class E extends Schema.ErrorClass<E>("E")({
|
|
a: Schema.Finite
|
|
}) {}
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(Schema.toType(E)))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(new E({ a: 0 }), { a: 0 })
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed({ a: 0 }, new E({ a: 0 }))
|
|
})
|
|
|
|
it("Date", async () => {
|
|
const schema = Schema.Date
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(
|
|
new Date("2021-01-01"),
|
|
"2021-01-01T00:00:00.000Z"
|
|
)
|
|
})
|
|
|
|
it("Error", async () => {
|
|
const schema = Schema.Error()
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(
|
|
new Error("a"),
|
|
{ name: "Error", message: "a" }
|
|
)
|
|
await encoding.succeed(
|
|
new Error("a", { cause: new Error("b") }),
|
|
{ name: "Error", message: "a", cause: { name: "Error", message: "b" } }
|
|
)
|
|
await encoding.succeed(
|
|
new Error("a", { cause: "b" }),
|
|
{ name: "Error", message: "a", cause: "b" }
|
|
)
|
|
const selfCause = new Error("a")
|
|
selfCause.stack = "stack"
|
|
selfCause.cause = selfCause
|
|
await encoding.succeed(
|
|
selfCause,
|
|
{
|
|
name: "Error",
|
|
message: "a",
|
|
cause: "[Circular]"
|
|
}
|
|
)
|
|
const cyclicCause: Record<string, unknown> = {}
|
|
cyclicCause.self = cyclicCause
|
|
await encoding.succeed(
|
|
new Error("a", { cause: cyclicCause }),
|
|
{ name: "Error", message: "a", cause: {} }
|
|
)
|
|
|
|
const decoding = asserts.decoding()
|
|
// Error: message only
|
|
await decoding.succeed(
|
|
{ message: "a" },
|
|
new Error("a")
|
|
)
|
|
// Error: message and name
|
|
await decoding.succeed(
|
|
{ name: "b", message: "a" },
|
|
(() => {
|
|
const err = new Error("a")
|
|
err.name = "b"
|
|
return err
|
|
})()
|
|
)
|
|
// Error: message, name, and stack
|
|
await decoding.succeed(
|
|
{ name: "b", message: "a", stack: "c" },
|
|
(() => {
|
|
const err = new Error("a")
|
|
err.name = "b"
|
|
err.stack = "c"
|
|
return err
|
|
})()
|
|
)
|
|
// Error: message and cause
|
|
await decoding.succeed(
|
|
{ message: "a", cause: { message: "b" } },
|
|
new Error("a", { cause: new Error("b") })
|
|
)
|
|
// Error: explicit null cause
|
|
await decoding.succeed(
|
|
{ message: "a", cause: null },
|
|
new Error("a", { cause: null })
|
|
)
|
|
})
|
|
|
|
it("Error with stack", async () => {
|
|
const schema = Schema.Error({ includeStack: true })
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
const error = new Error("a")
|
|
error.stack = "stack"
|
|
const customError = new Error("b")
|
|
customError.name = "CustomError"
|
|
customError.stack = "custom stack"
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(error, { name: "Error", message: "a", stack: "stack" })
|
|
await encoding.succeed(customError, { name: "CustomError", message: "b", stack: "custom stack" })
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(
|
|
{ message: "a", stack: "stack" },
|
|
error
|
|
)
|
|
await decoding.succeed(
|
|
{ name: "CustomError", message: "b", stack: "custom stack" },
|
|
customError
|
|
)
|
|
})
|
|
|
|
it("Error with excluded cause", async () => {
|
|
const schema = Schema.Error({ excludeCause: true })
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(
|
|
new Error("a", { cause: new Error("b") }),
|
|
{ name: "Error", message: "a" }
|
|
)
|
|
})
|
|
|
|
it("URL", async () => {
|
|
const schema = Schema.URL
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(
|
|
new URL("https://example.com"),
|
|
"https://example.com/"
|
|
)
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(
|
|
"https://example.com",
|
|
new URL("https://example.com")
|
|
)
|
|
await decoding.succeed(
|
|
"https://example.com/",
|
|
new URL("https://example.com")
|
|
)
|
|
await decoding.fail(
|
|
"not a url",
|
|
`Invalid URL string: not a url`
|
|
)
|
|
})
|
|
|
|
it("URLSearchParams", async () => {
|
|
const schema = Schema.URLSearchParams
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(
|
|
new URLSearchParams("a=1&b=two"),
|
|
"a=1&b=two"
|
|
)
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(
|
|
"a=1&b=two",
|
|
new URLSearchParams("a=1&b=two")
|
|
)
|
|
})
|
|
|
|
it.skipIf(isDeno)("File", async () => {
|
|
const schema = Schema.File
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(
|
|
new File(["hi"], "note.txt", { type: "text/plain", lastModified: 123 }),
|
|
{ data: "aGk=", type: "text/plain", name: "note.txt", lastModified: 123 }
|
|
)
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(
|
|
{ data: "aGk=", type: "text/plain", name: "note.txt", lastModified: 123 },
|
|
new File(["hi"], "note.txt", { type: "text/plain", lastModified: 123 })
|
|
)
|
|
})
|
|
|
|
it.skipIf(isDeno)("FormData", async () => {
|
|
const schema = Schema.FormData
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
const formData = new FormData()
|
|
formData.append("a", "1")
|
|
formData.append("b", new File(["hi"], "note.txt", { type: "text/plain", lastModified: 123 }))
|
|
await encoding.succeed(
|
|
formData,
|
|
[
|
|
["a", { _tag: "String", value: "1" }],
|
|
["b", { _tag: "File", value: { data: "aGk=", type: "text/plain", name: "note.txt", lastModified: 123 } }]
|
|
]
|
|
)
|
|
|
|
const decoding = asserts.decoding()
|
|
const expected = new FormData()
|
|
expected.append("a", "1")
|
|
expected.append("b", new File(["hi"], "note.txt", { type: "text/plain", lastModified: 123 }))
|
|
await decoding.succeed(
|
|
[
|
|
["a", { _tag: "String", value: "1" }],
|
|
["b", { _tag: "File", value: { data: "aGk=", type: "text/plain", name: "note.txt", lastModified: 123 } }]
|
|
],
|
|
expected
|
|
)
|
|
})
|
|
|
|
it("RegExp", async () => {
|
|
const schema = Schema.RegExp
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(new RegExp("a"), { source: "a", flags: "" })
|
|
await encoding.succeed(new RegExp("a", "i"), { source: "a", flags: "i" })
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed({ source: "a", flags: "" }, new RegExp("a"))
|
|
await decoding.succeed({ source: "a", flags: "i" }, new RegExp("a", "i"))
|
|
await decoding.fail(
|
|
{ source: "(", flags: "" },
|
|
`SyntaxError: Invalid regular expression: /(/: Unterminated group`
|
|
)
|
|
await decoding.fail(
|
|
{ source: "a", flags: "x" },
|
|
`SyntaxError: Invalid flags supplied to RegExp constructor 'x'`
|
|
)
|
|
})
|
|
|
|
it("Uint8Array", async () => {
|
|
const schema = Schema.Uint8Array
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(new Uint8Array([1, 2, 3]), "AQID")
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed("AQID", new Uint8Array([1, 2, 3]))
|
|
await decoding.fail(
|
|
"not a base64 string",
|
|
"Length must be a multiple of 4, but is 19"
|
|
)
|
|
})
|
|
|
|
it("Duration", async () => {
|
|
const schema = Schema.Duration
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(Duration.infinity, { _tag: "Infinity" })
|
|
await encoding.succeed(Duration.negativeInfinity, { _tag: "NegativeInfinity" })
|
|
await encoding.succeed(Duration.nanos(1000n), { _tag: "Nanos", value: "1000" })
|
|
await encoding.succeed(Duration.nanos(-1000n), { _tag: "Nanos", value: "-1000" })
|
|
await encoding.succeed(Duration.millis(1), { _tag: "Millis", value: 1 })
|
|
await encoding.succeed(Duration.millis(-1), { _tag: "Millis", value: -1 })
|
|
await encoding.succeed(Duration.zero, { _tag: "Millis", value: 0 })
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed({ _tag: "Infinity" }, Duration.infinity)
|
|
await decoding.succeed({ _tag: "NegativeInfinity" }, Duration.negativeInfinity)
|
|
await decoding.succeed({ _tag: "Millis", value: 1 }, Duration.millis(1))
|
|
await decoding.succeed({ _tag: "Millis", value: -1 }, Duration.millis(-1))
|
|
await decoding.succeed({ _tag: "Nanos", value: "1000" }, Duration.nanos(1000n))
|
|
await decoding.succeed({ _tag: "Nanos", value: "-1000" }, Duration.nanos(-1000n))
|
|
})
|
|
|
|
it("DateTimeUtc", async () => {
|
|
const schema = Schema.DateTimeUtc
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(
|
|
DateTime.makeUnsafe("2021-01-01T00:00:00.000Z"),
|
|
"2021-01-01T00:00:00.000Z"
|
|
)
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(
|
|
"2021-01-01T00:00:00.000Z",
|
|
DateTime.makeUnsafe("2021-01-01T00:00:00.000Z")
|
|
)
|
|
})
|
|
|
|
it("Option(Date)", async () => {
|
|
const schema = Schema.Option(Schema.Date)
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(Option.some(new Date("2021-01-01")), {
|
|
_tag: "Some",
|
|
value: "2021-01-01T00:00:00.000Z"
|
|
})
|
|
await encoding.succeed(Option.none(), { _tag: "None" })
|
|
})
|
|
|
|
describe("Redacted", () => {
|
|
it("Redacted(Option(String))", async () => {
|
|
const schema = Schema.Redacted(Schema.Option(Schema.String))
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(
|
|
Redacted.make(Option.none()),
|
|
{ _tag: "None" }
|
|
)
|
|
await encoding.succeed(
|
|
Redacted.make(Option.some("a")),
|
|
{ _tag: "Some", value: "a" }
|
|
)
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(
|
|
{ _tag: "None" },
|
|
Redacted.make(Option.none())
|
|
)
|
|
await decoding.succeed(
|
|
{ _tag: "Some", value: "a" },
|
|
Redacted.make(Option.some("a"))
|
|
)
|
|
})
|
|
|
|
it("Redacted(Option(String)) disallowJsonEncode", async () => {
|
|
const schema = Schema.Redacted(Schema.Option(Schema.String), {
|
|
disallowJsonEncode: true
|
|
})
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.fail(
|
|
Redacted.make(Option.none()),
|
|
`Cannot serialize Redacted`
|
|
)
|
|
await encoding.fail(
|
|
Redacted.make(Option.some("a")),
|
|
`Cannot serialize Redacted`
|
|
)
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(
|
|
{ _tag: "None" },
|
|
Redacted.make(Option.none())
|
|
)
|
|
await decoding.succeed(
|
|
{ _tag: "Some", value: "a" },
|
|
Redacted.make(Option.some("a"))
|
|
)
|
|
})
|
|
|
|
it("encoding a Redacted with a label", async () => {
|
|
const schema = Schema.Redacted(Schema.String, { label: "password" })
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.fail(
|
|
Redacted.make("a", { label: "API key" }),
|
|
`Expected "password", got "API key"
|
|
at ["label"]`
|
|
)
|
|
})
|
|
|
|
it("encoding a Redacted with a different label", async () => {
|
|
const schema = Schema.Redacted(Schema.String)
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(
|
|
Redacted.make("a", { label: "API key" }),
|
|
"a"
|
|
)
|
|
})
|
|
})
|
|
|
|
it("ReadonlySet", async () => {
|
|
const schema = Schema.ReadonlySet(Schema.Option(Schema.Date))
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(new Set([Option.some(new Date("2021-01-01"))]), [{
|
|
_tag: "Some",
|
|
value: "2021-01-01T00:00:00.000Z"
|
|
}])
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(
|
|
[{ _tag: "Some", value: "2021-01-01T00:00:00.000Z" }],
|
|
new Set([Option.some(new Date("2021-01-01"))])
|
|
)
|
|
})
|
|
|
|
it("ReadonlyMap", async () => {
|
|
const schema = Schema.ReadonlyMap(Schema.Option(Schema.Date), Schema.Finite)
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(
|
|
new Map([[Option.some(new Date("2021-01-01")), 0]]),
|
|
[[
|
|
{ _tag: "Some", value: "2021-01-01T00:00:00.000Z" },
|
|
0
|
|
]]
|
|
)
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(
|
|
[[{ _tag: "Some", value: "2021-01-01T00:00:00.000Z" }, 0]],
|
|
new Map([[Option.some(new Date("2021-01-01")), 0]])
|
|
)
|
|
})
|
|
|
|
it("HashMap", async () => {
|
|
const schema = Schema.HashMap(Schema.Option(Schema.Date), Schema.Finite)
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(
|
|
HashMap.make([Option.some(new Date("2021-01-01")), 0]),
|
|
[[
|
|
{ _tag: "Some", value: "2021-01-01T00:00:00.000Z" },
|
|
0
|
|
]]
|
|
)
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(
|
|
[[{ _tag: "Some", value: "2021-01-01T00:00:00.000Z" }, 0]],
|
|
HashMap.make([Option.some(new Date("2021-01-01")), 0])
|
|
)
|
|
})
|
|
})
|
|
|
|
describe("schemas with encoding", () => {
|
|
it("FiniteFromDate", async () => {
|
|
const schema = FiniteFromDate
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(0, "1970-01-01T00:00:00.000Z")
|
|
})
|
|
|
|
it("Struct", async () => {
|
|
const schema = Schema.Struct({
|
|
a: FiniteFromDate,
|
|
b: FiniteFromDate
|
|
})
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(
|
|
{ a: 0, b: 0 },
|
|
{ a: "1970-01-01T00:00:00.000Z", b: "1970-01-01T00:00:00.000Z" }
|
|
)
|
|
})
|
|
|
|
it("Tuple(Schema.Date, Schema.Date)", async () => {
|
|
const schema = Schema.Tuple([FiniteFromDate, FiniteFromDate])
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(
|
|
[0, 0],
|
|
["1970-01-01T00:00:00.000Z", "1970-01-01T00:00:00.000Z"]
|
|
)
|
|
})
|
|
|
|
it("Class", async () => {
|
|
class A extends Schema.Class<A>("A")(Schema.Struct({
|
|
a: FiniteFromDate
|
|
})) {}
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(A))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(new A({ a: 0 }), { a: "1970-01-01T00:00:00.000Z" })
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed({ a: "1970-01-01T00:00:00.000Z" }, new A({ a: 0 }))
|
|
})
|
|
|
|
it("Error", async () => {
|
|
class E extends Schema.ErrorClass<E>("E")({
|
|
a: FiniteFromDate
|
|
}) {}
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(E))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(new E({ a: 0 }), { a: "1970-01-01T00:00:00.000Z" })
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed({ a: "1970-01-01T00:00:00.000Z" }, new E({ a: 0 }))
|
|
})
|
|
|
|
it("Enum", async () => {
|
|
enum Fruits {
|
|
Apple,
|
|
Banana = "banana"
|
|
}
|
|
const schema = Schema.Enum(Fruits)
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(Fruits.Apple, 0)
|
|
await encoding.succeed(Fruits.Banana, "banana")
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(0, Fruits.Apple)
|
|
})
|
|
|
|
it("Option(Option(FiniteFromDate))", async () => {
|
|
const schema = Schema.Option(Schema.Option(FiniteFromDate))
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(Option.some(Option.some(0)), {
|
|
_tag: "Some",
|
|
value: {
|
|
_tag: "Some",
|
|
value: "1970-01-01T00:00:00.000Z"
|
|
}
|
|
})
|
|
})
|
|
|
|
it("ReadonlyMap(Option(Symbol), Date)", async () => {
|
|
const schema = Schema.ReadonlyMap(Schema.Option(Schema.Symbol), Schema.Date)
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(
|
|
new Map([[Option.some(Symbol.for("a")), new Date("2021-01-01")]]),
|
|
[[
|
|
{ _tag: "Some", value: "Symbol(a)" },
|
|
"2021-01-01T00:00:00.000Z"
|
|
]]
|
|
)
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(
|
|
[[{ _tag: "Some", value: "Symbol(a)" }, "2021-01-01T00:00:00.000Z"]],
|
|
new Map([[Option.some(Symbol.for("a")), new Date("2021-01-01")]])
|
|
)
|
|
})
|
|
|
|
it("Defect", async () => {
|
|
const schema = Schema.Defect()
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(new Error("a"), { name: "Error", message: "a" })
|
|
await encoding.succeed(
|
|
new Error("a", { cause: new Error("b") }),
|
|
{ name: "Error", message: "a", cause: { name: "Error", message: "b" } }
|
|
)
|
|
await encoding.succeed(
|
|
new Error("a", { cause: "b" }),
|
|
{ name: "Error", message: "a", cause: "b" }
|
|
)
|
|
await encoding.succeed(
|
|
new Cause.NoSuchElementError(),
|
|
{ name: "NoSuchElementError", message: "" }
|
|
)
|
|
const cyclicDefect: Record<string, unknown> = {}
|
|
cyclicDefect.self = cyclicDefect
|
|
await encoding.succeed(cyclicDefect, {})
|
|
await encoding.succeed("a")
|
|
await encoding.succeed({ a: 1 })
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(
|
|
{ message: "a", cause: { message: "b" } },
|
|
new Error("a", { cause: new Error("b") })
|
|
)
|
|
await decoding.succeed(
|
|
{ message: "a", cause: null },
|
|
new Error("a", { cause: null })
|
|
)
|
|
})
|
|
|
|
it("Defect with excluded cause", async () => {
|
|
const schema = Schema.Defect({ excludeCause: true })
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(
|
|
new Error("a", { cause: new Error("b") }),
|
|
{ name: "Error", message: "a" }
|
|
)
|
|
})
|
|
|
|
it("Cause(Option(Finite), Option(String))", async () => {
|
|
const schema = Schema.Cause(Schema.Option(Schema.Finite), Schema.Option(Schema.String))
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(Cause.fail(Option.some(1)), [{
|
|
_tag: "Fail",
|
|
error: { _tag: "Some", value: 1 }
|
|
}])
|
|
await encoding.succeed(Cause.die(Option.some("a")), [{
|
|
_tag: "Die",
|
|
defect: { _tag: "Some", value: "a" }
|
|
}])
|
|
await encoding.succeed(Cause.interrupt(1), [{
|
|
_tag: "Interrupt",
|
|
fiberId: 1
|
|
}])
|
|
await encoding.succeed(Cause.interrupt(), [{
|
|
_tag: "Interrupt",
|
|
fiberId: null
|
|
}])
|
|
})
|
|
|
|
it("DateTimeUtcFromValidDate", async () => {
|
|
const schema = Schema.DateTimeUtcFromDate
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(
|
|
DateTime.makeUnsafe("2021-01-01T00:00:00.000Z"),
|
|
"2021-01-01T00:00:00.000Z"
|
|
)
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(
|
|
"2021-01-01T00:00:00.000Z",
|
|
DateTime.makeUnsafe("2021-01-01T00:00:00.000Z")
|
|
)
|
|
})
|
|
})
|
|
|
|
it("StandardSchemaV1FailureResult", async () => {
|
|
const b = Symbol.for("b")
|
|
|
|
const schema = Schema.Struct({
|
|
a: Schema.NonEmptyString,
|
|
[b]: Schema.Finite,
|
|
c: Schema.Tuple([Schema.String])
|
|
})
|
|
|
|
const r = SchemaParser.decodeUnknownExit(schema)({ a: "", c: [] }, { errors: "all" })
|
|
|
|
assertTrue(r._tag === "Failure")
|
|
assertTrue(r.cause.reasons.length === 1)
|
|
const failure = r.cause.reasons[0]
|
|
assertTrue(failure._tag === "Fail")
|
|
|
|
const failureResult = SchemaIssue.makeFormatterStandardSchemaV1({
|
|
leafHook: SchemaIssue.defaultLeafHook
|
|
})(failure.error)
|
|
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(Schema.StandardSchemaV1FailureResult))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(failureResult, {
|
|
issues: [
|
|
{ path: ["a"], message: `Expected a value with a length of at least 1, got ""` },
|
|
{ path: ["c", 0], message: "Missing key" },
|
|
{ path: ["Symbol(b)"], message: "Missing key" }
|
|
]
|
|
})
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed({
|
|
issues: [
|
|
{ path: ["a"], message: `Expected a value with a length of at least 1, got ""` },
|
|
{ path: ["c", 0], message: "Missing key" },
|
|
{ path: ["Symbol(b)"], message: "Missing key" }
|
|
]
|
|
}, failureResult)
|
|
})
|
|
})
|
|
|
|
describe("toCodecStringTree", () => {
|
|
it("exposes the source schema", () => {
|
|
const schema = Schema.FiniteFromString
|
|
const serializer = Schema.toCodecStringTree(schema)
|
|
strictEqual(serializer.schema, schema)
|
|
})
|
|
|
|
it("should reorder the types in the Union based on the encoded side", async () => {
|
|
const schema = Schema.Union([
|
|
Schema.String,
|
|
Schema.String.pipe(Schema.encodeTo(Schema.BigInt, {
|
|
decode: SchemaGetter.transform((n: bigint) => String(n) + "a"),
|
|
encode: SchemaGetter.transform(() => 0n)
|
|
}))
|
|
])
|
|
const serializer = Schema.toCodecStringTree(schema)
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(serializer))
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed("1", "1a")
|
|
})
|
|
|
|
describe("should return the same reference if nothing changed", () => {
|
|
it("String", async () => {
|
|
const schema = Schema.String
|
|
const serializer = Schema.toCodecStringTree(schema)
|
|
strictEqual(serializer.ast, schema.ast)
|
|
})
|
|
|
|
it("Struct({ a: String })", async () => {
|
|
const schema = Schema.Struct({
|
|
a: Schema.String
|
|
})
|
|
const serializer = Schema.toCodecStringTree(schema)
|
|
strictEqual(serializer.ast, schema.ast)
|
|
})
|
|
})
|
|
|
|
describe("should memoize the result", () => {
|
|
it("Struct", async () => {
|
|
const schema = Schema.Struct({
|
|
a: Schema.Finite
|
|
})
|
|
const serializer = Schema.toCodecStringTree(schema)
|
|
strictEqual(serializer.ast, Schema.toCodecStringTree(serializer).ast)
|
|
})
|
|
|
|
it("Array", async () => {
|
|
const schema = Schema.Array(Schema.Finite)
|
|
const serializer = Schema.toCodecStringTree(schema)
|
|
strictEqual(serializer.ast, Schema.toCodecStringTree(serializer).ast)
|
|
})
|
|
})
|
|
|
|
describe("schemas without encoding", () => {
|
|
describe("Unsupported schemas", () => {
|
|
it("Struct with Symbol property name", () => {
|
|
const a = Symbol.for("a")
|
|
const schema = Schema.Struct({
|
|
[a]: Schema.String
|
|
})
|
|
throws(
|
|
() => Schema.toCodecStringTree(schema),
|
|
"Objects property names must be strings"
|
|
)
|
|
})
|
|
})
|
|
|
|
it("Declaration", async () => {
|
|
const schema = Schema.instanceOf(URL)
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(new URL("https://effect.website"), undefined)
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.fail("https://effect.website/", `Expected undefined, got "https://effect.website/"`)
|
|
})
|
|
|
|
it("Unknown", async () => {
|
|
const schema = Schema.Unknown
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed("a")
|
|
await encoding.fail(1, `Expected StringTree, got 1`)
|
|
await encoding.succeed({ a: "a" })
|
|
await encoding.succeed(["a"])
|
|
await encoding.fail({ a: 1 }, `Expected StringTree, got {"a":1}`)
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed("a")
|
|
await decoding.fail(1, `Expected StringTree, got 1`)
|
|
await decoding.succeed({ a: "a" })
|
|
await decoding.succeed(["a"])
|
|
await decoding.fail({ a: 1 }, `Expected StringTree, got {"a":1}`)
|
|
})
|
|
|
|
it("ObjectKeyword", async () => {
|
|
const schema = Schema.ObjectKeyword
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.fail("a", `Expected object | array | function, got "a"`)
|
|
await encoding.fail(1, `Expected object | array | function, got 1`)
|
|
await encoding.succeed({ a: "a" })
|
|
await encoding.succeed(["a"])
|
|
await encoding.fail({ a: 1 }, `Expected StringTree, got {"a":1}`)
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.fail("a", `Expected object | array | function, got "a"`)
|
|
await decoding.fail(1, `Expected StringTree, got 1`)
|
|
await decoding.succeed({ a: "a" })
|
|
await decoding.succeed(["a"])
|
|
await decoding.fail({ a: 1 }, `Expected StringTree, got {"a":1}`)
|
|
})
|
|
|
|
it("Never", async () => {
|
|
const schema = Schema.Never
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.fail({}, "Expected never, got {}")
|
|
})
|
|
|
|
it("Any should be an escape hatch", async () => {
|
|
const schema = Schema.Any
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(() => {})
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(() => {})
|
|
})
|
|
|
|
it("Undefined", async () => {
|
|
const schema = Schema.Undefined
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(undefined)
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(undefined)
|
|
})
|
|
|
|
it("Void", async () => {
|
|
const schema = Schema.Void
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(undefined)
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(undefined)
|
|
})
|
|
|
|
it("Null", async () => {
|
|
const schema = Schema.Null
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(null, "null")
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed("null", null)
|
|
})
|
|
|
|
it("String", async () => {
|
|
const schema = Schema.String
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed("a")
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed("a")
|
|
})
|
|
|
|
describe("Number", () => {
|
|
it("Number", async () => {
|
|
const schema = Schema.Number
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(1, "1")
|
|
await encoding.succeed(-1, "-1")
|
|
await encoding.succeed(1.2, "1.2")
|
|
await encoding.succeed(Infinity, "Infinity")
|
|
await encoding.succeed(-Infinity, "-Infinity")
|
|
await encoding.succeed(NaN, "NaN")
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed("1", 1)
|
|
await decoding.succeed("-1", -1)
|
|
await decoding.succeed("1.2", 1.2)
|
|
await decoding.succeed("Infinity", Infinity)
|
|
await decoding.succeed("-Infinity", -Infinity)
|
|
await decoding.succeed("NaN", NaN)
|
|
await decoding.fail(Infinity, `Expected string | "Infinity" | "-Infinity" | "NaN", got Infinity`)
|
|
await decoding.fail(-Infinity, `Expected string | "Infinity" | "-Infinity" | "NaN", got -Infinity`)
|
|
await decoding.fail(NaN, `Expected string | "Infinity" | "-Infinity" | "NaN", got NaN`)
|
|
await decoding.fail(null, `Expected string | "Infinity" | "-Infinity" | "NaN", got null`)
|
|
await decoding.fail(
|
|
"a",
|
|
`Expected a string representing a finite number, got "a"
|
|
Expected "Infinity" | "-Infinity" | "NaN", got "a"`
|
|
)
|
|
})
|
|
|
|
describe("checks", () => {
|
|
it("Finite", async () => {
|
|
const schema = Schema.Finite
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(1, "1")
|
|
await encoding.succeed(-1, "-1")
|
|
await encoding.succeed(1.2, "1.2")
|
|
await encoding.fail(Infinity, "Expected a finite number, got Infinity")
|
|
await encoding.fail(-Infinity, "Expected a finite number, got -Infinity")
|
|
await encoding.fail(NaN, "Expected a finite number, got NaN")
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed("1", 1)
|
|
await decoding.succeed("-1", -1)
|
|
await decoding.succeed("1.2", 1.2)
|
|
await decoding.fail("Infinity", `Expected a string representing a finite number, got "Infinity"`)
|
|
await decoding.fail("-Infinity", `Expected a string representing a finite number, got "-Infinity"`)
|
|
await decoding.fail("NaN", `Expected a string representing a finite number, got "NaN"`)
|
|
await decoding.fail(Infinity, `Expected string, got Infinity`)
|
|
await decoding.fail(-Infinity, `Expected string, got -Infinity`)
|
|
await decoding.fail(NaN, `Expected string, got NaN`)
|
|
await decoding.fail(null, `Expected string, got null`)
|
|
await decoding.fail("a", `Expected a string representing a finite number, got "a"`)
|
|
})
|
|
|
|
it("Int", async () => {
|
|
const schema = Schema.Int
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(1, "1")
|
|
await encoding.succeed(-1, "-1")
|
|
await encoding.fail(1.2, `Expected an integer, got 1.2`)
|
|
await encoding.fail(Infinity, `Expected an integer, got Infinity`)
|
|
await encoding.fail(-Infinity, `Expected an integer, got -Infinity`)
|
|
await encoding.fail(NaN, `Expected an integer, got NaN`)
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed("1", 1)
|
|
await decoding.succeed("-1", -1)
|
|
await decoding.fail("1.2", `Expected an integer, got 1.2`)
|
|
await decoding.fail("Infinity", `Expected a string representing a finite number, got "Infinity"`)
|
|
await decoding.fail("-Infinity", `Expected a string representing a finite number, got "-Infinity"`)
|
|
await decoding.fail("NaN", `Expected a string representing a finite number, got "NaN"`)
|
|
await decoding.fail(Infinity, `Expected string, got Infinity`)
|
|
await decoding.fail(-Infinity, `Expected string, got -Infinity`)
|
|
await decoding.fail(NaN, `Expected string, got NaN`)
|
|
await decoding.fail(null, `Expected string, got null`)
|
|
await decoding.fail("a", `Expected a string representing a finite number, got "a"`)
|
|
})
|
|
|
|
it("isGreaterThanOrEqualTo", async () => {
|
|
const schema = Schema.Number.check(Schema.isGreaterThanOrEqualTo(1))
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(1, "1")
|
|
await encoding.fail(-1, `Expected a value greater than or equal to 1, got -1`)
|
|
await encoding.succeed(1.2, "1.2")
|
|
await encoding.succeed(Infinity, "Infinity")
|
|
await encoding.fail(-Infinity, "Expected a value greater than or equal to 1, got -Infinity")
|
|
await encoding.fail(NaN, "Expected a value greater than or equal to 1, got NaN")
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed("1", 1)
|
|
await decoding.fail("-1", `Expected a value greater than or equal to 1, got -1`)
|
|
await decoding.succeed("1.2", 1.2)
|
|
await decoding.succeed("Infinity", Infinity)
|
|
await decoding.fail("-Infinity", `Expected a value greater than or equal to 1, got -Infinity`)
|
|
await decoding.fail("NaN", `Expected a value greater than or equal to 1, got NaN`)
|
|
await decoding.fail(Infinity, `Expected string | "Infinity" | "-Infinity" | "NaN", got Infinity`)
|
|
await decoding.fail(-Infinity, `Expected string | "Infinity" | "-Infinity" | "NaN", got -Infinity`)
|
|
await decoding.fail(NaN, `Expected string | "Infinity" | "-Infinity" | "NaN", got NaN`)
|
|
await decoding.fail(null, `Expected string | "Infinity" | "-Infinity" | "NaN", got null`)
|
|
await decoding.fail(
|
|
"a",
|
|
`Expected a string representing a finite number, got "a"
|
|
Expected "Infinity" | "-Infinity" | "NaN", got "a"`
|
|
)
|
|
})
|
|
})
|
|
})
|
|
|
|
it("Boolean", async () => {
|
|
const schema = Schema.Boolean
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(true, "true")
|
|
await encoding.succeed(false, "false")
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed("true", true)
|
|
await decoding.succeed("false", false)
|
|
})
|
|
|
|
it("Symbol", async () => {
|
|
const schema = Schema.Symbol
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(Symbol.for("a"), "Symbol(a)")
|
|
await encoding.fail(
|
|
Symbol("a"),
|
|
"cannot serialize to string, Symbol is not registered"
|
|
)
|
|
await encoding.fail(
|
|
Symbol(),
|
|
"cannot serialize to string, Symbol is not registered"
|
|
)
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed("Symbol(a)", Symbol.for("a"))
|
|
await decoding.fail("a", `Expected a string representing a symbol, got "a"`)
|
|
})
|
|
|
|
it("UniqueSymbol", async () => {
|
|
const schema = Schema.UniqueSymbol(Symbol.for("a"))
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(Symbol.for("a"), "Symbol(a)")
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed("Symbol(a)", Symbol.for("a"))
|
|
await decoding.fail("a", `Expected a string representing a symbol, got "a"`)
|
|
})
|
|
|
|
it("BigInt", async () => {
|
|
const schema = Schema.BigInt
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(1n, "1")
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed("1", 1n)
|
|
await decoding.fail("a", `Expected a string representing a bigint, got "a"`)
|
|
})
|
|
|
|
it("PropertyKey", async () => {
|
|
const schema = Schema.PropertyKey
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed("a", "a")
|
|
await encoding.succeed(1, "1")
|
|
await encoding.succeed(Symbol.for("a"), "Symbol(a)")
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed("a", "a")
|
|
await decoding.succeed("1", 1)
|
|
await decoding.succeed("Symbol(a)", Symbol.for("a"))
|
|
})
|
|
|
|
describe("Literal", () => {
|
|
it("string", async () => {
|
|
const schema = Schema.Literal("a")
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed("a", "a")
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed("a", "a")
|
|
})
|
|
|
|
it("number", async () => {
|
|
const schema = Schema.Literal(1)
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(1, "1")
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed("1", 1)
|
|
})
|
|
|
|
it("boolean", async () => {
|
|
const schema = Schema.Literal(true)
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(true, "true")
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed("true", true)
|
|
})
|
|
|
|
it("bigint", async () => {
|
|
const schema = Schema.Literal(1n)
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(1n, "1")
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed("1", 1n)
|
|
})
|
|
})
|
|
|
|
it("Literals", async () => {
|
|
const schema = Schema.Literals(["a", 1, 2n, true])
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.fail(
|
|
"-",
|
|
`Expected "a" | "1" | "2" | "true", got "-"`
|
|
)
|
|
})
|
|
|
|
describe("TemplateLiteral", () => {
|
|
it("1n + string", async () => {
|
|
const schema = Schema.TemplateLiteral([1n, Schema.String])
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed("1a")
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed("1a")
|
|
})
|
|
|
|
it(`"a" + bigint`, async () => {
|
|
const schema = Schema.TemplateLiteral(["a", Schema.BigInt])
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed("a1")
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed("a1")
|
|
})
|
|
})
|
|
|
|
it("Enum", async () => {
|
|
enum Fruits {
|
|
Apple,
|
|
Banana
|
|
}
|
|
const schema = Schema.Enum(Fruits)
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(Fruits.Apple, "0")
|
|
await encoding.succeed(Fruits.Banana, "1")
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed("0", Fruits.Apple)
|
|
await decoding.succeed("1", Fruits.Banana)
|
|
})
|
|
|
|
describe("Struct", () => {
|
|
it("Date", async () => {
|
|
const schema = Schema.Struct({
|
|
a: Schema.Date
|
|
})
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed({ a: new Date("2021-01-01") }, {
|
|
a: "2021-01-01T00:00:00.000Z"
|
|
})
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed({ a: "2021-01-01T00:00:00.000Z" }, {
|
|
a: new Date("2021-01-01")
|
|
})
|
|
})
|
|
|
|
it("UndefinedOr(Date)", async () => {
|
|
const schema = Schema.Struct({
|
|
a: Schema.UndefinedOr(Schema.Date)
|
|
})
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed({ a: new Date("2021-01-01") }, {
|
|
a: "2021-01-01T00:00:00.000Z"
|
|
})
|
|
await encoding.succeed({ a: undefined }, { a: undefined })
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed({ a: "2021-01-01T00:00:00.000Z" }, {
|
|
a: new Date("2021-01-01")
|
|
})
|
|
await decoding.succeed({ a: undefined }, { a: undefined })
|
|
})
|
|
|
|
it("NullOr(Date)", async () => {
|
|
const schema = Schema.Struct({
|
|
a: Schema.NullOr(Schema.Date)
|
|
})
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed({ a: new Date("2021-01-01") }, {
|
|
a: "2021-01-01T00:00:00.000Z"
|
|
})
|
|
await encoding.succeed({ a: null }, { a: "null" })
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed({ a: "2021-01-01T00:00:00.000Z" }, {
|
|
a: new Date("2021-01-01")
|
|
})
|
|
await decoding.succeed({ a: "null" }, { a: null })
|
|
})
|
|
|
|
it("optionalKey(Date)", async () => {
|
|
const schema = Schema.Struct({
|
|
a: Schema.optionalKey(Schema.Date)
|
|
})
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed({ a: new Date("2021-01-01") }, {
|
|
a: "2021-01-01T00:00:00.000Z"
|
|
})
|
|
await encoding.succeed({}, {})
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed({ a: "2021-01-01T00:00:00.000Z" }, {
|
|
a: new Date("2021-01-01")
|
|
})
|
|
await decoding.succeed({}, {})
|
|
})
|
|
|
|
it("optional(Date)", async () => {
|
|
const schema = Schema.Struct({
|
|
a: Schema.optional(Schema.Date)
|
|
})
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed({ a: new Date("2021-01-01") }, {
|
|
a: "2021-01-01T00:00:00.000Z"
|
|
})
|
|
await encoding.succeed({}, {})
|
|
await encoding.succeed({ a: undefined }, { a: undefined })
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed({ a: "2021-01-01T00:00:00.000Z" }, {
|
|
a: new Date("2021-01-01")
|
|
})
|
|
await decoding.succeed({}, {})
|
|
await decoding.succeed({ a: undefined }, { a: undefined })
|
|
})
|
|
})
|
|
|
|
it("Record(Symbol, Date)", async () => {
|
|
const schema = Schema.Record(Schema.Symbol, Schema.Date)
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(
|
|
{ [Symbol.for("a")]: new Date("2021-01-01"), [Symbol.for("b")]: new Date("2021-01-01") },
|
|
{ "Symbol(a)": "2021-01-01T00:00:00.000Z", "Symbol(b)": "2021-01-01T00:00:00.000Z" }
|
|
)
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(
|
|
{ "Symbol(a)": "2021-01-01T00:00:00.000Z", "Symbol(b)": "2021-01-01T00:00:00.000Z" },
|
|
{ [Symbol.for("a")]: new Date("2021-01-01"), [Symbol.for("b")]: new Date("2021-01-01") }
|
|
)
|
|
})
|
|
|
|
describe("Tuple", () => {
|
|
it("Date", async () => {
|
|
const schema = Schema.Tuple([Schema.Date])
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(
|
|
[new Date("2021-01-01")],
|
|
["2021-01-01T00:00:00.000Z"]
|
|
)
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(
|
|
["2021-01-01T00:00:00.000Z"],
|
|
[new Date("2021-01-01")]
|
|
)
|
|
})
|
|
|
|
it("UndefinedOr(Date)", async () => {
|
|
const schema = Schema.Tuple([Schema.UndefinedOr(Schema.Date)])
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(
|
|
[new Date("2021-01-01")],
|
|
["2021-01-01T00:00:00.000Z"]
|
|
)
|
|
await encoding.succeed([undefined])
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(["2021-01-01T00:00:00.000Z"], [
|
|
new Date("2021-01-01")
|
|
])
|
|
await decoding.succeed([undefined])
|
|
})
|
|
|
|
it("NullOr(Date)", async () => {
|
|
const schema = Schema.Tuple([Schema.NullOr(Schema.Date)])
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed([new Date("2021-01-01")], [
|
|
"2021-01-01T00:00:00.000Z"
|
|
])
|
|
await encoding.succeed([null], ["null"])
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(["2021-01-01T00:00:00.000Z"], [
|
|
new Date("2021-01-01")
|
|
])
|
|
await decoding.succeed(["null"], [null])
|
|
})
|
|
|
|
it("optionalKey(Date)", async () => {
|
|
const schema = Schema.Tuple([Schema.optionalKey(Schema.Date)])
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed([new Date("2021-01-01")], [
|
|
"2021-01-01T00:00:00.000Z"
|
|
])
|
|
await encoding.succeed([], [])
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(["2021-01-01T00:00:00.000Z"], [
|
|
new Date("2021-01-01")
|
|
])
|
|
await decoding.succeed([], [])
|
|
})
|
|
|
|
it("optional(Date)", async () => {
|
|
const schema = Schema.Tuple([Schema.optional(Schema.Date)])
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed([new Date("2021-01-01")], [
|
|
"2021-01-01T00:00:00.000Z"
|
|
])
|
|
await encoding.succeed([], [])
|
|
await encoding.succeed([undefined])
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(["2021-01-01T00:00:00.000Z"], [
|
|
new Date("2021-01-01")
|
|
])
|
|
await decoding.succeed([], [])
|
|
await decoding.succeed([undefined])
|
|
})
|
|
})
|
|
|
|
it("Array(Date)", async () => {
|
|
const schema = Schema.Array(Schema.Date)
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(
|
|
[new Date("2021-01-01"), new Date("2021-01-02")],
|
|
["2021-01-01T00:00:00.000Z", "2021-01-02T00:00:00.000Z"]
|
|
)
|
|
})
|
|
|
|
it("Array(Finite) preserves the top-level AST", async () => {
|
|
const serializer = Schema.toCodecStringTree(Schema.Array(Schema.Finite))
|
|
strictEqual(serializer.ast._tag, "Arrays")
|
|
|
|
const asserts = new TestSchema.Asserts(serializer)
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed([1, 2], ["1", "2"])
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.fail("1,2", `Expected array, got "1,2"`)
|
|
await decoding.succeed(["1", "2"], [1, 2])
|
|
})
|
|
|
|
describe("Union", () => {
|
|
it("NullOr(Date)", async () => {
|
|
const schema = Schema.NullOr(Schema.String)
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed("a", "a")
|
|
await encoding.succeed(null, "null")
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed("null", null)
|
|
await decoding.succeed("a", "a")
|
|
})
|
|
|
|
it("NullOr(Number)", async () => {
|
|
const schema = Schema.NullOr(Schema.Number)
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(1, "1")
|
|
await encoding.succeed(null, "null")
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed("null", null)
|
|
await decoding.succeed("1", 1)
|
|
})
|
|
|
|
it("Array(NullOr(Number))", async () => {
|
|
const schema = Schema.Array(Schema.NullOr(Schema.Number))
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed([1, null], ["1", "null"])
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(["1", "null"], [1, null])
|
|
})
|
|
|
|
it("Union(Date, Finite)", async () => {
|
|
const schema = Schema.Union([Schema.Date, Schema.Finite])
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(
|
|
new Date("2021-01-01"),
|
|
"2021-01-01T00:00:00.000Z"
|
|
)
|
|
await encoding.succeed(0, "0")
|
|
})
|
|
})
|
|
|
|
it("Suspend", async () => {
|
|
interface Category {
|
|
readonly a: number
|
|
readonly categories: ReadonlyArray<Category>
|
|
}
|
|
|
|
const schema = Schema.Struct({
|
|
a: Schema.Finite.check(Schema.isGreaterThan(0)),
|
|
categories: Schema.Array(Schema.suspend((): Schema.Codec<Category> => schema))
|
|
})
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed({ a: 1, categories: [] }, {
|
|
a: "1",
|
|
categories: []
|
|
})
|
|
await encoding.succeed({
|
|
a: 1,
|
|
categories: [{ a: 2, categories: [] }]
|
|
}, {
|
|
a: "1",
|
|
categories: [
|
|
{ a: "2", categories: [] }
|
|
]
|
|
})
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed({
|
|
a: "1",
|
|
categories: []
|
|
}, { a: 1, categories: [] })
|
|
await decoding.succeed({
|
|
a: "1",
|
|
categories: [
|
|
{ a: "2", categories: [] }
|
|
]
|
|
}, { a: 1, categories: [{ a: 2, categories: [] }] })
|
|
})
|
|
|
|
it("Class", async () => {
|
|
class A extends Schema.Class<A>("A")(Schema.Struct({
|
|
a: Schema.Finite
|
|
})) {}
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(Schema.toType(A)))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(new A({ a: 0 }), { a: "0" })
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed({ a: "0" }, new A({ a: 0 }))
|
|
})
|
|
|
|
it("ErrorClass", async () => {
|
|
class E extends Schema.ErrorClass<E>("E")({
|
|
a: Schema.Finite
|
|
}) {}
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(Schema.toType(E)))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(new E({ a: 0 }), { a: "0" })
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed({ a: "0" }, new E({ a: 0 }))
|
|
})
|
|
|
|
it("Date", async () => {
|
|
const schema = Schema.Date
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(
|
|
new Date("2021-01-01"),
|
|
"2021-01-01T00:00:00.000Z"
|
|
)
|
|
})
|
|
|
|
it("Error", async () => {
|
|
const schema = Schema.Error()
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(
|
|
new Error("a"),
|
|
{ name: "Error", message: "a" }
|
|
)
|
|
|
|
const decoding = asserts.decoding()
|
|
// Error: message only
|
|
await decoding.succeed(
|
|
{ message: "a" },
|
|
new Error("a")
|
|
)
|
|
// Error: message and name
|
|
await decoding.succeed(
|
|
{ name: "b", message: "a" },
|
|
(() => {
|
|
const err = new Error("a")
|
|
err.name = "b"
|
|
return err
|
|
})()
|
|
)
|
|
// Error: message, name, and stack
|
|
await decoding.succeed(
|
|
{ name: "b", message: "a", stack: "c" },
|
|
(() => {
|
|
const err = new Error("a")
|
|
err.name = "b"
|
|
err.stack = "c"
|
|
return err
|
|
})()
|
|
)
|
|
})
|
|
|
|
it("URL", async () => {
|
|
const schema = Schema.URL
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(
|
|
new URL("https://example.com"),
|
|
"https://example.com/"
|
|
)
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(
|
|
"https://example.com",
|
|
new URL("https://example.com")
|
|
)
|
|
await decoding.succeed(
|
|
"https://example.com/",
|
|
new URL("https://example.com")
|
|
)
|
|
await decoding.fail(
|
|
"not a url",
|
|
`Invalid URL string: not a url`
|
|
)
|
|
})
|
|
|
|
it("RegExp", async () => {
|
|
const schema = Schema.RegExp
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(new RegExp("a"), { source: "a", flags: "" })
|
|
await encoding.succeed(new RegExp("a", "i"), { source: "a", flags: "i" })
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed({ source: "a", flags: "" }, new RegExp("a"))
|
|
await decoding.succeed({ source: "a", flags: "i" }, new RegExp("a", "i"))
|
|
await decoding.fail(
|
|
{ source: "a", flags: "x" },
|
|
`SyntaxError: Invalid flags supplied to RegExp constructor 'x'`
|
|
)
|
|
})
|
|
|
|
it("Option(Date)", async () => {
|
|
const schema = Schema.Option(Schema.Date)
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(Option.some(new Date("2021-01-01")), {
|
|
_tag: "Some",
|
|
value: "2021-01-01T00:00:00.000Z"
|
|
})
|
|
await encoding.succeed(Option.none(), { _tag: "None" })
|
|
})
|
|
|
|
it("Redacted(Option(String))", async () => {
|
|
const schema = Schema.Redacted(Schema.Option(Schema.String))
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(
|
|
Redacted.make(Option.none()),
|
|
{ _tag: "None" }
|
|
)
|
|
await encoding.succeed(
|
|
Redacted.make(Option.some("a")),
|
|
{ _tag: "Some", value: "a" }
|
|
)
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(
|
|
{ _tag: "None" },
|
|
Redacted.make(Option.none())
|
|
)
|
|
await decoding.succeed(
|
|
{ _tag: "Some", value: "a" },
|
|
Redacted.make(Option.some("a"))
|
|
)
|
|
})
|
|
|
|
it("Redacted(Option(String)) disallowJsonEncode", async () => {
|
|
const schema = Schema.Redacted(Schema.Option(Schema.String), {
|
|
disallowJsonEncode: true
|
|
})
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.fail(
|
|
Redacted.make(Option.none()),
|
|
"Cannot serialize Redacted"
|
|
)
|
|
await encoding.fail(
|
|
Redacted.make(Option.some("a")),
|
|
"Cannot serialize Redacted"
|
|
)
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(
|
|
{ _tag: "None" },
|
|
Redacted.make(Option.none())
|
|
)
|
|
await decoding.succeed(
|
|
{ _tag: "Some", value: "a" },
|
|
Redacted.make(Option.some("a"))
|
|
)
|
|
})
|
|
|
|
it("ReadonlySet", async () => {
|
|
const schema = Schema.ReadonlySet(Schema.Option(Schema.Date))
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecJson(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(new Set([Option.some(new Date("2021-01-01"))]), [{
|
|
_tag: "Some",
|
|
value: "2021-01-01T00:00:00.000Z"
|
|
}])
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(
|
|
[{ _tag: "Some", value: "2021-01-01T00:00:00.000Z" }],
|
|
new Set([Option.some(new Date("2021-01-01"))])
|
|
)
|
|
})
|
|
|
|
it("ReadonlyMap", async () => {
|
|
const schema = Schema.ReadonlyMap(Schema.Option(Schema.Date), Schema.Finite)
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(
|
|
new Map([[Option.some(new Date("2021-01-01")), 0]]),
|
|
[[
|
|
{ _tag: "Some", value: "2021-01-01T00:00:00.000Z" },
|
|
"0"
|
|
]]
|
|
)
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(
|
|
[[{ _tag: "Some", value: "2021-01-01T00:00:00.000Z" }, "0"]],
|
|
new Map([[Option.some(new Date("2021-01-01")), 0]])
|
|
)
|
|
})
|
|
|
|
it("HashMap", async () => {
|
|
const schema = Schema.HashMap(Schema.Option(Schema.Date), Schema.Finite)
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(schema))
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed(
|
|
HashMap.make([Option.some(new Date("2021-01-01")), 0]),
|
|
[[
|
|
{ _tag: "Some", value: "2021-01-01T00:00:00.000Z" },
|
|
"0"
|
|
]]
|
|
)
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed(
|
|
[[{ _tag: "Some", value: "2021-01-01T00:00:00.000Z" }, "0"]],
|
|
HashMap.make([Option.some(new Date("2021-01-01")), 0])
|
|
)
|
|
})
|
|
})
|
|
|
|
it("should handle optional keys", async () => {
|
|
const schema = Schema.Struct({
|
|
a: Schema.optionalKey(Schema.NonEmptyArray(Schema.String))
|
|
})
|
|
const serializer = Schema.toCodecStringTree(schema)
|
|
const asserts = new TestSchema.Asserts(Schema.toCodecStringTree(serializer))
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed({})
|
|
await decoding.succeed({ a: ["a"] })
|
|
await decoding.fail({ a: "a" }, `Expected array, got "a"\n at ["a"]`)
|
|
})
|
|
})
|
|
|
|
describe("toCodecArrayFromSingle", () => {
|
|
it("accepts string and array inputs for a top-level array", async () => {
|
|
const serializer = Schema.toCodecArrayFromSingle(Schema.toCodecStringTree(Schema.Array(Schema.Finite)))
|
|
strictEqual(serializer.ast._tag, "Arrays")
|
|
|
|
const asserts = new TestSchema.Asserts(serializer)
|
|
|
|
const encoding = asserts.encoding()
|
|
await encoding.succeed([1, 2], ["1", "2"])
|
|
await encoding.fail(1 as any, "Expected array, got 1")
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed("1", [1])
|
|
await decoding.fail("1,2", `Expected a string representing a finite number, got "1,2"\n at [0]`)
|
|
await decoding.succeed(["1", "2"], [1, 2])
|
|
})
|
|
|
|
it("accepts string and array inputs for required array fields", async () => {
|
|
const schema = Schema.toCodecArrayFromSingle(Schema.toCodecStringTree(Schema.Struct({
|
|
a: Schema.Array(Schema.Finite)
|
|
})))
|
|
const asserts = new TestSchema.Asserts(schema)
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed({ a: "1" }, { a: [1] })
|
|
await decoding.succeed({ a: ["1", "2"] }, { a: [1, 2] })
|
|
})
|
|
|
|
it("accepts string and array inputs for nested optional array fields", async () => {
|
|
const schema = Schema.toCodecArrayFromSingle(Schema.toCodecStringTree(Schema.Struct({
|
|
a: Schema.optionalKey(Schema.NonEmptyArray(Schema.String))
|
|
})))
|
|
const asserts = new TestSchema.Asserts(schema)
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed({})
|
|
await decoding.succeed({ a: ["a"] })
|
|
await decoding.succeed({ a: "a" }, { a: ["a"] })
|
|
})
|
|
|
|
it("accepts string and array inputs for tuples with optional elements", async () => {
|
|
const schema = Schema.toCodecArrayFromSingle(Schema.toCodecStringTree(Schema.Tuple([
|
|
Schema.optionalKey(Schema.String)
|
|
])))
|
|
const asserts = new TestSchema.Asserts(schema)
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed([])
|
|
await decoding.succeed("a", ["a"])
|
|
await decoding.succeed(["a"])
|
|
})
|
|
|
|
it("applies recursively to nested arrays", async () => {
|
|
const schema = Schema.toCodecArrayFromSingle(Schema.toCodecStringTree(Schema.Array(Schema.Array(Schema.Finite))))
|
|
const asserts = new TestSchema.Asserts(schema)
|
|
|
|
const decoding = asserts.decoding()
|
|
await decoding.succeed("1", [[1]])
|
|
await decoding.succeed(["1", "2"], [[1], [2]])
|
|
await decoding.succeed([["1", "2"]], [[1, 2]])
|
|
})
|
|
|
|
it("is idempotent", () => {
|
|
const schema = Schema.toCodecArrayFromSingle(Schema.toCodecStringTree(Schema.Array(Schema.Finite)))
|
|
strictEqual(schema.ast, Schema.toCodecArrayFromSingle(schema).ast)
|
|
})
|
|
})
|
|
|
|
describe("toEncoderXml", () => {
|
|
async function assertXml<T, E, RD>(schema: Schema.Codec<T, E, RD>, value: T, expected: string) {
|
|
const serializer = Schema.toEncoderXml(Schema.toCodecStringTree(schema))
|
|
strictEqual(await Effect.runPromise(serializer(value)), expected)
|
|
}
|
|
|
|
async function assertXmlFailure<T, E, RD>(schema: Schema.Codec<T, E, RD>, value: T, message: string) {
|
|
const serializer = Schema.toEncoderXml(Schema.toCodecStringTree(schema))
|
|
const r = await serializer(value).pipe(
|
|
Effect.mapError((err) => err.issue.toString()),
|
|
Effect.result,
|
|
Effect.runPromise
|
|
)
|
|
deepStrictEqual(r, Result.fail(message))
|
|
}
|
|
|
|
describe("Schemas without annotations", () => {
|
|
it("Declaration", async () => {
|
|
await assertXml(Schema.instanceOf(URL), new URL("https://effect.website"), "<root/>")
|
|
})
|
|
|
|
it("Unknown", async () => {
|
|
await assertXml(Schema.Unknown, "value", "<root/>")
|
|
})
|
|
|
|
it("ObjectKeyword", async () => {
|
|
await assertXml(Schema.ObjectKeyword, { a: "value" }, "<root/>")
|
|
})
|
|
})
|
|
|
|
it("should use the identifier as the root name", async () => {
|
|
await assertXml(Schema.String.annotate({ identifier: "a" }), "value", "<a>value</a>")
|
|
await assertXml(Schema.String.annotate({ identifier: "a b" }), "value", `<a_b data-name="a b">value</a_b>`)
|
|
await assertXml(Schema.String.annotate({ identifier: "a", title: "b" }), "value", "<a>value</a>")
|
|
})
|
|
|
|
it("should use the title as the root name", async () => {
|
|
await assertXml(Schema.String.annotate({ title: "a" }), "value", "<a>value</a>")
|
|
await assertXml(Schema.String.annotate({ title: "a b" }), "value", `<a_b data-name="a b">value</a_b>`)
|
|
})
|
|
|
|
it("should escape the text", async () => {
|
|
await assertXml(Schema.String, "value&", `<root>value&</root>`)
|
|
await assertXml(Schema.String, "<value/>", `<root><value/></root>`)
|
|
})
|
|
|
|
it("should escape the attributes", async () => {
|
|
await assertXml(Schema.String.annotate({ title: "value&" }), "", `<value_ data-name="value&"></value_>`)
|
|
await assertXml(
|
|
Schema.String.annotate({ title: "<value/>" }),
|
|
"",
|
|
`<__value__ data-name="<value/>"></__value__>`
|
|
)
|
|
})
|
|
|
|
it("Never", async () => {
|
|
await assertXmlFailure(Schema.Never, "test", `Expected never, got "test"`)
|
|
})
|
|
|
|
it("Any", async () => {
|
|
await assertXml(Schema.Any, "test", "<root>test</root>")
|
|
await assertXml(Schema.Any, 42, "<root>42</root>")
|
|
await assertXml(
|
|
Schema.Any,
|
|
{ a: 1 },
|
|
`<root>
|
|
<a>1</a>
|
|
</root>`
|
|
)
|
|
})
|
|
|
|
it("Void", async () => {
|
|
await assertXml(Schema.Void, undefined, "<root/>")
|
|
})
|
|
|
|
it("Undefined", async () => {
|
|
await assertXml(Schema.Undefined, undefined, "<root/>")
|
|
})
|
|
|
|
it("Null", async () => {
|
|
await assertXml(Schema.Null, null, "<root>null</root>")
|
|
})
|
|
|
|
it("Number", async () => {
|
|
await assertXml(Schema.Number, 1, "<root>1</root>")
|
|
await assertXml(Schema.Number, 0, "<root>0</root>")
|
|
await assertXml(Schema.Number, -1.5, "<root>-1.5</root>")
|
|
await assertXml(Schema.Number, Infinity, "<root>Infinity</root>")
|
|
await assertXml(Schema.Number, -Infinity, "<root>-Infinity</root>")
|
|
await assertXml(Schema.Number, NaN, "<root>NaN</root>")
|
|
})
|
|
|
|
it("Boolean", async () => {
|
|
await assertXml(Schema.Boolean, true, "<root>true</root>")
|
|
await assertXml(Schema.Boolean, false, "<root>false</root>")
|
|
})
|
|
|
|
it("BigInt", async () => {
|
|
await assertXml(Schema.BigInt, BigInt(42), "<root>42</root>")
|
|
await assertXml(Schema.BigInt, BigInt(0), "<root>0</root>")
|
|
await assertXml(Schema.BigInt, BigInt(-123), "<root>-123</root>")
|
|
})
|
|
|
|
it("Symbol", async () => {
|
|
const sym = Symbol.for("test")
|
|
await assertXml(Schema.Symbol, sym, "<root>Symbol(test)</root>")
|
|
})
|
|
|
|
it("TemplateLiteral", async () => {
|
|
const schema = Schema.TemplateLiteral([Schema.Literal("Hello "), Schema.String, Schema.Literal("!")])
|
|
await assertXml(schema, "Hello World!", "<root>Hello World!</root>")
|
|
})
|
|
|
|
it("Struct", async () => {
|
|
await assertXml(
|
|
Schema.Struct({
|
|
a: Schema.Number,
|
|
"a b": Schema.Number
|
|
}),
|
|
{ a: 1, "a b": 2 },
|
|
`<root>
|
|
<a>1</a>
|
|
<a_b data-name="a b">2</a_b>
|
|
</root>`
|
|
)
|
|
})
|
|
|
|
it("Array", async () => {
|
|
await assertXml(Schema.Array(Schema.Number), [], "<root/>")
|
|
await assertXml(
|
|
Schema.Array(Schema.Number),
|
|
[1, 2, 3],
|
|
`<root>
|
|
<item>1</item>
|
|
<item>2</item>
|
|
<item>3</item>
|
|
</root>`
|
|
)
|
|
})
|
|
|
|
it("Array with custom item name", async () => {
|
|
const serializer = Schema.toEncoderXml(Schema.toCodecStringTree(Schema.Array(Schema.Number)), {
|
|
arrayItemName: "number"
|
|
})
|
|
strictEqual(
|
|
await Effect.runPromise(serializer([1, 2, 3])),
|
|
`<root>
|
|
<number>1</number>
|
|
<number>2</number>
|
|
<number>3</number>
|
|
</root>`
|
|
)
|
|
})
|
|
|
|
it("Union", async () => {
|
|
await assertXml(Schema.Union([Schema.String, Schema.Number]), "test", "<root>test</root>")
|
|
await assertXml(Schema.Union([Schema.String, Schema.Number]), 42, "<root>42</root>")
|
|
})
|
|
|
|
it("Tuple", async () => {
|
|
await assertXml(Schema.Tuple([]), [], "<root/>")
|
|
await assertXml(
|
|
Schema.Tuple([Schema.String, Schema.Number, Schema.Boolean]),
|
|
["a", 1, true],
|
|
`<root>
|
|
<item>a</item>
|
|
<item>1</item>
|
|
<item>true</item>
|
|
</root>`
|
|
)
|
|
})
|
|
|
|
it("Record", async () => {
|
|
await assertXml(Schema.Record(Schema.String, Schema.Number), {}, "<root/>")
|
|
await assertXml(
|
|
Schema.Record(Schema.String, Schema.Number),
|
|
{ a: 1, b: 2 },
|
|
`<root>
|
|
<a>1</a>
|
|
<b>2</b>
|
|
</root>`
|
|
)
|
|
})
|
|
|
|
it("NullOr", async () => {
|
|
await assertXml(Schema.NullOr(Schema.String), "test", "<root>test</root>")
|
|
await assertXml(Schema.NullOr(Schema.String), null, "<root>null</root>")
|
|
})
|
|
|
|
it("TaggedUnion", async () => {
|
|
const schema = Schema.TaggedUnion({
|
|
A: { value: Schema.String },
|
|
B: { value: Schema.Number }
|
|
})
|
|
await assertXml(
|
|
schema,
|
|
{ _tag: "A", value: "test" },
|
|
`<root>
|
|
<_tag>A</_tag>
|
|
<value>test</value>
|
|
</root>`
|
|
)
|
|
await assertXml(
|
|
schema,
|
|
{ _tag: "B", value: 42 },
|
|
`<root>
|
|
<_tag>B</_tag>
|
|
<value>42</value>
|
|
</root>`
|
|
)
|
|
})
|
|
|
|
it("TaggedStruct", async () => {
|
|
const schema = Schema.TaggedStruct("User", {
|
|
name: Schema.String,
|
|
age: Schema.Number
|
|
})
|
|
await assertXml(
|
|
schema,
|
|
{ _tag: "User", name: "John", age: 30 },
|
|
`<root>
|
|
<_tag>User</_tag>
|
|
<age>30</age>
|
|
<name>John</name>
|
|
</root>`
|
|
)
|
|
})
|
|
|
|
it("Enum", async () => {
|
|
const schema = Schema.Enum({
|
|
A: "a",
|
|
B: "b"
|
|
})
|
|
await assertXml(schema, "a", "<root>a</root>")
|
|
await assertXml(schema, "b", "<root>b</root>")
|
|
})
|
|
|
|
it("Literals", async () => {
|
|
const schema = Schema.Literals(["a", 1, true, 1n])
|
|
await assertXml(schema, "a", "<root>a</root>")
|
|
await assertXml(schema, 1, "<root>1</root>")
|
|
await assertXml(schema, true, "<root>true</root>")
|
|
await assertXml(schema, 1n, "<root>1</root>")
|
|
})
|
|
|
|
it("Nested Structures", async () => {
|
|
const schema = Schema.Struct({
|
|
user: Schema.Struct({
|
|
name: Schema.String,
|
|
age: Schema.Number
|
|
}),
|
|
tags: Schema.Array(Schema.String)
|
|
})
|
|
await assertXml(
|
|
schema,
|
|
{ user: { name: "John", age: 30 }, tags: ["admin", "user"] },
|
|
`<root>
|
|
<tags>
|
|
<item>admin</item>
|
|
<item>user</item>
|
|
</tags>
|
|
<user>
|
|
<age>30</age>
|
|
<name>John</name>
|
|
</user>
|
|
</root>`
|
|
)
|
|
})
|
|
|
|
it("Special Characters in Text", async () => {
|
|
await assertXml(Schema.String, "&<>\"'", `<root>&<>"'</root>`)
|
|
await assertXml(
|
|
Schema.String,
|
|
"line1\nline2",
|
|
`<root>line1
|
|
line2</root>`
|
|
)
|
|
await assertXml(Schema.String, "tab\there", "<root>tab here</root>")
|
|
})
|
|
|
|
it("Special Characters in Attributes", async () => {
|
|
await assertXml(
|
|
Schema.String.annotate({ title: "test&value" }),
|
|
"content",
|
|
`<test_value data-name="test&value">content</test_value>`
|
|
)
|
|
await assertXml(
|
|
Schema.String.annotate({ title: "test<value>" }),
|
|
"content",
|
|
`<test_value_ data-name="test<value>">content</test_value_>`
|
|
)
|
|
await assertXml(
|
|
Schema.String.annotate({ title: "test\"value" }),
|
|
"content",
|
|
`<test_value data-name="test"value">content</test_value>`
|
|
)
|
|
})
|
|
|
|
it("XML Reserved Names", async () => {
|
|
await assertXml(
|
|
Schema.String.annotate({ title: "xml" }),
|
|
"content",
|
|
`<_xml data-name="xml">content</_xml>`
|
|
)
|
|
await assertXml(
|
|
Schema.String.annotate({ title: "XML" }),
|
|
"content",
|
|
`<_XML data-name="XML">content</_XML>`
|
|
)
|
|
await assertXml(
|
|
Schema.String.annotate({ title: "xmlns" }),
|
|
"content",
|
|
`<_xmlns data-name="xmlns">content</_xmlns>`
|
|
)
|
|
})
|
|
|
|
it("Invalid XML Tag Names", async () => {
|
|
await assertXml(
|
|
Schema.String.annotate({ title: "123invalid" }),
|
|
"content",
|
|
`<_123invalid data-name="123invalid">content</_123invalid>`
|
|
)
|
|
await assertXml(
|
|
Schema.String.annotate({ title: "invalid name" }),
|
|
"content",
|
|
`<invalid_name data-name="invalid name">content</invalid_name>`
|
|
)
|
|
})
|
|
|
|
it("Empty String", async () => {
|
|
await assertXml(Schema.String, "", "<root></root>")
|
|
})
|
|
|
|
it("Whitespace Only String", async () => {
|
|
await assertXml(Schema.String, " ", "<root> </root>")
|
|
await assertXml(Schema.String, "\n\t", "<root>\n\t</root>")
|
|
})
|
|
|
|
it("Unicode Characters", async () => {
|
|
await assertXml(Schema.String, "Hello 世界", "<root>Hello 世界</root>")
|
|
await assertXml(Schema.String, "🚀🌟✨", "<root>🚀🌟✨</root>")
|
|
await assertXml(Schema.String, "αβγδε", "<root>αβγδε</root>")
|
|
})
|
|
|
|
it("XML Encoder Options - rootName", async () => {
|
|
const serializer = Schema.toEncoderXml(Schema.toCodecStringTree(Schema.String), {
|
|
rootName: "custom"
|
|
})
|
|
strictEqual(await Effect.runPromise(serializer("test")), "<custom>test</custom>")
|
|
})
|
|
|
|
it("XML Encoder Options - pretty: false", async () => {
|
|
const serializer = Schema.toEncoderXml(
|
|
Schema.toCodecStringTree(Schema.Struct({
|
|
a: Schema.Number,
|
|
b: Schema.String
|
|
})),
|
|
{
|
|
pretty: false
|
|
}
|
|
)
|
|
strictEqual(await Effect.runPromise(serializer({ a: 1, b: "test" })), "<root><a>1</a><b>test</b></root>")
|
|
})
|
|
|
|
it("XML Encoder Options - custom indent", async () => {
|
|
const serializer = Schema.toEncoderXml(
|
|
Schema.toCodecStringTree(Schema.Struct({
|
|
a: Schema.Number
|
|
})),
|
|
{
|
|
indent: " "
|
|
}
|
|
)
|
|
strictEqual(
|
|
await Effect.runPromise(serializer({ a: 1 })),
|
|
`<root>
|
|
<a>1</a>
|
|
</root>`
|
|
)
|
|
})
|
|
|
|
it("XML Encoder Options - sortKeys: false", async () => {
|
|
const serializer = Schema.toEncoderXml(
|
|
Schema.toCodecStringTree(Schema.Struct({
|
|
z: Schema.Number,
|
|
a: Schema.Number,
|
|
m: Schema.Number
|
|
})),
|
|
{
|
|
sortKeys: false
|
|
}
|
|
)
|
|
strictEqual(
|
|
await Effect.runPromise(serializer({ z: 3, a: 1, m: 2 })),
|
|
`<root>
|
|
<z>3</z>
|
|
<a>1</a>
|
|
<m>2</m>
|
|
</root>`
|
|
)
|
|
})
|
|
|
|
it("Circular Reference Detection", async () => {
|
|
const obj: any = { name: "test" }
|
|
obj.self = obj
|
|
|
|
const serializer = Schema.toEncoderXml(Schema.toCodecStringTree(Schema.Any))
|
|
try {
|
|
await Effect.runPromise(serializer(obj))
|
|
throw new Error("Expected error")
|
|
} catch (error: any) {
|
|
strictEqual(error.message, "Cycle detected while serializing to XML.")
|
|
}
|
|
})
|
|
|
|
it("Nested Arrays", async () => {
|
|
const schema = Schema.Array(Schema.Array(Schema.Number))
|
|
await assertXml(
|
|
schema,
|
|
[[1, 2], [3, 4]],
|
|
`<root>
|
|
<item>
|
|
<item>1</item>
|
|
<item>2</item>
|
|
</item>
|
|
<item>
|
|
<item>3</item>
|
|
<item>4</item>
|
|
</item>
|
|
</root>`
|
|
)
|
|
})
|
|
|
|
it("Record with Number Keys", async () => {
|
|
const schema = Schema.Record(Schema.Number, Schema.String)
|
|
await assertXml(
|
|
schema,
|
|
{ 1: "one", 2: "two" },
|
|
`<root>
|
|
<_1 data-name="1">one</_1>
|
|
<_2 data-name="2">two</_2>
|
|
</root>`
|
|
)
|
|
})
|
|
|
|
it("Record with Symbol Keys", async () => {
|
|
const sym1 = Symbol.for("key1")
|
|
const sym2 = Symbol.for("key2")
|
|
const schema = Schema.Record(Schema.Symbol, Schema.String)
|
|
await assertXml(
|
|
schema,
|
|
{ [sym1]: "value1", [sym2]: "value2" },
|
|
`<root>
|
|
<Symbol_key1_ data-name="Symbol(key1)">value1</Symbol_key1_>
|
|
<Symbol_key2_ data-name="Symbol(key2)">value2</Symbol_key2_>
|
|
</root>`
|
|
)
|
|
})
|
|
|
|
it("Tuple with Rest", async () => {
|
|
const schema = Schema.TupleWithRest(Schema.Tuple([Schema.String, Schema.Number]), [Schema.Boolean])
|
|
await assertXml(
|
|
schema,
|
|
["test", 42, true, false],
|
|
`<root>
|
|
<item>test</item>
|
|
<item>42</item>
|
|
<item>true</item>
|
|
<item>false</item>
|
|
</root>`
|
|
)
|
|
})
|
|
|
|
it("Suspend (Recursive Types)", async () => {
|
|
interface Tree {
|
|
readonly value: number
|
|
readonly children: ReadonlyArray<Tree>
|
|
}
|
|
|
|
const Tree: Schema.Codec<Tree> = Schema.Struct({
|
|
value: Schema.Number,
|
|
children: Schema.Array(Schema.suspend(() => Tree))
|
|
})
|
|
|
|
const tree: Tree = {
|
|
value: 1,
|
|
children: [
|
|
{ value: 2, children: [] },
|
|
{ value: 3, children: [] }
|
|
]
|
|
}
|
|
|
|
await assertXml(
|
|
Tree,
|
|
tree,
|
|
`<root>
|
|
<children>
|
|
<item>
|
|
<children/>
|
|
<value>2</value>
|
|
</item>
|
|
<item>
|
|
<children/>
|
|
<value>3</value>
|
|
</item>
|
|
</children>
|
|
<value>1</value>
|
|
</root>`
|
|
)
|
|
})
|
|
})
|
|
})
|