1import { Effect, Option, Predicate, Schema, SchemaGetter } from "effect"
2import { TestSchema } from "effect/testing"
3import { describe, it } from "vitest"
4
5describe("v3 -> v4 migration tests", () => {
6 describe("optionalWith", () => {
7 it("default", async () => {
8 // const schema = Schema.Struct({
9 // a: Schema.optionalWith(Schema.NumberFromString, { default: () => "default value" })
10 // })
11
12 function f<S extends Schema.Constraint>(schema: S, defaultValue: S["Type"]) {
13 return Schema.Struct({
14 a: Schema.optional(schema).pipe(
15 Schema.decodeTo(Schema.toType(schema), {
16 decode: SchemaGetter.withDefault(Effect.succeed(defaultValue)),
17 encode: SchemaGetter.required()
18 })
19 )
20 })
21 }
22
23 const schema = f(Schema.NumberFromString, -1)
24
25 const asserts = new TestSchema.Asserts(schema)
26
27 const decoding = asserts.decoding()
28 await decoding.succeed({ a: "1" }, { a: 1 })
29 await decoding.succeed({}, { a: -1 })
30 await decoding.succeed({ a: undefined }, { a: -1 })
31
32 const encoding = asserts.encoding()
33 await encoding.succeed({ a: 1 }, { a: "1" })
34 await encoding.fail(
35 {},
36 `Missing key
37 at ["a"]`
38 )
39 await encoding.fail(
40 { a: undefined },
41 `Expected number
42 at ["a"]`
43 )
44 })
45
46 it("default & exact", async () => {
47 // const schema = Schema.Struct({
48 // a: Schema.optionalWith(Schema.NumberFromString, { default: () => "default value", exact: true })
49 // })
50
51 function f<S extends Schema.Constraint>(schema: S, defaultValue: S["Type"]) {
52 return Schema.Struct({
53 a: Schema.optionalKey(schema).pipe(
54 Schema.decodeTo(Schema.toType(schema), {
55 decode: SchemaGetter.withDefault(Effect.succeed(defaultValue)),
56 encode: SchemaGetter.required()
57 })
58 )
59 })
60 }
61
62 const schema = f(Schema.NumberFromString, -1)
63
64 const asserts = new TestSchema.Asserts(schema)
65
66 const decoding = asserts.decoding()
67 await decoding.succeed({ a: "1" }, { a: 1 })
68 await decoding.succeed({}, { a: -1 })
69 await decoding.fail(
70 { a: undefined },
71 `Expected string
72 at ["a"]`
73 )
74
75 const encoding = asserts.encoding()
76 await encoding.succeed({ a: 1 }, { a: "1" })
77 await encoding.fail(
78 {},
79 `Missing key
80 at ["a"]`
81 )
82 await encoding.fail(
83 { a: undefined },
84 `Expected number
85 at ["a"]`
86 )
87 })
88
89 it("nullable", async () => {
90 // const schema = Schema.Struct({
91 // a: Schema.optionalWith(Schema.String, { nullable: true })
92 // })
93
94 function f<S extends Schema.Constraint>(schema: S) {
95 return Schema.Struct({
96 a: Schema.optional(Schema.NullOr(schema)).pipe(
97 Schema.decodeTo(Schema.optional(Schema.toType(schema)), {
98 decode: SchemaGetter.transformOptional(Option.filter(Predicate.isNotNull)),
99 encode: SchemaGetter.passthrough()
100 })
101 )
102 })
103 }
104
105 const schema = f(Schema.NumberFromString)
106
107 const asserts = new TestSchema.Asserts(schema)
108
109 const decoding = asserts.decoding()
110 await decoding.succeed({ a: "1" }, { a: 1 })
111 await decoding.succeed({})
112 await decoding.succeed({ a: undefined })
113 await decoding.succeed({ a: null }, {})
114
115 const encoding = asserts.encoding()
116 await encoding.succeed({ a: 1 }, { a: "1" })
117 await encoding.succeed({ a: undefined })
118 await encoding.succeed({})
119 await encoding.fail(
120 { a: null },
121 `Expected number | undefined
122 at ["a"]`
123 )
124 })
125
126 it("nullable & exact", async () => {
127 // const schema = Schema.Struct({
128 // a: Schema.optionalWith(Schema.NumberFromString, { nullable: true, exact: true })
129 // })
130
131 function f<S extends Schema.Constraint>(schema: S) {
132 return Schema.Struct({
133 a: Schema.optionalKey(Schema.NullOr(schema)).pipe(
134 Schema.decodeTo(Schema.optionalKey(Schema.toType(schema)), {
135 decode: SchemaGetter.transformOptional(Option.filter(Predicate.isNotNull)),
136 encode: SchemaGetter.passthrough()
137 })
138 )
139 })
140 }
141
142 const schema = f(Schema.NumberFromString)
143
144 const asserts = new TestSchema.Asserts(schema)
145
146 const decoding = asserts.decoding()
147 await decoding.succeed({ a: "1" }, { a: 1 })
148 await decoding.succeed({}, {})
149 await decoding.succeed({ a: null }, {})
150 await decoding.fail(
151 { a: undefined },
152 `Expected string | null
153 at ["a"]`
154 )
155
156 const encoding = asserts.encoding()
157 await encoding.succeed({ a: 1 }, { a: "1" })
158 await encoding.succeed({})
159 await encoding.fail(
160 { a: undefined },
161 `Expected number
162 at ["a"]`
163 )
164 })
165
166 it("nullable & default", async () => {
167 // const schema = Schema.Struct({
168 // a: Schema.optionalWith(Schema.NumberFromString, { nullable: true, default: () => "default value" })
169 // })
170
171 function f<S extends Schema.Constraint>(schema: S, defaultValue: () => S["Type"]) {
172 return Schema.Struct({
173 a: Schema.optional(Schema.NullOr(schema)).pipe(
174 Schema.decodeTo(Schema.UndefinedOr(Schema.toType(schema)), {
175 decode: SchemaGetter.transformOptional((o) =>
176 o.pipe(Option.filter(Predicate.isNotNull), Option.orElseSome(defaultValue))
177 ),
178 encode: SchemaGetter.required()
179 })
180 )
181 })
182 }
183
184 const schema = f(Schema.NumberFromString, () => -1)
185
186 const asserts = new TestSchema.Asserts(schema)
187
188 const decoding = asserts.decoding()
189 await decoding.succeed({ a: "1" }, { a: 1 })
190 await decoding.succeed({}, { a: -1 })
191 await decoding.succeed({ a: null }, { a: -1 })
192 await decoding.succeed({ a: undefined })
193
194 const encoding = asserts.encoding()
195 await encoding.succeed({ a: 1 }, { a: "1" })
196 await encoding.succeed({ a: undefined })
197 await encoding.fail(
198 {},
199 `Missing key
200 at ["a"]`
201 )
202 })
203
204 it("nullable & default & exact", async () => {
205 // const schema = Schema.Struct({
206 // a: Schema.optionalWith(Schema.NumberFromString, { nullable: true, default: () => "default value", exact: true })
207 // })
208
209 function f<S extends Schema.Constraint>(schema: S, defaultValue: () => S["Type"]) {
210 return Schema.Struct({
211 a: Schema.optionalKey(Schema.NullOr(schema)).pipe(
212 Schema.decodeTo(Schema.toType(schema), {
213 decode: SchemaGetter.transformOptional((o) =>
214 o.pipe(Option.filter(Predicate.isNotNull), Option.orElseSome(defaultValue))
215 ),
216 encode: SchemaGetter.required()
217 })
218 )
219 })
220 }
221
222 const schema = f(Schema.NumberFromString, () => -1)
223
224 const asserts = new TestSchema.Asserts(schema)
225
226 const decoding = asserts.decoding()
227 await decoding.succeed({ a: "1" }, { a: 1 })
228 await decoding.succeed({}, { a: -1 })
229 await decoding.succeed({ a: null }, { a: -1 })
230 await decoding.fail(
231 { a: undefined },
232 `Expected string | null
233 at ["a"]`
234 )
235
236 const encoding = asserts.encoding()
237 await encoding.succeed({ a: 1 }, { a: "1" })
238 await encoding.fail(
239 { a: undefined },
240 `Expected number
241 at ["a"]`
242 )
243 await encoding.fail(
244 {},
245 `Missing key
246 at ["a"]`
247 )
248 })
249 })
250})