| 1 | import { Effect, Option, Predicate, Schema, SchemaGetter } from "effect" |
| 2 | import { TestSchema } from "effect/testing" |
| 3 | import { describe, it } from "vitest" |
| 4 | |
| 5 | describe("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 | }) |