1/**
2 * Provides helpers for testing Schema behavior.
3 *
4 * These utilities assert how schemas construct values, decode input, encode
5 * output, generate arbitrary values, and round-trip between encoded and decoded
6 * forms. The `Asserts` class groups the common checks for one schema, while
7 * `Decoding` and `Encoding` can be used directly when a test only needs one
8 * direction.
9 *
10 * Methods with an `Effect` suffix return lazy assertions that use the calling
11 * fiber's services. The Promise-returning methods can be used with any test
12 * runner.
13 *
14 * @stability unstable
15 * @since 4.0.0
16 */
17import * as assert from "node:assert"
18import { isDeepStrictEqual } from "node:util"
19import * as Arbitrary from "../Arbitrary.ts"
20import * as Cause from "../Cause.ts"
21import type * as Context from "../Context.ts"
22import * as Effect from "../Effect.ts"
23import * as Exit from "../Exit.ts"
24import { pipe } from "../Function.ts"
25import * as InternalSchemaCause from "../internal/schema/cause.ts"
26import * as Result from "../Result.ts"
27import * as Schema from "../Schema.ts"
28import * as SchemaAST from "../SchemaAST.ts"
29import * as SchemaIssue from "../SchemaIssue.ts"
30import * as SchemaParser from "../SchemaParser.ts"
31
32function assertPropertyPassed<A, E>(result: Arbitrary.CheckResult<A, E>): void {
33 const failure = Arbitrary.formatCheckFailure(result)
34 if (failure !== undefined) assert.fail(failure)
35}
36
37const schemaResult = Effect.fnUntraced(function*<A, R>(
38 effect: Effect.Effect<A, SchemaIssue.Issue, R>
39): Effect.fn.Return<Result.Result<A, string>, never, R> {
40 const exit = yield* Effect.exit(effect)
41 if (Exit.isSuccess(exit)) {
42 return Result.succeed(exit.value)
43 }
44 const issue = InternalSchemaCause.getSchemaIssue(exit.cause)
45 if (issue !== undefined) {
46 return Result.fail(SchemaIssue.defaultFormatter(issue))
47 }
48 return yield* Effect.failCause(Cause.fromReasons<never>(exit.cause.reasons.map((reason) =>
49 Cause.isFailReason(reason)
50 ? Cause.makeDieReason(reason.error).annotate(Cause.reasonAnnotations(reason))
51 : reason
52 )))
53})
54
55const assertResult = Effect.fnUntraced(function*<A, R>(
56 parse: () => Effect.Effect<A, SchemaIssue.Issue, R>,
57 expected: Result.Result<A, string>
58) {
59 const result = yield* schemaResult(parse())
60 assert.deepStrictEqual(result, expected)
61})
62
63/**
64 * Provides schema test assertions for decoding, encoding, make, arbitrary generation, and round-trip verification.
65 *
66 * **When to use**
67 *
68 * Use when writing schema unit tests for decoding, encoding, construction, property-based round-trip, or generation behavior.
69 *
70 * **Example** (Decoding and encoding a struct)
71 *
72 * ```ts import.meta.vitest
73 * import { Schema } from "effect"
74 * import { TestSchema } from "effect/testing"
75 *
76 * const schema = Schema.Struct({ name: Schema.String })
77 * const asserts = new TestSchema.Asserts(schema)
78 *
79 * // decoding
80 * await asserts.decoding().succeed({ name: "Alice" }) // => undefined
81 *
82 * // encoding
83 * await asserts.encoding().succeed({ name: "Alice" }) // => undefined
84 * ```
85 *
86 * @see {@link Decoding}
87 * @see {@link Encoding}
88 * @stability unstable
89 * @category testing
90 * @since 4.0.0
91 */
92export class Asserts<S extends Schema.Constraint> {
93 /**
94 * Static helpers for comparing schema AST structures.
95 *
96 * **When to use**
97 *
98 * Use to assert that two schema field or tuple element definitions produce
99 * the same AST structure.
100 *
101 * **Details**
102 *
103 * `ast.fields.equals(a, b)` compares struct field ASTs via `assert.deepStrictEqual`. `ast.elements.equals(a, b)` compares tuple element ASTs via `assert.deepStrictEqual`.
104 *
105 * **Example** (Comparing struct fields)
106 *
107 * ```ts import.meta.vitest
108 * import { Schema } from "effect"
109 * import { TestSchema } from "effect/testing"
110 *
111 * const fieldsA = { name: Schema.String }
112 * const fieldsB = { name: Schema.String }
113 * TestSchema.Asserts.ast.fields.equals(fieldsA, fieldsB) // => undefined
114 * ```
115 *
116 * @stability unstable
117 */
118 static ast = {
119 /**
120 * Groups schema field comparisons.
121 *
122 * @stability unstable
123 */
124 fields: {
125 /**
126 * Asserts that schema fields have equal AST structures.
127 *
128 * @stability unstable
129 */
130 equals: (a: Schema.Struct.Fields, b: Schema.Struct.Fields) => {
131 assert.deepStrictEqual(
132 Object.fromEntries(Reflect.ownKeys(a).map((key) => [key, SchemaAST.getAST(a[key])])),
133 Object.fromEntries(Reflect.ownKeys(b).map((key) => [key, SchemaAST.getAST(b[key])]))
134 )
135 }
136 },
137 /**
138 * Groups tuple element comparisons.
139 *
140 * @stability unstable
141 */
142 elements: {
143 /**
144 * Asserts that tuple elements have equal AST structures.
145 *
146 * @stability unstable
147 */
148 equals: (a: Schema.Tuple.Elements, b: Schema.Tuple.Elements) => {
149 assert.deepStrictEqual(a.map(SchemaAST.getAST), b.map(SchemaAST.getAST))
150 }
151 }
152 } as const
153
154 /**
155 * Schema used by these assertions.
156 *
157 * @stability unstable
158 */
159 readonly schema: S
160 /**
161 * Creates assertions for a schema.
162 *
163 * @stability unstable
164 */
165 constructor(schema: S) {
166 this.schema = schema
167 }
168 /**
169 * Returns Promise and Effect assertions for the schema's `make` operation.
170 *
171 * **When to use**
172 *
173 * Use to assert how `Schema.make` accepts, transforms, or rejects
174 * construction input for this schema.
175 *
176 * **Details**
177 *
178 * `succeed(input)` asserts make returns the input unchanged. `succeed(input, expected)` asserts make returns `expected`. `fail(input, message)` asserts make fails with `message`.
179 * `succeedEffect` and `failEffect` return lazy effects with the same assertions.
180 * Assertion mismatches are defects in the returned effects.
181 *
182 * **Example** (Testing make)
183 *
184 * ```ts import.meta.vitest
185 * import { Schema } from "effect"
186 * import { TestSchema } from "effect/testing"
187 *
188 * const schema = Schema.String
189 * const asserts = new TestSchema.Asserts(schema)
190 * await asserts.make().succeed("hello") // => undefined
191 * ```
192 *
193 * @see {@link decoding} for assertions against decoded input
194 * @see {@link encoding} for assertions against encoded output
195 * @stability unstable
196 */
197 make(options?: Schema.MakeOptions) {
198 const makeEffect = SchemaParser.makeEffect(this.schema)
199 function succeedEffect(input: S["Type"]): Effect.Effect<void>
200 function succeedEffect(input: S["~type.make.in"], expected: S["Type"]): Effect.Effect<void>
201 function succeedEffect(input: S["~type.make.in"], expected?: S["Type"]): Effect.Effect<void> {
202 return assertResult(
203 () => makeEffect(input, options),
204 Result.succeed(arguments.length === 1 ? input : expected)
205 )
206 }
207 async function succeed(input: S["Type"]): Promise<void>
208 async function succeed(input: S["~type.make.in"], expected: S["Type"]): Promise<void>
209 async function succeed(input: S["~type.make.in"], expected?: S["Type"]) {
210 return Effect.runPromise(succeedEffect(input, arguments.length === 1 ? input : expected))
211 }
212 function failEffect(input: unknown, message: string): Effect.Effect<void> {
213 return assertResult(() => makeEffect(input, options), Result.fail(message))
214 }
215 return {
216 /**
217 * Asserts that construction succeeds with the input or the explicit expected value.
218 *
219 * @stability unstable
220 */
221 succeed,
222 /**
223 * Returns a lazy construction success assertion whose mismatches are defects.
224 *
225 * @stability unstable
226 */
227 succeedEffect,
228 /**
229 * Returns a lazy assertion that construction fails with the expected issue message.
230 *
231 * @stability unstable
232 */
233 failEffect,
234 /**
235 * Asserts that construction fails with the expected issue message.
236 *
237 * @stability unstable
238 */
239 async fail(input: unknown, message: string) {
240 return Effect.runPromise(failEffect(input, message))
241 }
242 }
243 }
244 /**
245 * Runs a property-based test that encodes arbitrary values and then decodes them, asserting the decoded value equals the original.
246 *
247 * **When to use**
248 *
249 * Use to verify that generated schema values survive an encode-then-decode
250 * round trip.
251 *
252 * **Details**
253 *
254 * The native Schema arbitrary generates values matching the schema's `Type`. The assertion fails with the minimized
255 * shrunk input and replay token if any generated value does not round-trip.
256 *
257 * **Example** (Verifying round trips)
258 *
259 * ```ts import.meta.vitest
260 * import { Schema } from "effect"
261 * import { TestSchema } from "effect/testing"
262 *
263 * const asserts = new TestSchema.Asserts(Schema.String)
264 * await asserts.verifyRoundTrip({ seed: 1, runs: 20 }) // => undefined
265 * ```
266 *
267 * @see {@link arbitrary} for checking that generated values satisfy the schema
268 * @see {@link verifyRoundTripEffect} for the Effect variant
269 * @stability unstable
270 */
271 verifyRoundTrip<S extends Schema.ConstraintCodec<unknown, unknown>>(
272 this: Asserts<S>,
273 options?: Arbitrary.CheckOptions
274 ): Promise<void> {
275 return Effect.runPromise(this.verifyRoundTripEffect(options))
276 }
277 /**
278 * Returns a lazy property-based assertion that generated values survive an
279 * encode-then-decode round trip.
280 *
281 * **When to use**
282 *
283 * Use to check round trips with the calling fiber's decoding and encoding services.
284 *
285 * **Details**
286 *
287 * Assertion mismatches are defects containing the shrunk input and replay token.
288 * Schema defects and interruption propagate without becoming assertion failures.
289 *
290 * **Example** (Composing round-trip assertions)
291 *
292 * ```ts import.meta.vitest
293 * import { Effect, Schema } from "effect"
294 * import { TestSchema } from "effect/testing"
295 *
296 * const asserts = new TestSchema.Asserts(Schema.NumberFromString)
297 * const test = Effect.gen(function*() {
298 * yield* asserts.decoding().succeedEffect("42", 42)
299 * yield* asserts.verifyRoundTripEffect({ seed: 1, runs: 20 })
300 * })
301 * await Effect.runPromise(test) // => undefined
302 * ```
303 *
304 * @see {@link verifyRoundTrip} for the Promise variant
305 * @stability unstable
306 */
307 verifyRoundTripEffect(
308 options?: Arbitrary.CheckOptions
309 ): Effect.Effect<void, never, S["DecodingServices"] | S["EncodingServices"]> {
310 return Effect.suspend(() => {
311 const decodeUnknownEffect = SchemaParser.decodeUnknownEffect(this.schema)
312 const encodeEffect = SchemaParser.encodeEffect(this.schema)
313 const arbitrary = Arbitrary.schema(this.schema)
314 return Arbitrary.checkEffect(
315 arbitrary,
316 (value) =>
317 Effect.suspend(() => encodeEffect(value)).pipe(
318 Effect.flatMapEager((encoded) => decodeUnknownEffect(encoded)),
319 schemaResult,
320 Effect.mapEager((result) => isDeepStrictEqual(result, Result.succeed(value)))
321 ),
322 options
323 ).pipe(Effect.mapEager(assertPropertyPassed))
324 })
325 }
326 /**
327 * Returns a {@link Decoding} instance for this schema with helpers for decoding assertions.
328 *
329 * **When to use**
330 *
331 * Use to test how unknown input is decoded into the schema's type.
332 *
333 * **Details**
334 *
335 * Pass `parseOptions` to control error reporting, for example `{ errors: "all" }`.
336 *
337 * **Example** (Decoding assertions)
338 *
339 * ```ts import.meta.vitest
340 * import { Schema } from "effect"
341 * import { TestSchema } from "effect/testing"
342 *
343 * const asserts = new TestSchema.Asserts(Schema.NumberFromString)
344 * const decoding = asserts.decoding()
345 * await decoding.succeed("42", 42) // => undefined
346 * await decoding.fail(null, "Expected string") // => undefined
347 * ```
348 *
349 * @see {@link Decoding}
350 * @see {@link encoding} for assertions in the opposite direction
351 * @stability unstable
352 */
353 decoding(options?: {
354 readonly parseOptions?: SchemaAST.ParseOptions | undefined
355 }) {
356 return new Decoding(this.schema, options)
357 }
358 /**
359 * Returns an {@link Encoding} instance for this schema with helpers for encoding assertions.
360 *
361 * **When to use**
362 *
363 * Use to test how schema values are encoded into their external form.
364 *
365 * **Details**
366 *
367 * Pass `parseOptions` to control error reporting, for example `{ errors: "all" }`.
368 *
369 * **Example** (Encoding assertions)
370 *
371 * ```ts import.meta.vitest
372 * import { Schema } from "effect"
373 * import { TestSchema } from "effect/testing"
374 *
375 * const asserts = new TestSchema.Asserts(Schema.NumberFromString)
376 * const encoding = asserts.encoding()
377 * await encoding.succeed(42, "42") // => undefined
378 * ```
379 *
380 * @see {@link Encoding}
381 * @see {@link decoding} for assertions in the opposite direction
382 * @stability unstable
383 */
384 encoding(options?: {
385 readonly parseOptions?: SchemaAST.ParseOptions | undefined
386 }) {
387 return new Encoding(this.schema, options)
388 }
389 /**
390 * Returns an object with property-based testing helpers for the schema's arbitrary generator.
391 *
392 * **When to use**
393 *
394 * Use to verify that arbitrary values generated for this schema satisfy the
395 * schema's predicate.
396 *
397 * **Details**
398 *
399 * `verifyGeneration()` generates arbitrary values and asserts each value satisfies the schema's `is` predicate. It
400 * defaults to 20 runs. Native checking options can control generation, shrinking, bounded discards, and replay.
401 *
402 * **Example** (Verifying arbitrary generation)
403 *
404 * ```ts import.meta.vitest
405 * import { Schema } from "effect"
406 * import { TestSchema } from "effect/testing"
407 *
408 * const asserts = new TestSchema.Asserts(Schema.String)
409 * asserts.arbitrary().verifyGeneration({ seed: 1, runs: 20 }) // => undefined
410 * ```
411 *
412 * @see {@link verifyRoundTrip} for property-based round-trip checks
413 * @stability unstable
414 */
415 arbitrary<S extends Schema.ConstraintCodec<unknown, unknown>>(this: Asserts<S>) {
416 const schema = this.schema
417 return {
418 /**
419 * Asserts that generated values satisfy the schema's predicate.
420 *
421 * @stability unstable
422 */
423 verifyGeneration(options?: Arbitrary.CheckOptions): void {
424 const is = Schema.is(schema)
425 const arbitrary = Arbitrary.schema(schema)
426 assertPropertyPassed(Effect.runSync(Arbitrary.checkEffect(arbitrary, is, { runs: 20, ...options })))
427 }
428 }
429 }
430}
431
432/**
433 * Provides decoding test assertions through `succeed` and `fail` methods that run the schema's decoder and compare the result.
434 *
435 * **When to use**
436 *
437 * Use when you want to assert that specific inputs decode to expected values, invalid inputs produce specific error messages, or schemas receive required decoding services.
438 *
439 * **Details**
440 *
441 * `succeed` and `fail` return Promises. `succeedEffect` and `failEffect` return lazy
442 * effects that preserve required decoding services. All assertions use
443 * `assert.deepStrictEqual` internally. With one argument, success assertions
444 * compare the output to the input; with two, they compare it to `expected`.
445 * Failure assertions compare the formatted schema issue to `message`.
446 * `provide(key, impl)` returns a new `Decoding` with the service injected.
447 *
448 * **Example** (Decoding with service provision)
449 *
450 * ```ts import.meta.vitest
451 * import { Schema } from "effect"
452 * import { TestSchema } from "effect/testing"
453 *
454 * const asserts = new TestSchema.Asserts(Schema.String)
455 * const decoding = asserts.decoding()
456 * await decoding.succeed("hello") // => undefined
457 * ```
458 *
459 * @see {@link Asserts}
460 * @see {@link Encoding}
461 * @stability unstable
462 * @category testing
463 * @since 4.0.0
464 */
465export class Decoding<S extends Schema.Constraint> {
466 /**
467 * Schema used by these decoding assertions.
468 *
469 * @stability unstable
470 */
471 readonly schema: S
472 /**
473 * Decoder used by these assertions, preserving schema issues and required services.
474 *
475 * @stability unstable
476 */
477 readonly decodeUnknownEffect: (
478 input: unknown,
479 options?: SchemaAST.ParseOptions
480 ) => Effect.Effect<S["Type"], SchemaIssue.Issue, S["DecodingServices"]>
481 /**
482 * Parsing options used by these assertions.
483 *
484 * @stability unstable
485 */
486 readonly options?: {
487 readonly parseOptions?: SchemaAST.ParseOptions | undefined
488 } | undefined
489 /**
490 * Creates decoding assertions for a schema and optional parsing options.
491 *
492 * @stability unstable
493 */
494 constructor(schema: S, options?: {
495 readonly parseOptions?: SchemaAST.ParseOptions | undefined
496 }) {
497 this.schema = schema
498 this.decodeUnknownEffect = SchemaParser.decodeUnknownEffect(schema)
499 this.options = options
500 }
501 /**
502 * Asserts that decoding `input` succeeds. With one argument, asserts the
503 * output equals the input. With two arguments, asserts the output equals
504 * `expected`.
505 *
506 * **When to use**
507 *
508 * Use to verify successful decoding for one input case.
509 *
510 * **Example** (Testing identity and transformed decoding)
511 *
512 * ```ts import.meta.vitest
513 * import { Schema } from "effect"
514 * import { TestSchema } from "effect/testing"
515 *
516 * const decoding = new TestSchema.Asserts(Schema.NumberFromString).decoding()
517 * await decoding.succeed("1", 1) // => undefined
518 * ```
519 *
520 * @see {@link succeedEffect} for the Effect variant
521 * @see {@link fail} for asserting decoding failures
522 * @stability unstable
523 */
524 async succeed<S extends Schema.ConstraintDecoder<unknown, never>>(
525 this: Decoding<S>,
526 input: unknown
527 ): Promise<void>
528 async succeed<S extends Schema.ConstraintDecoder<unknown, never>>(
529 this: Decoding<S>,
530 input: unknown,
531 expected: S["Type"]
532 ): Promise<void>
533 async succeed<S extends Schema.ConstraintDecoder<unknown, never>>(
534 this: Decoding<S>,
535 input: unknown,
536 expected?: S["Type"]
537 ) {
538 return Effect.runPromise(this.succeedEffect(input, arguments.length === 1 ? input : expected))
539 }
540 /**
541 * Returns a lazy assertion that decoding succeeds with the input or the
542 * explicit expected value.
543 *
544 * **When to use**
545 *
546 * Use to assert decoding in an Effect test while preserving required decoding services.
547 *
548 * **Details**
549 *
550 * Assertion mismatches are defects. The decoder uses the calling fiber's services.
551 *
552 * @see {@link succeed} for the Promise variant
553 * @see {@link failEffect} for asserting decoding failures
554 * @stability unstable
555 */
556 succeedEffect(input: unknown): Effect.Effect<void, never, S["DecodingServices"]>
557 succeedEffect(input: unknown, expected: S["Type"]): Effect.Effect<void, never, S["DecodingServices"]>
558 succeedEffect(input: unknown, expected?: S["Type"]): Effect.Effect<void, never, S["DecodingServices"]> {
559 return assertResult(
560 () => this.decodeUnknownEffect(input, this.options?.parseOptions),
561 Result.succeed(arguments.length === 1 ? input : expected)
562 )
563 }
564 /**
565 * Asserts that decoding `input` fails and the stringified issue equals
566 * `message`.
567 *
568 * **When to use**
569 *
570 * Use to verify that invalid decoding input produces the expected issue text.
571 *
572 * **Example** (Asserting a decoding failure)
573 *
574 * ```ts import.meta.vitest
575 * import { Schema } from "effect"
576 * import { TestSchema } from "effect/testing"
577 *
578 * const decoding = new TestSchema.Asserts(Schema.String).decoding()
579 * await decoding.fail(42, "Expected string") // => undefined
580 * ```
581 *
582 * @see {@link failEffect} for the Effect variant
583 * @see {@link succeed} for asserting successful decoding
584 * @stability unstable
585 */
586 async fail<S extends Schema.ConstraintDecoder<unknown, never>>(
587 this: Decoding<S>,
588 input: unknown,
589 message: string
590 ) {
591 return Effect.runPromise(this.failEffect(input, message))
592 }
593 /**
594 * Returns a lazy assertion that decoding fails with the expected issue message.
595 *
596 * **Details**
597 *
598 * Assertion mismatches are defects. Schema defects and interruption propagate
599 * without satisfying the assertion. Required decoding services come from the
600 * calling fiber.
601 *
602 * @see {@link fail} for the Promise variant
603 * @see {@link succeedEffect} for asserting decoding success
604 * @stability unstable
605 */
606 failEffect(input: unknown, message: string): Effect.Effect<void, never, S["DecodingServices"]> {
607 return assertResult(() => this.decodeUnknownEffect(input, this.options?.parseOptions), Result.fail(message))
608 }
609 /**
610 * Returns a new {@link Decoding} instance with the given service injected into the decoding effect context.
611 *
612 * **When to use**
613 *
614 * Use when the schema's decoder requires a service dependency.
615 *
616 * @see {@link Encoding.provide}
617 * @stability unstable
618 */
619 provide<Id, Service>(
620 service: Context.Key<Id, Service>,
621 implementation: Service
622 ): Decoding<Schema.middlewareDecoding<S, Exclude<S["DecodingServices"], Id>>> {
623 return new Decoding(
624 pipe(this.schema, Schema.middlewareDecoding(Effect.provideService(service, implementation))),
625 this.options
626 )
627 }
628}
629
630/**
631 * Provides encoding test assertions through `succeed` and `fail` methods that run the schema's encoder and compare the result.
632 *
633 * **When to use**
634 *
635 * Use when you want to assert that specific values encode to expected outputs, invalid inputs produce specific error messages, or schemas receive required encoding services.
636 *
637 * **Details**
638 *
639 * `succeed` and `fail` return Promises. `succeedEffect` and `failEffect` return lazy
640 * effects that preserve required encoding services. All assertions use
641 * `assert.deepStrictEqual` internally. With one argument, success assertions
642 * compare the output to the input; with two, they compare it to `expected`.
643 * Failure assertions compare the formatted schema issue to `message`.
644 * `provide(key, impl)` returns a new `Encoding` with the service injected.
645 *
646 * **Example** (Encoding assertions)
647 *
648 * ```ts import.meta.vitest
649 * import { Schema } from "effect"
650 * import { TestSchema } from "effect/testing"
651 *
652 * const encoding = new TestSchema.Asserts(Schema.NumberFromString).encoding()
653 * await encoding.succeed(42, "42") // => undefined
654 * ```
655 *
656 * @see {@link Asserts}
657 * @see {@link Decoding}
658 *
659 * @stability unstable
660 * @category testing
661 * @since 4.0.0
662 */
663export class Encoding<S extends Schema.Constraint> {
664 /**
665 * Schema used by these encoding assertions.
666 *
667 * @stability unstable
668 */
669 readonly schema: S
670 /**
671 * Encoder used by these assertions, preserving schema issues and required services.
672 *
673 * @stability unstable
674 */
675 readonly encodeUnknownEffect: (
676 input: unknown,
677 options?: SchemaAST.ParseOptions
678 ) => Effect.Effect<S["Encoded"], SchemaIssue.Issue, S["EncodingServices"]>
679 /**
680 * Parsing options used by these assertions.
681 *
682 * @stability unstable
683 */
684 readonly options?: {
685 readonly parseOptions?: SchemaAST.ParseOptions | undefined
686 } | undefined
687 /**
688 * Creates encoding assertions for a schema and optional parsing options.
689 *
690 * @stability unstable
691 */
692 constructor(schema: S, options?: {
693 readonly parseOptions?: SchemaAST.ParseOptions | undefined
694 }) {
695 this.schema = schema
696 this.encodeUnknownEffect = SchemaParser.encodeUnknownEffect(schema)
697 this.options = options
698 }
699 /**
700 * Asserts that encoding `input` succeeds. With one argument, asserts the
701 * output equals the input. With two arguments, asserts the output equals
702 * `expected`.
703 *
704 * **When to use**
705 *
706 * Use to verify successful encoding for one input case.
707 *
708 * **Example** (Testing identity and transformed encoding)
709 *
710 * ```ts import.meta.vitest
711 * import { Schema } from "effect"
712 * import { TestSchema } from "effect/testing"
713 *
714 * const encoding = new TestSchema.Asserts(Schema.NumberFromString).encoding()
715 * await encoding.succeed(1, "1") // => undefined
716 * ```
717 *
718 * @see {@link succeedEffect} for the Effect variant
719 * @see {@link fail} for asserting encoding failures
720 * @stability unstable
721 */
722 async succeed<S extends Schema.ConstraintEncoder<unknown, never>>(
723 this: Encoding<S>,
724 input: unknown
725 ): Promise<void>
726 async succeed<S extends Schema.ConstraintEncoder<unknown, never>>(
727 this: Encoding<S>,
728 input: unknown,
729 expected: S["Encoded"]
730 ): Promise<void>
731 async succeed<S extends Schema.ConstraintEncoder<unknown, never>>(
732 this: Encoding<S>,
733 input: unknown,
734 expected?: S["Encoded"]
735 ) {
736 return Effect.runPromise(this.succeedEffect(input, arguments.length === 1 ? input : expected))
737 }
738 /**
739 * Returns a lazy assertion that encoding succeeds with the input or the
740 * explicit expected value.
741 *
742 * **When to use**
743 *
744 * Use to assert encoding in an Effect test while preserving required encoding services.
745 *
746 * **Details**
747 *
748 * Assertion mismatches are defects. The encoder uses the calling fiber's services.
749 *
750 * @see {@link succeed} for the Promise variant
751 * @see {@link failEffect} for asserting encoding failures
752 * @stability unstable
753 */
754 succeedEffect(input: unknown): Effect.Effect<void, never, S["EncodingServices"]>
755 succeedEffect(input: unknown, expected: S["Encoded"]): Effect.Effect<void, never, S["EncodingServices"]>
756 succeedEffect(input: unknown, expected?: S["Encoded"]): Effect.Effect<void, never, S["EncodingServices"]> {
757 return assertResult(
758 () => this.encodeUnknownEffect(input, this.options?.parseOptions),
759 Result.succeed(arguments.length === 1 ? input : expected)
760 )
761 }
762 /**
763 * Asserts that encoding `input` fails and the stringified issue equals
764 * `message`.
765 *
766 * **When to use**
767 *
768 * Use to verify that invalid encoding input produces the expected issue text.
769 *
770 * **Example** (Asserting an encoding failure)
771 *
772 * ```ts import.meta.vitest
773 * import { Schema } from "effect"
774 * import { TestSchema } from "effect/testing"
775 *
776 * const encoding = new TestSchema.Asserts(Schema.NumberFromString).encoding()
777 * await encoding.fail("not-a-number", "Expected number") // => undefined
778 * ```
779 *
780 * @see {@link failEffect} for the Effect variant
781 * @see {@link succeed} for asserting successful encoding
782 * @stability unstable
783 */
784 async fail<S extends Schema.ConstraintEncoder<unknown, never>>(
785 this: Encoding<S>,
786 input: unknown,
787 message: string
788 ) {
789 return Effect.runPromise(this.failEffect(input, message))
790 }
791 /**
792 * Returns a lazy assertion that encoding fails with the expected issue message.
793 *
794 * **Details**
795 *
796 * Assertion mismatches are defects. Schema defects and interruption propagate
797 * without satisfying the assertion. Required encoding services come from the
798 * calling fiber.
799 *
800 * @see {@link fail} for the Promise variant
801 * @see {@link succeedEffect} for asserting encoding success
802 * @stability unstable
803 */
804 failEffect(input: unknown, message: string): Effect.Effect<void, never, S["EncodingServices"]> {
805 return assertResult(() => this.encodeUnknownEffect(input, this.options?.parseOptions), Result.fail(message))
806 }
807 /**
808 * Returns a new {@link Encoding} instance with the given service injected into the encoding effect context.
809 *
810 * **When to use**
811 *
812 * Use when the schema's encoder requires a service dependency.
813 *
814 * @see {@link Decoding.provide}
815 * @stability unstable
816 */
817 provide<Id, Service>(
818 service: Context.Key<Id, Service>,
819 implementation: Service
820 ): Encoding<Schema.middlewareEncoding<S, Exclude<S["EncodingServices"], Id>>> {
821 return new Encoding(
822 pipe(this.schema, Schema.middlewareEncoding(Effect.provideService(service, implementation))),
823 this.options
824 )
825 }
826}