1import assert from "node:assert/strict"
2import * as Schema from "../../../src/Schema.ts"
3import * as SchemaTransformation from "../../../src/SchemaTransformation.ts"
4
5export const caseNames = [
6 "literal",
7 "struct-2",
8 "struct-32",
9 "struct-256",
10 "nested",
11 "composed",
12 "encoded",
13 "union-8",
14 "struct-ready-32",
15 "struct-ready-256"
16]
17
18export function fixture(name: string) {
19 if (name === "literal") {
20 return {
21 build: (index: number) => Schema.Literal(index),
22 input: (index: number) => index,
23 output: (index: number) => index,
24 invalid: null
25 }
26 }
27 if (name.startsWith("struct-")) {
28 const ready = name.startsWith("struct-ready-")
29 const size = Number(name.slice(ready ? "struct-ready-".length : "struct-".length))
30 assert.ok([2, 32, 256].includes(size))
31 const keys = Array.from({ length: size }, (_, index) => `field${index}`)
32 const value = Object.fromEntries(keys.map((key) => [key, "value"]))
33 if (ready) {
34 const fields = Object.fromEntries(keys.map((key) => [key, Schema.String]))
35 return {
36 build: () => Schema.Struct(fields),
37 input: () => value,
38 output: () => value,
39 invalid: {}
40 }
41 }
42 return {
43 build: () => Schema.Struct(Object.fromEntries(keys.map((key) => [key, Schema.String]))),
44 input: () => value,
45 output: () => value,
46 invalid: {}
47 }
48 }
49 if (name === "nested") {
50 const value = (index: number) => ({
51 endpoint: index,
52 payload: { name: "Ada", count: 1, location: { country: "IT", city: "Rome" } }
53 })
54 return {
55 build: (index: number) =>
56 Schema.Struct({
57 endpoint: Schema.Literal(index),
58 payload: Schema.Struct({
59 name: Schema.String,
60 count: Schema.Number,
61 location: Schema.Struct({ country: Schema.Literal("IT"), city: Schema.String })
62 })
63 }),
64 input: value,
65 output: value,
66 invalid: {}
67 }
68 }
69 if (name === "composed") {
70 const value = (index: number) => ({
71 endpoint: index,
72 name: "Ada",
73 items: [{ sku: "A", quantity: 1 }],
74 status: "open"
75 })
76 return {
77 build: (index: number) =>
78 Schema.Struct({
79 endpoint: Schema.Literal(index),
80 name: Schema.String.check(Schema.isMinLength(1)),
81 items: Schema.Array(Schema.Struct({
82 sku: Schema.String.check(Schema.isMinLength(1)),
83 quantity: Schema.Number.check(Schema.isGreaterThanOrEqualTo(1))
84 })),
85 note: Schema.optionalKey(Schema.String),
86 status: Schema.Literals(["open", "closed"])
87 }),
88 input: value,
89 output: value,
90 invalid: { endpoint: 0, name: "", items: [{ sku: "", quantity: 0 }], status: "unknown" }
91 }
92 }
93 if (name === "encoded") {
94 return {
95 build: (index: number) => {
96 const amount = Schema.String.pipe(Schema.decodeTo(
97 Schema.Number.check(Schema.isGreaterThanOrEqualTo(0)),
98 SchemaTransformation.numberFromString
99 ))
100 return Schema.Struct({
101 endpoint: Schema.Literal(index),
102 amount,
103 values: Schema.Array(amount),
104 note: Schema.optionalKey(Schema.String)
105 })
106 },
107 input: (index: number) => ({ endpoint: index, amount: "1", values: ["2"] }),
108 output: (index: number) => ({ endpoint: index, amount: 1, values: [2] }),
109 invalid: { endpoint: 0, amount: "-1", values: ["-2"] }
110 }
111 }
112 if (name === "union-8") {
113 const value = (index: number) => ({ kind: index * 8 + 7, value: "Ada" })
114 return {
115 build: (index: number) =>
116 Schema.Union(Array.from({ length: 8 }, (_, member) =>
117 Schema.Struct({
118 kind: Schema.Literal(index * 8 + member),
119 value: Schema.String
120 }))),
121 input: value,
122 output: value,
123 invalid: { kind: -1, value: "Ada" }
124 }
125 }
126 throw new Error(`Unknown fixture: ${name}`)
127}
128
129export function validate(name: string) {
130 const { build, input, output, invalid } = fixture(name)
131 for (const index of [0, 1, 999]) {
132 const schema = build(index)
133 assert.deepEqual(Schema.decodeUnknownSync(schema)(input(index)), output(index))
134 assert.deepEqual(schema.make(output(index) as never), output(index))
135 assert.throws(() => Schema.decodeUnknownSync(schema)(invalid))
136 }
137}