From 858a23dc81add3217c2336e7e76b3df377b119b3 Mon Sep 17 00:00:00 2001 From: Giulio Canti Date: Sat, 8 Aug 2026 08:21:32 +0200 Subject: [PATCH] Simplify Schema arbitrary derivation API --- .changeset/schema-arbitrary-factory.md | 5 + migration/v3-to-v4.md | 8 +- packages/effect/SCHEMA.md | 51 +------ packages/effect/src/Schema.ts | 128 ++++------------- .../effect/src/internal/schema/toArbitrary.ts | 72 ---------- packages/effect/src/testing/TestSchema.ts | 4 +- .../effect/test/schema/toArbitrary.test.ts | 129 ++++++++---------- .../test/schema/toDifferJsonPatch.test.ts | 2 +- .../test/schema/toJsonSchemaDocument.test.ts | 2 +- .../effect/typetest/schema/toArbitrary.tst.ts | 19 +-- ...ArbitraryLazy.ts => schema-toArbitrary.ts} | 2 +- packages/vitest/src/internal/internal.ts | 4 +- 12 files changed, 102 insertions(+), 324 deletions(-) create mode 100644 .changeset/schema-arbitrary-factory.md rename packages/tools/bundle/fixtures/{schema-toArbitraryLazy.ts => schema-toArbitrary.ts} (75%) diff --git a/.changeset/schema-arbitrary-factory.md b/.changeset/schema-arbitrary-factory.md new file mode 100644 index 00000000000..d0802c815b4 --- /dev/null +++ b/.changeset/schema-arbitrary-factory.md @@ -0,0 +1,5 @@ +--- +"effect": patch +--- + +Consolidate schema arbitrary derivation into `Schema.toArbitrary`, which now returns a `Schema.Arbitrary` factory that accepts the fast-check module. Remove `Schema.toArbitraryLazy` and arbitrary derivation reports. diff --git a/migration/v3-to-v4.md b/migration/v3-to-v4.md index 5df3c810b5c..459cdc46095 100644 --- a/migration/v3-to-v4.md +++ b/migration/v3-to-v4.md @@ -8508,7 +8508,7 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) - `Arbitrary.ArbitraryGenerationContext` -> `Schema.Annotations.ToArbitrary.Context`: Use the v4 arbitrary-derivation context type from Schema.Annotations. -- `Arbitrary.LazyArbitrary` -> `Schema.LazyArbitrary`: The lazy arbitrary type moved onto Schema. +- `Arbitrary.LazyArbitrary` -> `Schema.Arbitrary`: The arbitrary factory type moved onto Schema. #### `Arbitrary.make` @@ -8519,19 +8519,19 @@ Arbitrary derivation is now exposed directly by Schema. **Example** ```ts -Schema.toArbitrary(schema) +Schema.toArbitrary(schema)(FastCheck) ``` #### `Arbitrary.makeLazy` -**Replacement:** `Schema.toArbitraryLazy` +**Replacement:** `Schema.toArbitrary` Lazy arbitrary derivation is now exposed directly by Schema. **Example** ```ts -Schema.toArbitraryLazy(schema) +Schema.toArbitrary(schema) ``` ### `effect/Array` diff --git a/packages/effect/SCHEMA.md b/packages/effect/SCHEMA.md index 89da3c26f2f..2a2d416dfe3 100644 --- a/packages/effect/SCHEMA.md +++ b/packages/effect/SCHEMA.md @@ -5558,9 +5558,9 @@ console.log(JSON.stringify(document, null, 2)) ### Generating an Arbitrary from a Schema -Property-based tests need generators. `Schema.toArbitrary` derives a -`fast-check` `Arbitrary` that generates decoded `Type` values accepted by the -schema. +Property-based tests need generators. `Schema.toArbitrary` derives a factory +that accepts the `fast-check` module and returns an `Arbitrary` that generates +decoded `Type` values accepted by the schema. Most schemas do not need any extra work: @@ -5573,23 +5573,11 @@ const Person = Schema.Struct({ age: Schema.Int.check(Schema.isBetween({ minimum: 18, maximum: 80 })) }) -const PersonArbitrary = Schema.toArbitrary(Person) +const PersonArbitrary = Schema.toArbitrary(Person)(FastCheck) console.log(FastCheck.sample(PersonArbitrary, 3)) ``` -Use `Schema.toArbitraryLazy` only when you want the caller to provide -`fast-check`: - -```ts -import { Schema } from "effect" -import { FastCheck } from "effect/testing" - -const makeStringArbitrary = Schema.toArbitraryLazy(Schema.String) - -const StringArbitrary = makeStringArbitrary(FastCheck) -``` - `Schema.Never` and declaration schemas without a `toArbitrary` annotation cannot be derived automatically. @@ -5642,35 +5630,6 @@ This works because the final predicate check rejects strings that are not palindromes. It may need many attempts, because the base string generator has no reason to produce mirrored strings. -#### Reports - -Use `{ report: true }` when you want to know which filters did not guide -generation: - -```ts -import { Schema } from "effect" - -const isPalindrome = (s: string) => s === Array.from(s).reverse().join("") - -const Palindrome = Schema.String.check( - Schema.makeFilter(isPalindrome, { - expected: "a palindrome" - }) -) - -const result = Schema.toArbitrary(Palindrome, { report: true }) - -result.value -result.report.warnings -``` - -An `OpaqueFilter` warning means: "this filter is still checked, but it did not -help build the generator." - -Reports contain warnings only. Unsupported schemas, impossible constraints, -invalid candidates, and recursive schemas without a finite terminal path still -fail immediately. - #### Custom Filters With Constraints If part of a custom filter can be described as a normal generation constraint, @@ -5947,7 +5906,7 @@ const Person = Schema.Struct({ company: Company }) -console.log(FastCheck.sample(Schema.toArbitrary(Person), 3)) +console.log(FastCheck.sample(Schema.toArbitrary(Person)(FastCheck), 3)) ``` These overrides are useful because the values have domain shape: names look like diff --git a/packages/effect/src/Schema.ts b/packages/effect/src/Schema.ts index be7813c1e66..28961e24c1a 100644 --- a/packages/effect/src/Schema.ts +++ b/packages/effect/src/Schema.ts @@ -63,7 +63,7 @@ import type * as SchemaRepresentation from "./SchemaRepresentation.ts" import * as SchemaTransformation from "./SchemaTransformation.ts" import type { Assign, Lambda, Mutable, Simplify } from "./Struct.ts" import * as Struct_ from "./Struct.ts" -import * as FastCheck from "./testing/FastCheck.ts" +import type * as FastCheck from "./testing/FastCheck.ts" import type { RequiredKeys, UnionToIntersection } from "./Types.ts" import type { Unify } from "./Unify.ts" @@ -14507,44 +14507,32 @@ export const TaggedError: { // ----------------------------------------------------------------------------- /** - * A thunk that, given the `fast-check` module, returns an `Arbitrary`. - * Use this type when you need to defer instantiation of the arbitrary, for - * example to support recursive schemas. + * Represents a function that builds a fast-check `Arbitrary` from the + * `fast-check` module. + * + * **When to use** + * + * Use as the result type of schema arbitrary derivation. * * @category utility types * @since 4.0.0 */ -export type LazyArbitrary = (fc: typeof FastCheck) => FastCheck.Arbitrary +export type Arbitrary = (fc: typeof FastCheck) => FastCheck.Arbitrary /** - * Derives a {@link LazyArbitrary} from a schema. The result is memoized so - * repeated calls with the same schema are cheap. + * Returns an {@link Arbitrary} factory derived from a schema. The generated + * values satisfy the schema and use its decoded `Type`. * - * **Details** - * - * Prefer {@link toArbitrary} when you need the arbitrary directly, or when you - * want derivation diagnostics via `{ report: true }`. Unsupported schema - * nodes, impossible constraints, invalid candidates, and recursive schemas - * without a finite terminal path fail immediately. + * **When to use** * - * @category generators - * @since 4.0.0 - */ -export function toArbitraryLazy(schema: S): LazyArbitrary { - const lawc = InternalArbitrary.memoized(schema.ast) - return (fc) => lawc(fc, {}) -} - -/** - * Derives a `fast-check` `Arbitrary` from a schema for property-based - * testing. The derived arbitrary generates values that satisfy the schema. + * Use when you need a fast-check generator for values accepted by a schema. * * **Details** * * Constraints refine base generators; candidates add weighted sources while - * filters still validate every value. `{ report: true }` returns warnings such - * as `OpaqueFilter`, while derivation errors remain fail-fast. Recursive - * schemas use terminal branches and fail when no finite terminal path exists. + * filters still validate every value. Recursive schemas use terminal branches + * and fail when no finite terminal path exists. The result is memoized so + * repeated calls with the same schema are cheap. * * **Example** (Generating arbitrary values) * @@ -14552,36 +14540,20 @@ export function toArbitraryLazy(schema: S): LazyArbitrary< * import { Schema } from "effect" * import * as FastCheck from "fast-check" * - * const PersonArb = Schema.toArbitrary( + * const makePersonArbitrary = Schema.toArbitrary( * Schema.Struct({ name: Schema.String, age: Schema.Number }) * ) * - * // Sample a random value - * FastCheck.sample(PersonArb, 1) + * const PersonArbitrary = makePersonArbitrary(FastCheck) + * FastCheck.sample(PersonArbitrary, 1) * ``` * * @category generators * @since 4.0.0 */ -export function toArbitrary(schema: S): FastCheck.Arbitrary -export function toArbitrary( - schema: S, - options: { readonly report: true } -): Annotations.ToArbitrary.WithReport> -export function toArbitrary( - schema: S, - options?: { readonly report?: boolean } -): FastCheck.Arbitrary | Annotations.ToArbitrary.WithReport> { - if (options?.report === true) { - const lawc = InternalArbitrary.memoized(schema.ast) - const report = InternalArbitrary.makeReport() - InternalArbitrary.collectReport(schema.ast, report) - return { - value: lawc(FastCheck, {}), - report: InternalArbitrary.toReport(report) - } - } - return toArbitraryLazy(schema)(FastCheck) +export function toArbitrary(schema: S): Arbitrary { + const lawc = InternalArbitrary.memoized(schema.ast) + return (fc) => lawc(fc, {}) } // ----------------------------------------------------------------------------- @@ -16369,8 +16341,7 @@ export declare namespace Annotations { /** * Types used by arbitrary-derivation annotations to configure `toArbitrary` - * hooks, filter hints, candidate sources, diagnostics, and merged generation - * constraints. + * hooks, filter hints, candidate sources, and merged generation constraints. * * @since 4.0.0 */ @@ -16383,8 +16354,7 @@ export declare namespace Annotations { * `constraint` refines the schema node's base generator. `candidate` adds a * weighted source before all filters run. If neither hint is provided, the * filter does not guide generation; generated values are still checked by - * the filter predicate. With `{ report: true }`, this is reported as - * `OpaqueFilter`. + * the filter predicate. * * @category models * @since 4.0.0 @@ -16572,58 +16542,6 @@ export declare namespace Annotations { typeParameters: { readonly [K in keyof TypeParameters]: TypeParameter } ): (fc: typeof FastCheck, context: Context) => Output } - - /** - * Wraps a derived value together with arbitrary-derivation diagnostics. - * - * @category models - * @since 4.0.0 - */ - export interface WithReport { - readonly value: A - readonly report: Report - } - - /** - * Diagnostics collected while deriving an arbitrary. - * - * **Details** - * - * Reports contain warnings only. Unsupported schema nodes, impossible - * constraints, invalid candidate weights, and throwing candidate factories - * fail immediately. - * - * @category models - * @since 4.0.0 - */ - export interface Report { - readonly warnings: ReadonlyArray - } - - /** - * Non-fatal arbitrary-derivation warning. - * - * @category models - * @since 4.0.0 - */ - export type Warning = OpaqueFilterWarning - - /** - * Warning emitted when a filter is handled only by the final `.filter`. - * - * **Details** - * - * The filter is still enforced. The warning means it did not contribute - * a constraint or candidate, so generation may rely on fast-check discards. - * - * @category models - * @since 4.0.0 - */ - export interface OpaqueFilterWarning { - readonly _tag: "OpaqueFilter" - readonly path: ReadonlyArray - readonly description?: string | undefined - } } /** diff --git a/packages/effect/src/internal/schema/toArbitrary.ts b/packages/effect/src/internal/schema/toArbitrary.ts index 4ea511b4cb6..c6d2ae27c6f 100644 --- a/packages/effect/src/internal/schema/toArbitrary.ts +++ b/packages/effect/src/internal/schema/toArbitrary.ts @@ -34,20 +34,6 @@ type LazyOption = ( recursionStack: RecursionStack ) => FastCheck.Arbitrary | undefined -export interface MutableReport { - readonly warnings: Array -} - -/** @internal */ -export function makeReport(): MutableReport { - return { warnings: [] } -} - -/** @internal */ -export function toReport(report: MutableReport): Schema.Annotations.ToArbitrary.Report { - return { warnings: report.warnings.slice() } -} - function arbitraryError(what: string) { return new Error(`Unable to derive an arbitrary for ${what}`) } @@ -394,64 +380,6 @@ function finiteNumberContext(ctx: Context): Context { } } -function reportChecks(report: MutableReport, checks: SchemaAST.Checks | undefined, path: ReadonlyArray) { - function visit(check: SchemaAST.Check, covered: boolean) { - const arbitrary = check.annotations?.arbitrary - const nextCovered = covered || arbitrary?.constraint !== undefined || arbitrary?.candidate !== undefined - if (check._tag !== "Filter") { - for (const child of check.checks) { - visit(child, nextCovered) - } - } else if (!nextCovered) { - const description = check.annotations?.representation?.id ?? check.annotations?.identifier ?? - check.annotations?.expected - report.warnings.push({ _tag: "OpaqueFilter", path, ...(description === undefined ? {} : { description }) }) - } - } - checks?.forEach((check) => visit(check, false)) -} - -/** @internal */ -export function collectReport(ast: SchemaAST.AST, report: MutableReport) { - const stack = new WeakSet() - function visit(ast: SchemaAST.AST, path: ReadonlyArray) { - if (stack.has(ast)) { - return - } - stack.add(ast) - reportChecks(report, ast.checks, path) - switch (ast._tag) { - case "Declaration": - ast.typeParameters.forEach((tp) => visit(tp, path)) - break - case "Arrays": { - for (const [i, type] of [...ast.elements, ...ast.rest].entries()) { - visit(type, [...path, i]) - } - break - } - case "Objects": - ast.propertySignatures.forEach((ps) => visit(ps.type, [...path, ps.name])) - ast.indexSignatures.forEach((is) => { - visit(is.parameter, path) - visit(is.type, path) - }) - break - case "Union": - ast.types.forEach((type) => visit(type, path)) - break - case "TemplateLiteral": - ast.parts.forEach((part, i) => visit(SchemaAST.toEncoded(part), [...path, i])) - break - case "Suspend": - visit(ast.thunk(), path) - break - } - stack.delete(ast) - } - visit(ast, []) -} - function applyCandidates( fc: typeof FastCheck, ctx: Context, diff --git a/packages/effect/src/testing/TestSchema.ts b/packages/effect/src/testing/TestSchema.ts index 11aea8a78b4..88e55f97ffc 100644 --- a/packages/effect/src/testing/TestSchema.ts +++ b/packages/effect/src/testing/TestSchema.ts @@ -172,7 +172,7 @@ export class Asserts { }) { const decodeUnknownEffect = SchemaParser.decodeUnknownEffect(this.schema) const encodeEffect = SchemaParser.encodeEffect(this.schema) - const arbitrary = Schema.toArbitrary(this.schema) + const arbitrary = Schema.toArbitrary(this.schema)(FastCheck) return FastCheck.assert( FastCheck.asyncProperty(arbitrary, async (t) => { const r = await Effect.runPromise( @@ -280,7 +280,7 @@ export class Asserts { }) { const params = options?.params const is = Schema.is(schema) - const arb = Schema.toArbitrary(schema) + const arb = Schema.toArbitrary(schema)(FastCheck) FastCheck.assert(FastCheck.property(arb, (a) => is(a)), { numRuns: 20, ...params }) } } diff --git a/packages/effect/test/schema/toArbitrary.test.ts b/packages/effect/test/schema/toArbitrary.test.ts index 05cb1a54606..522d0b9fd19 100644 --- a/packages/effect/test/schema/toArbitrary.test.ts +++ b/packages/effect/test/schema/toArbitrary.test.ts @@ -3,8 +3,12 @@ import { FastCheck, TestSchema } from "effect/testing" import { describe, it } from "vitest" import { assertInclude, assertInstanceOf, deepStrictEqual, strictEqual, throws } from "../utils/assert.ts" +function toArbitrary(schema: S) { + return Schema.toArbitrary(schema)(FastCheck) +} + function assertUnsupportedSchema(schema: Schema.Constraint, message: string) { - throws(() => Schema.toArbitrary(schema), message) + throws(() => toArbitrary(schema), message) } function verifyGeneration>(schema: S, numRuns?: number) { @@ -19,12 +23,12 @@ function verifyGeneration>(sc // Guard for "fast but wrong" regressions: samples the derived arbitrary and // asserts an output invariant (length/size/property-count bounds) over many runs. function assertInvariant(schema: Schema.Constraint, predicate: (value: any) => boolean, numRuns = 200) { - FastCheck.assert(FastCheck.property(Schema.toArbitrary(schema), predicate), { numRuns }) + FastCheck.assert(FastCheck.property(toArbitrary(schema), predicate), { numRuns }) } function assertRecursiveNoFiniteGenerationPath(schema: Schema.Constraint) { throws( - () => Schema.toArbitrary(schema), + () => toArbitrary(schema), (e) => { assertInstanceOf(e, Error) assertInclude( @@ -174,9 +178,9 @@ describe("Arbitrary generation", () => { } ) - Schema.toArbitraryLazy(Schema.Number.check(noNaN))(fc) - Schema.toArbitraryLazy(Schema.Number.check(noInfinity))(fc) - Schema.toArbitraryLazy(Schema.Finite)(fc) + Schema.toArbitrary(Schema.Number.check(noNaN))(fc) + Schema.toArbitrary(Schema.Number.check(noInfinity))(fc) + Schema.toArbitrary(Schema.Finite)(fc) deepStrictEqual(constraints, [ { noNaN: true }, @@ -185,7 +189,16 @@ describe("Arbitrary generation", () => { ]) }) - describe("report and candidates", () => { + it("should enforce opaque filters", () => { + const schema = Schema.Struct({ + a: Schema.String.check(Schema.makeFilter((s: string) => s.length > 0, { expected: "a custom string" })) + }) + const arbitrary = toArbitrary(schema) + + FastCheck.assert(FastCheck.property(arbitrary, (a) => a.a.length > 0), { numRuns: 5 }) + }) + + describe("candidates", () => { it("should use filter candidates with the merged constraint context", () => { let constraint: Schema.Annotations.ToArbitrary.GenerationConstraint | undefined const schema = Schema.String.check( @@ -202,11 +215,31 @@ describe("Arbitrary generation", () => { } }) ) - const result = Schema.toArbitrary(schema, { report: true }) + const arbitrary = toArbitrary(schema) - deepStrictEqual(result.report.warnings, []) deepStrictEqual(constraint, { minLength: 9 }) - FastCheck.assert(FastCheck.property(result.value, (s) => s === "candidate"), { numRuns: 20 }) + FastCheck.assert(FastCheck.property(arbitrary, (s) => s === "candidate"), { numRuns: 20 }) + }) + + it("should use filter group candidates", () => { + const schema = Schema.String.check( + Schema.makeFilterGroup( + [ + Schema.makeFilter((s: string) => s.startsWith("a"), { expected: "starts with a" }), + Schema.makeFilter((s: string) => s.endsWith("a"), { expected: "ends with a" }) + ], + { + arbitrary: { + candidate: { + make: (fc) => fc.constant("a") + } + } + } + ) + ) + const arbitrary = toArbitrary(schema) + + FastCheck.assert(FastCheck.property(arbitrary, (s) => s.startsWith("a") && s.endsWith("a")), { numRuns: 20 }) }) it("should allow candidates to be disabled for a context", () => { @@ -223,10 +256,9 @@ describe("Arbitrary generation", () => { } }) ) - const result = Schema.toArbitrary(schema, { report: true }) + toArbitrary(schema) strictEqual(calls, 1) - deepStrictEqual(result.report.warnings, []) }) it("should fail fast for invalid candidate weights", () => { @@ -243,61 +275,14 @@ describe("Arbitrary generation", () => { ) throws( - () => Schema.toArbitrary(makeSchema(0)), + () => toArbitrary(makeSchema(0)), "Unable to derive an arbitrary for a candidate with an invalid weight" ) throws( - () => Schema.toArbitrary(makeSchema(0.5)), + () => toArbitrary(makeSchema(0.5)), "Unable to derive an arbitrary for a candidate with an invalid weight" ) }) - - it("should report opaque filters", () => { - const schema = Schema.Struct({ - a: Schema.String.check(Schema.makeFilter((s: string) => s.length > 0, { expected: "a custom string" })) - }) - const result = Schema.toArbitrary(schema, { report: true }) - - deepStrictEqual(result.report.warnings, [ - { _tag: "OpaqueFilter", path: ["a"], description: "a custom string" } - ]) - FastCheck.assert(FastCheck.property(result.value, (a) => a.a.length > 0), { numRuns: 5 }) - }) - - it("should not report child filters when a filter group provides arbitrary metadata", () => { - const schema = Schema.String.check( - Schema.makeFilterGroup( - [ - Schema.makeFilter((s: string) => s.startsWith("a"), { expected: "starts with a" }), - Schema.makeFilter((s: string) => s.endsWith("a"), { expected: "ends with a" }) - ], - { - arbitrary: { - candidate: { - make: (fc) => fc.constant("a") - } - } - } - ) - ) - const result = Schema.toArbitrary(schema, { report: true }) - - deepStrictEqual(result.report.warnings, []) - FastCheck.assert(FastCheck.property(result.value, (s) => s.startsWith("a") && s.endsWith("a")), { numRuns: 20 }) - }) - - it("should not report warnings for constructive built-in filters", () => { - const schema = Schema.Struct({ - string: Schema.String.check(Schema.isMinLength(1), Schema.isStartsWith("a")), - number: Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 10 })), - array: Schema.Array(Schema.String).check(Schema.isMinLength(1), Schema.isUnique()), - object: Schema.Record(Schema.String, Schema.Number).check(Schema.isMinProperties(1), Schema.isMaxProperties(3)), - set: Schema.ReadonlySet(Schema.String).check(Schema.isMinSize(1), Schema.isMaxSize(3)) - }) - const result = Schema.toArbitrary(schema, { report: true }) - - deepStrictEqual(result.report.warnings, []) - }) }) describe("object property counts", () => { @@ -308,7 +293,7 @@ describe("Arbitrary generation", () => { c: Schema.optionalKey(Schema.String) }).check(Schema.isMinProperties(2)) FastCheck.assert( - FastCheck.property(Schema.toArbitrary(schema), (o) => globalThis.Object.keys(o).length >= 2), + FastCheck.property(toArbitrary(schema), (o) => globalThis.Object.keys(o).length >= 2), { numRuns: 100 } ) verifyGeneration(schema) @@ -321,7 +306,7 @@ describe("Arbitrary generation", () => { c: Schema.optionalKey(Schema.String) }).check(Schema.isMaxProperties(1)) FastCheck.assert( - FastCheck.property(Schema.toArbitrary(schema), (o) => globalThis.Object.keys(o).length <= 1), + FastCheck.property(toArbitrary(schema), (o) => globalThis.Object.keys(o).length <= 1), { numRuns: 100 } ) verifyGeneration(schema) @@ -334,7 +319,7 @@ describe("Arbitrary generation", () => { b: Schema.optionalKey(Schema.String) }).check(Schema.isMinProperties(2)) FastCheck.assert( - FastCheck.property(Schema.toArbitrary(schema), (o) => + FastCheck.property(toArbitrary(schema), (o) => globalThis.Reflect.ownKeys(o).length >= 2 && globalThis.Object.hasOwn(o, key)), { numRuns: 100 } @@ -350,7 +335,7 @@ describe("Arbitrary generation", () => { c: Schema.optionalKey(Schema.String) }).check(Schema.isPropertiesLengthBetween(2, 3)) FastCheck.assert( - FastCheck.property(Schema.toArbitrary(schema), (o) => { + FastCheck.property(toArbitrary(schema), (o) => { const n = globalThis.Object.keys(o).length return n >= 2 && n <= 3 }), @@ -384,7 +369,7 @@ describe("Arbitrary generation", () => { } const schema = Schema.Struct(fields).check(Schema.isMinProperties(64)) FastCheck.assert( - FastCheck.property(Schema.toArbitrary(schema), (o) => globalThis.Object.keys(o).length === 64), + FastCheck.property(toArbitrary(schema), (o) => globalThis.Object.keys(o).length === 64), { numRuns: 100 } ) }) @@ -598,7 +583,7 @@ describe("Arbitrary generation", () => { Schema.optionalKey(Schema.Number) ]) - FastCheck.assert(FastCheck.property(Schema.toArbitrary(schema), Schema.is(schema)), { + FastCheck.assert(FastCheck.property(toArbitrary(schema), Schema.is(schema)), { numRuns: 100, seed: 17 }) @@ -753,7 +738,7 @@ describe("Arbitrary generation", () => { a: Rec }) throws( - () => Schema.toArbitrary(schema), + () => toArbitrary(schema), (e) => { assertInstanceOf(e, Error) assertInclude( @@ -769,7 +754,7 @@ describe("Arbitrary generation", () => { const Rec = Schema.suspend((): Schema.Codec => schema) const schema: any = Schema.Array(Rec).check(Schema.isMinLength(1)) throws( - () => Schema.toArbitrary(schema), + () => toArbitrary(schema), (e) => { assertInstanceOf(e, Error) assertInclude( @@ -811,7 +796,7 @@ describe("Arbitrary generation", () => { [Schema.Union([Schema.Number, Rec])] ).check(Schema.isMinLength(2)) FastCheck.assert( - FastCheck.property(Schema.toArbitrary(schema), (a) => (a as Array).length >= 2), + FastCheck.property(toArbitrary(schema), (a) => (a as Array).length >= 2), { numRuns: 100 } ) }) @@ -1661,7 +1646,7 @@ describe("Arbitrary generation", () => { it("isBetweenBigDecimal with impossible exclusive bounds", () => { throws(() => - Schema.toArbitrary(Schema.BigDecimal.check(Schema.isBetweenBigDecimal({ + toArbitrary(Schema.BigDecimal.check(Schema.isBetweenBigDecimal({ minimum: BigDecimal.fromStringUnsafe("1.01"), maximum: BigDecimal.fromStringUnsafe("1.01"), exclusiveMinimum: true, @@ -1671,7 +1656,7 @@ describe("Arbitrary generation", () => { it("isGreaterThanBigDecimal + isLessThanBigDecimal with impossible bounds", () => { throws(() => - Schema.toArbitrary(Schema.BigDecimal.check( + toArbitrary(Schema.BigDecimal.check( Schema.isGreaterThanBigDecimal(BigDecimal.fromStringUnsafe("1.01")), Schema.isLessThanBigDecimal(BigDecimal.fromStringUnsafe("1.01")) )), "Unable to derive an arbitrary for the ordered BigDecimal constraints") diff --git a/packages/effect/test/schema/toDifferJsonPatch.test.ts b/packages/effect/test/schema/toDifferJsonPatch.test.ts index e8fbde06b53..9e35f6444ab 100644 --- a/packages/effect/test/schema/toDifferJsonPatch.test.ts +++ b/packages/effect/test/schema/toDifferJsonPatch.test.ts @@ -18,7 +18,7 @@ import { assertSchemaIssueError, deepStrictEqual, strictEqual, throws } from ".. function roundtrip(codec: Schema.Codec) { const differ = Schema.toDifferJsonPatch(codec) - const arbitrary = Schema.toArbitrary(codec) + const arbitrary = Schema.toArbitrary(codec)(FastCheck) const arb = arbitrary.filter((v) => { // avoid prototype-poisoning-ish values that aren't valid JSON-ish containers for patching if ( diff --git a/packages/effect/test/schema/toJsonSchemaDocument.test.ts b/packages/effect/test/schema/toJsonSchemaDocument.test.ts index 42c473ad56c..333e73b057d 100644 --- a/packages/effect/test/schema/toJsonSchemaDocument.test.ts +++ b/packages/effect/test/schema/toJsonSchemaDocument.test.ts @@ -43,7 +43,7 @@ function assertJsonSchemaDocument( const valid = ajvDraft2020_12.validateSchema(jsonSchema) assertTrue(valid) // const validate = ajvDraft2020_12.compile(jsonSchema) - // const arb = Schema.toArbitrary(schema) + // const arb = Schema.toArbitrary(schema)(FastCheck) // const codec = Schema.toCodecJson(schema) // const encode = Schema.encodeSync(codec) // FastCheck.assert(FastCheck.property(arb, (t) => { diff --git a/packages/effect/typetest/schema/toArbitrary.tst.ts b/packages/effect/typetest/schema/toArbitrary.tst.ts index afa0d0175f1..6a5c0e7eb03 100644 --- a/packages/effect/typetest/schema/toArbitrary.tst.ts +++ b/packages/effect/typetest/schema/toArbitrary.tst.ts @@ -11,30 +11,13 @@ describe("toArbitrary", () => { const arbitrary = Schema.toArbitrary(schema) expect(arbitrary).type.toBe< - FastCheck.Arbitrary<{ + Schema.Arbitrary<{ readonly name: string readonly age: number }> >() }) - it("returns a report when requested", () => { - const schema = Schema.Struct({ - name: Schema.String, - age: Schema.Number - }) - const result = Schema.toArbitrary(schema, { report: true }) - - expect(result).type.toBe< - Schema.Annotations.ToArbitrary.WithReport< - FastCheck.Arbitrary<{ - readonly name: string - readonly age: number - }> - > - >() - }) - it("passes recursion metadata in the arbitrary context", () => { Schema.String.annotate({ toArbitrary: () => (fc, context) => { diff --git a/packages/tools/bundle/fixtures/schema-toArbitraryLazy.ts b/packages/tools/bundle/fixtures/schema-toArbitrary.ts similarity index 75% rename from packages/tools/bundle/fixtures/schema-toArbitraryLazy.ts rename to packages/tools/bundle/fixtures/schema-toArbitrary.ts index fc5fb658034..191ad957de1 100644 --- a/packages/tools/bundle/fixtures/schema-toArbitraryLazy.ts +++ b/packages/tools/bundle/fixtures/schema-toArbitrary.ts @@ -6,4 +6,4 @@ const schema = Schema.Struct({ c: Schema.Array(Schema.String) }) -export const arbitrary = Schema.toArbitraryLazy(schema) +export const arbitrary = Schema.toArbitrary(schema) diff --git a/packages/vitest/src/internal/internal.ts b/packages/vitest/src/internal/internal.ts index d4040a00978..f7717b0a60f 100644 --- a/packages/vitest/src/internal/internal.ts +++ b/packages/vitest/src/internal/internal.ts @@ -129,7 +129,7 @@ const makeTester = ( if (Array.isArray(arbitraries)) { const arbs = arbitraries.map((arbitrary) => { if (Schema.isSchema(arbitrary)) { - return Schema.toArbitrary(arbitrary) + return Schema.toArbitrary(arbitrary)(fc) } return arbitrary as fc.Arbitrary }) @@ -150,7 +150,7 @@ const makeTester = ( const arbs = fc.record( Object.keys(arbitraries).reduce(function(result, key) { const arb: any = arbitraries[key] - Rec.assignProperty(result, key, Schema.isSchema(arb) ? Schema.toArbitrary(arb) : arb) + Rec.assignProperty(result, key, Schema.isSchema(arb) ? Schema.toArbitrary(arb)(fc) : arb) return result }, {} as Record>) )