1import { assert, describe, it } from "@effect/vitest"
2import { SchemaRepresentation } from "effect"
3import { throws } from "../../utils/assert.ts"
4
5describe("SchemaRepresentation.fromJson", () => {
6 it("decodes a document", () => {
7 const input = {
8 representation: {
9 _tag: "String",
10 annotations: { description: "value" },
11 checks: []
12 },
13 references: {}
14 } as const
15
16 assert.deepStrictEqual(SchemaRepresentation.fromJson(input), input)
17 })
18
19 it("decodes Null", () => {
20 const input = { representation: { _tag: "Null", checks: [] }, references: {} } as const
21 assert.deepStrictEqual(SchemaRepresentation.fromJson(input), input)
22 })
23
24 it("decodes Undefined", () => {
25 const input = { representation: { _tag: "Undefined", checks: [] }, references: {} } as const
26 assert.deepStrictEqual(SchemaRepresentation.fromJson(input), input)
27 })
28
29 it("decodes Void", () => {
30 const input = { representation: { _tag: "Void", checks: [] }, references: {} } as const
31 assert.deepStrictEqual(SchemaRepresentation.fromJson(input), input)
32 })
33
34 it("decodes Never", () => {
35 const input = { representation: { _tag: "Never", checks: [] }, references: {} } as const
36 assert.deepStrictEqual(SchemaRepresentation.fromJson(input), input)
37 })
38
39 it("decodes Unknown", () => {
40 const input = { representation: { _tag: "Unknown", checks: [] }, references: {} } as const
41 assert.deepStrictEqual(SchemaRepresentation.fromJson(input), input)
42 })
43
44 it("decodes Any", () => {
45 const input = { representation: { _tag: "Any", checks: [] }, references: {} } as const
46 assert.deepStrictEqual(SchemaRepresentation.fromJson(input), input)
47 })
48
49 it("decodes Number", () => {
50 const input = { representation: { _tag: "Number", checks: [] }, references: {} } as const
51 assert.deepStrictEqual(SchemaRepresentation.fromJson(input), input)
52 })
53
54 it("decodes Boolean", () => {
55 const input = { representation: { _tag: "Boolean", checks: [] }, references: {} } as const
56 assert.deepStrictEqual(SchemaRepresentation.fromJson(input), input)
57 })
58
59 it("decodes BigInt", () => {
60 const input = { representation: { _tag: "BigInt", checks: [] }, references: {} } as const
61 assert.deepStrictEqual(SchemaRepresentation.fromJson(input), input)
62 })
63
64 it("decodes Symbol", () => {
65 const input = { representation: { _tag: "Symbol", checks: [] }, references: {} } as const
66 assert.deepStrictEqual(SchemaRepresentation.fromJson(input), input)
67 })
68
69 it("decodes ObjectKeyword", () => {
70 const input = { representation: { _tag: "ObjectKeyword", checks: [] }, references: {} } as const
71 assert.deepStrictEqual(SchemaRepresentation.fromJson(input), input)
72 })
73
74 it("decodes Enum", () => {
75 const input = {
76 representation: {
77 _tag: "Enum",
78 enums: [
79 ["A", { type: "string", value: "a" }],
80 ["One", { type: "number", value: 1 }]
81 ],
82 checks: []
83 },
84 references: {}
85 } as const
86 assert.deepStrictEqual(SchemaRepresentation.fromJson(input), {
87 representation: {
88 _tag: "Enum",
89 enums: [["A", "a"], ["One", 1]],
90 checks: []
91 },
92 references: {}
93 })
94 })
95
96 it("decodes TemplateLiteral", () => {
97 const input = {
98 representation: {
99 _tag: "TemplateLiteral",
100 parts: [
101 { _tag: "Literal", literal: { type: "string", value: "prefix-" }, checks: [] },
102 { _tag: "String", checks: [] }
103 ],
104 checks: []
105 },
106 references: {}
107 } as const
108 assert.deepStrictEqual(SchemaRepresentation.fromJson(input), {
109 representation: {
110 _tag: "TemplateLiteral",
111 parts: [
112 { _tag: "Literal", literal: "prefix-", checks: [] },
113 { _tag: "String", checks: [] }
114 ],
115 checks: []
116 },
117 references: {}
118 })
119 })
120
121 it("decodes Arrays", () => {
122 const input = {
123 representation: {
124 _tag: "Arrays",
125 elements: [{ type: { _tag: "String", checks: [] }, isOptional: false }],
126 rest: [{ _tag: "Number", checks: [] }],
127 checks: []
128 },
129 references: {}
130 } as const
131 assert.deepStrictEqual(SchemaRepresentation.fromJson(input), input)
132 })
133
134 it("decodes Objects", () => {
135 const input = {
136 representation: {
137 _tag: "Objects",
138 propertySignatures: [{
139 name: { type: "string", value: "value" },
140 type: { _tag: "String", checks: [] },
141 isOptional: false,
142 isMutable: true
143 }],
144 indexSignatures: [{
145 parameter: { _tag: "String", checks: [] },
146 type: { _tag: "Number", checks: [] }
147 }],
148 checks: []
149 },
150 references: {}
151 } as const
152 assert.deepStrictEqual(SchemaRepresentation.fromJson(input), {
153 representation: {
154 _tag: "Objects",
155 propertySignatures: [{
156 name: "value",
157 type: { _tag: "String", checks: [] },
158 isOptional: false,
159 isMutable: true
160 }],
161 indexSignatures: [{
162 parameter: { _tag: "String", checks: [] },
163 type: { _tag: "Number", checks: [] }
164 }],
165 checks: []
166 },
167 references: {}
168 })
169 })
170
171 it("decodes Union", () => {
172 const input = {
173 representation: {
174 _tag: "Union",
175 types: [{ _tag: "String", checks: [] }, { _tag: "Number", checks: [] }],
176 options: { mode: "oneOf" },
177 checks: []
178 },
179 references: {}
180 } as const
181 assert.deepStrictEqual(SchemaRepresentation.fromJson(input), input)
182 })
183
184 it("decodes Reference", () => {
185 const input = {
186 representation: { _tag: "Reference", $ref: "Value" },
187 references: { Value: { _tag: "String", checks: [] } }
188 } as const
189 assert.deepStrictEqual(SchemaRepresentation.fromJson(input), input)
190 })
191
192 it("decodes Suspend", () => {
193 const input = {
194 representation: {
195 _tag: "Suspend",
196 thunk: { _tag: "Reference", $ref: "Value" },
197 checks: []
198 },
199 references: { Value: { _tag: "String", checks: [] } }
200 } as const
201 assert.deepStrictEqual(SchemaRepresentation.fromJson(input), input)
202 })
203
204 it("decodes Declaration", () => {
205 const input = {
206 representation: {
207 _tag: "Declaration",
208 representation: { id: "acme/schema/Value", payload: null },
209 annotations: {},
210 typeParameters: [{ _tag: "String", checks: [] }],
211 checks: []
212 },
213 references: {}
214 } as const
215 assert.deepStrictEqual(SchemaRepresentation.fromJson(input), input)
216 })
217
218 it("decodes Filter", () => {
219 const input = {
220 representation: {
221 _tag: "String",
222 checks: [{
223 _tag: "Filter",
224 representation: { id: "acme/schema/filter", payload: null },
225 annotations: {},
226 aborted: true
227 }]
228 },
229 references: {}
230 } as const
231 assert.deepStrictEqual(SchemaRepresentation.fromJson(input), input)
232 })
233
234 it("decodes FilterGroup", () => {
235 const input = {
236 representation: {
237 _tag: "String",
238 checks: [{
239 _tag: "FilterGroup",
240 checks: [{
241 _tag: "Filter",
242 representation: { id: "acme/schema/filter", payload: null },
243 annotations: {},
244 aborted: false
245 }]
246 }]
247 },
248 references: {}
249 } as const
250 assert.deepStrictEqual(SchemaRepresentation.fromJson(input), input)
251 })
252
253 it("decodes bigint structural values", () => {
254 assert.deepStrictEqual(
255 SchemaRepresentation.fromJson({
256 representation: {
257 _tag: "Literal",
258 literal: { type: "bigint", value: "1" },
259 checks: []
260 },
261 references: {}
262 }),
263 {
264 representation: { _tag: "Literal", literal: 1n, checks: [] },
265 references: {}
266 }
267 )
268 })
269
270 it("rejects mismatched literal types", () => {
271 for (
272 const representation of [
273 { _tag: "Literal", literal: { type: "bigint", value: "not-an-integer" }, checks: [] },
274 { _tag: "Literal", literal: { type: "boolean", value: "true" }, checks: [] },
275 { _tag: "Literal", literal: { type: "string", value: 1 }, checks: [] }
276 ] as const
277 ) {
278 throws(() => SchemaRepresentation.fromJson({ representation, references: {} }))
279 }
280 })
281
282 it("rejects mismatched Enum value types", () => {
283 throws(() =>
284 SchemaRepresentation.fromJson({
285 representation: {
286 _tag: "Enum",
287 enums: [["One", { type: "number", value: "1" }]],
288 checks: []
289 },
290 references: {}
291 })
292 )
293 })
294
295 it("rejects mismatched property name types", () => {
296 throws(() =>
297 SchemaRepresentation.fromJson({
298 representation: {
299 _tag: "Objects",
300 propertySignatures: [{
301 name: { type: "number", value: "1" },
302 type: { _tag: "String", checks: [] },
303 isOptional: false,
304 isMutable: false
305 }],
306 indexSignatures: [],
307 checks: []
308 },
309 references: {}
310 })
311 )
312 })
313
314 it("preserves string literals resembling non-finite numbers", () => {
315 for (const literal of ["NaN", "Infinity", "-Infinity"]) {
316 const input = {
317 representation: { _tag: "Literal", literal: { type: "string", value: literal }, checks: [] },
318 references: {}
319 } as const
320 assert.deepStrictEqual(SchemaRepresentation.fromJson(input), {
321 representation: { _tag: "Literal", literal, checks: [] },
322 references: {}
323 })
324 }
325 })
326
327 it("decodes global symbols", () => {
328 assert.deepStrictEqual(
329 SchemaRepresentation.fromJson({
330 representation: {
331 _tag: "UniqueSymbol",
332 symbol: "Symbol(acme/schema/key)",
333 checks: []
334 },
335 references: {}
336 }),
337 {
338 representation: {
339 _tag: "UniqueSymbol",
340 symbol: Symbol.for("acme/schema/key"),
341 checks: []
342 },
343 references: {}
344 }
345 )
346 })
347
348 it("does not coerce strings resembling symbols", () => {
349 const input = {
350 representation: { _tag: "Literal", literal: { type: "string", value: "Symbol(a)" }, checks: [] },
351 references: {}
352 } as const
353
354 assert.deepStrictEqual(SchemaRepresentation.fromJson(input), {
355 representation: { _tag: "Literal", literal: "Symbol(a)", checks: [] },
356 references: {}
357 })
358 })
359
360 it("requires representation on persisted Filters", () => {
361 throws(
362 () =>
363 SchemaRepresentation.fromJson({
364 representation: { _tag: "String", checks: [{ _tag: "Filter", aborted: false }] },
365 references: {}
366 }),
367 `Missing key\n at ["representation"]["checks"][0]["representation"]`
368 )
369 })
370
371 it("requires representation on persisted Declarations", () => {
372 throws(
373 () =>
374 SchemaRepresentation.fromJson({
375 representation: { _tag: "Declaration", typeParameters: [], checks: [] },
376 references: {}
377 }),
378 `Missing key\n at ["representation"]["representation"]`
379 )
380 })
381
382 it("rejects empty references", () => {
383 throws(
384 () =>
385 SchemaRepresentation.fromJson({
386 representation: { _tag: "Reference", $ref: "" },
387 references: {}
388 }),
389 `Expected a value with a length of at least 1\n at ["representation"]["$ref"]`
390 )
391 })
392
393 it("rejects non-JSON annotations", () => {
394 throws(
395 () =>
396 SchemaRepresentation.fromJson({
397 representation: {
398 _tag: "String",
399 annotations: { invalid: 1n },
400 checks: []
401 },
402 references: {}
403 } as never),
404 `Expected JSON value\n at ["representation"]["annotations"]["invalid"]`
405 )
406 })
407
408 it("rejects non-JSON roots", () => {
409 const input = () => undefined
410 throws(
411 () => SchemaRepresentation.fromJson(input as never),
412 `Expected object`
413 )
414 })
415
416 it("does not invoke toJSON", () => {
417 let calls = 0
418 const input = {
419 representation: { _tag: "String", checks: [], unexpected: true },
420 references: {},
421 toJSON() {
422 calls++
423 return null
424 }
425 }
426
427 assert.deepStrictEqual(
428 SchemaRepresentation.fromJson(input as never),
429 {
430 representation: { _tag: "String", checks: [] },
431 references: {}
432 }
433 )
434 assert.strictEqual(calls, 0)
435 })
436})