1import * as Arbitrary from "effect/Arbitrary"
2import * as Effect from "effect/Effect"
3import * as Result from "effect/Result"
4import * as Schema from "effect/Schema"
5import * as TestSchema from "effect/testing/TestSchema"
6import assert from "node:assert/strict"
7import {
8 makeBigDecimalSchema,
9 makeConstrainedStringSchema,
10 makeDateTimeUtcSchema,
11 makeDateTimeZonedSchema,
12 makeOptionalStructSchema,
13 makeRareFilterSchema,
14 makeRegExpSchema,
15 makeTreeSchema,
16 makeUniqueArraySchema,
17 regularExpression,
18 validateNumbers,
19 validateRegExpCoverage,
20 validateRegExpValues,
21 validateSchemaValues,
22 validateStrings,
23 validateTrees,
24 validateUint8Arrays
25} from "./schema.ts"
26
27const seed = 42
28const recursiveSeed = 188
29const regExpShrinkSeed = 0
30const size = 10
31
32interface FlatMapValue {
33 readonly length: number
34 readonly values: ReadonlyArray<number>
35}
36
37const flatMapSource = Arbitrary.schema(Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 8 })))
38const flatMapItem = Schema.Int.check(Schema.isBetween({ minimum: -1_000, maximum: 1_000 }))
39const flatMapTargets = globalThis.Array.from({ length: 8 }, (_, index) => {
40 const length = index + 1
41 return Arbitrary.schema(
42 Schema.Array(flatMapItem).check(Schema.isMinLength(length), Schema.isMaxLength(length))
43 ).pipe(Arbitrary.map((values): FlatMapValue => ({ length, values })))
44})
45const makeFlatMapArbitrary = () => flatMapSource.pipe(Arbitrary.flatMap((length) => flatMapTargets[length - 1]))
46
47export const coldRecursiveFirstSample = () => ({
48 run: () =>
49 Effect.runSync(
50 Arbitrary.sampleEffect(Arbitrary.schema(makeTreeSchema()), { count: 1, seed, size: 1 })
51 ),
52 validate: validateTrees(1, 2, 2)
53})
54
55export const recursiveSample32 = () => {
56 const arbitrary = Arbitrary.schema(makeTreeSchema())
57 const program = Arbitrary.sampleEffect(arbitrary, { count: 32, seed: recursiveSeed, size: 3 })
58 return {
59 run: () => Effect.runSync(program),
60 validate: validateTrees(32, 90, 110)
61 }
62}
63
64export const optionalStructSample128 = () => {
65 const schema = makeOptionalStructSchema()
66 const arbitrary = Arbitrary.schema(schema)
67 const program = Arbitrary.sampleEffect(arbitrary, { count: 128, maxDiscards: 0, seed, size: 8 })
68 return {
69 run: () => Effect.runSync(program),
70 validate: validateSchemaValues(schema, 128)
71 }
72}
73
74export const constrainedStringSample128 = () => {
75 const arbitrary = Arbitrary.schema(makeConstrainedStringSchema())
76 const program = Arbitrary.sampleEffect(arbitrary, { count: 128, maxDiscards: 0, seed, size })
77 return {
78 run: () => Effect.runSync(program),
79 validate: validateStrings(128)
80 }
81}
82
83export const coldRegExpFirstSample = () => ({
84 run: () =>
85 Effect.runSync(
86 Arbitrary.sampleEffect(Arbitrary.schema(makeRegExpSchema()), {
87 count: 1,
88 maxDiscards: 0,
89 seed,
90 size: 48
91 })
92 ),
93 validate: validateRegExpValues(1)
94})
95
96export const regExpSample64 = () => {
97 const arbitrary = Arbitrary.schema(makeRegExpSchema())
98 const program = Arbitrary.sampleEffect(arbitrary, { count: 64, maxDiscards: 0, seed, size: 48 })
99 return {
100 run: () => Effect.runSync(program),
101 validate: validateRegExpCoverage
102 }
103}
104
105export const regExpCheckFalsifyAndShrink = () => {
106 const arbitrary = Arbitrary.schema(makeRegExpSchema())
107 const program = Arbitrary.checkEffect(arbitrary, () => false, {
108 runs: 1,
109 maxDiscards: 0,
110 seed: regExpShrinkSeed,
111 size: 48
112 })
113 return {
114 run: () => Effect.runSync(program),
115 validate: (result: Arbitrary.CheckResult<string, never>) => {
116 assert.equal(result._tag, "Falsified")
117 if (result._tag !== "Falsified") return
118 validateRegExpValues(2)([result.initialInput, result.shrunkInput])
119 assert.equal(result.shrinks > 0, true)
120 }
121 }
122}
123
124export const boundedNumberSample128 = () => {
125 const arbitrary = Arbitrary.schema(Schema.Number.check(Schema.isBetween({ minimum: 2, maximum: 4 })))
126 const program = Arbitrary.sampleEffect(arbitrary, { count: 128, maxDiscards: 0, seed, size })
127 return {
128 run: () => Effect.runSync(program),
129 validate: validateNumbers(128)
130 }
131}
132
133export const uint8ArraySample128 = () => {
134 const arbitrary = Arbitrary.schema(Schema.Uint8Array)
135 const program = Arbitrary.sampleEffect(arbitrary, { count: 128, maxDiscards: 0, seed, size })
136 return {
137 run: () => Effect.runSync(program),
138 validate: validateUint8Arrays(128)
139 }
140}
141
142export const bigDecimalSample128 = () => {
143 const schema = makeBigDecimalSchema()
144 const arbitrary = Arbitrary.schema(schema)
145 const program = Arbitrary.sampleEffect(arbitrary, { count: 128, maxDiscards: 0, seed, size })
146 return {
147 run: () => Effect.runSync(program),
148 validate: validateSchemaValues(schema, 128)
149 }
150}
151
152export const dateTimeUtcSample128 = () => {
153 const schema = makeDateTimeUtcSchema()
154 const arbitrary = Arbitrary.schema(schema)
155 const program = Arbitrary.sampleEffect(arbitrary, { count: 128, maxDiscards: 0, seed, size })
156 return {
157 run: () => Effect.runSync(program),
158 validate: validateSchemaValues(schema, 128)
159 }
160}
161
162export const timeZoneNamedSample128 = () => {
163 const arbitrary = Arbitrary.schema(Schema.TimeZoneNamed)
164 const program = Arbitrary.sampleEffect(arbitrary, { count: 128, maxDiscards: 0, seed, size })
165 return {
166 run: () => Effect.runSync(program),
167 validate: validateSchemaValues(Schema.TimeZoneNamed, 128)
168 }
169}
170
171export const timeZoneSample128 = () => {
172 const arbitrary = Arbitrary.schema(Schema.TimeZone)
173 const program = Arbitrary.sampleEffect(arbitrary, { count: 128, maxDiscards: 0, seed, size })
174 return {
175 run: () => Effect.runSync(program),
176 validate: validateSchemaValues(Schema.TimeZone, 128)
177 }
178}
179
180export const dateTimeZonedSample128 = () => {
181 const schema = makeDateTimeZonedSchema()
182 const arbitrary = Arbitrary.schema(schema)
183 const program = Arbitrary.sampleEffect(arbitrary, { count: 128, maxDiscards: 0, seed, size })
184 return {
185 run: () => Effect.runSync(program),
186 validate: validateSchemaValues(schema, 128)
187 }
188}
189
190export const rareFilterSample32 = () => {
191 const arbitrary = Arbitrary.schema(makeRareFilterSchema())
192 const program = Arbitrary.sampleEffect(arbitrary, { count: 32, maxDiscards: 2_048, seed, size })
193 return {
194 run: () => Effect.runSync(program),
195 validate: (values: ReadonlyArray<number>) => {
196 assert.equal(values.length, 32)
197 assert.equal(values.every((value) => value % 16 === 0), true)
198 }
199 }
200}
201
202export const uniqueArraySample32 = () => {
203 const arbitrary = Arbitrary.schema(makeUniqueArraySchema())
204 const program = Arbitrary.sampleEffect(arbitrary, { count: 32, maxDiscards: 2_048, seed, size })
205 return {
206 run: () => Effect.runSync(program),
207 validate: (values: ReadonlyArray<ReadonlyArray<number>>) => {
208 assert.equal(values.length, 32)
209 assert.equal(values.every((value) => value.length === 32 && new Set(value).size === 32), true)
210 }
211 }
212}
213
214export const literalSample128 = () => {
215 const arbitrary = Arbitrary.schema(Schema.Literal("value"))
216 const program = Arbitrary.sampleEffect(arbitrary, { count: 128, maxDiscards: 0, seed, size })
217 return {
218 run: () => Effect.runSync(program),
219 validate: (values: ReadonlyArray<unknown>) => {
220 assert.equal(values.length, 128)
221 assert.equal(values.every((value) => value === "value"), true)
222 }
223 }
224}
225
226export const mapSample128 = () => {
227 const arbitrary = Arbitrary.map(
228 Arbitrary.schema(Schema.Int.check(Schema.isBetween({ minimum: 0, maximum: 1_000 }))),
229 (value) => value + 1
230 )
231 const program = Arbitrary.sampleEffect(arbitrary, { count: 128, maxDiscards: 0, seed, size })
232 return {
233 run: () => Effect.runSync(program),
234 validate: (values: ReadonlyArray<number>) => {
235 assert.equal(values.length, 128)
236 assert.equal(values.every((value) => value >= 1 && value <= 1_001), true)
237 }
238 }
239}
240
241export const passingFilterSample128 = () => {
242 const arbitrary = Arbitrary.filter(
243 Arbitrary.schema(Schema.Int.check(Schema.isBetween({ minimum: 0, maximum: 1_000 }))),
244 (value) => value >= 0
245 )
246 const program = Arbitrary.sampleEffect(arbitrary, { count: 128, maxDiscards: 0, seed, size })
247 return {
248 run: () => Effect.runSync(program),
249 validate: (values: ReadonlyArray<number>) => {
250 assert.equal(values.length, 128)
251 assert.equal(values.every((value) => value >= 0 && value <= 1_000), true)
252 }
253 }
254}
255
256export const selectiveFilterSample32 = () => {
257 const arbitrary = Arbitrary.filter(
258 Arbitrary.schema(Schema.Int.check(Schema.isBetween({ minimum: 0, maximum: 255 }))),
259 (value) => value % 16 === 0
260 )
261 const program = Arbitrary.sampleEffect(arbitrary, { count: 32, maxDiscards: 2_048, seed, size })
262 return {
263 run: () => Effect.runSync(program),
264 validate: (values: ReadonlyArray<number>) => {
265 assert.equal(values.length, 32)
266 assert.equal(values.every((value) => value % 16 === 0), true)
267 }
268 }
269}
270
271export const filterMapSample128 = () => {
272 const arbitrary = Arbitrary.filterMap(
273 Arbitrary.schema(Schema.Int.check(Schema.isBetween({ minimum: 0, maximum: 255 }))),
274 (value) => value % 2 === 0 ? Result.succeed(value / 2) : Result.fail(value)
275 )
276 const program = Arbitrary.sampleEffect(arbitrary, { count: 128, maxDiscards: 2_048, seed, size })
277 return {
278 run: () => Effect.runSync(program),
279 validate: (values: ReadonlyArray<number>) => {
280 assert.equal(values.length, 128)
281 assert.equal(values.every((value) => Number.isInteger(value) && value >= 0 && value <= 127), true)
282 }
283 }
284}
285
286export const filterCheckFalsifyAndShrink = () => {
287 const arbitrary = Arbitrary.filter(
288 Arbitrary.schema(Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 8 }))),
289 (value) => value === 8 || value === 5 || value === 4
290 )
291 const program = Arbitrary.checkEffect(arbitrary, () => false, {
292 runs: 1,
293 seed: 47,
294 size,
295 maxShrinks: 100
296 })
297 return {
298 run: () => Effect.runSync(program),
299 validate: (result: Arbitrary.CheckResult<number, never>) => {
300 assert.equal(result._tag, "Falsified")
301 if (result._tag !== "Falsified") return
302 assert.equal(result.initialInput, 8)
303 assert.equal(result.shrunkInput, 4)
304 assert.equal(result.shrinks, 2)
305 }
306 }
307}
308
309export const allTupleSample128 = () => {
310 const arbitrary = Arbitrary.all([
311 Arbitrary.schema(Schema.Literal("left")),
312 Arbitrary.schema(Schema.Literal(1))
313 ])
314 const program = Arbitrary.sampleEffect(arbitrary, { count: 128, maxDiscards: 0, seed, size })
315 return {
316 run: () => Effect.runSync(program),
317 validate: (values: ReadonlyArray<["left", 1]>) => {
318 assert.equal(values.length, 128)
319 assert.equal(values.every(([left, right]) => left === "left" && right === 1), true)
320 }
321 }
322}
323
324export const allRecordSample128 = () => {
325 const arbitrary = Arbitrary.all({
326 name: Arbitrary.schema(Schema.Literals(["Ada", "Grace"])),
327 age: Arbitrary.schema(Schema.Int)
328 })
329 const program = Arbitrary.sampleEffect(arbitrary, { count: 128, maxDiscards: 0, seed, size })
330 return {
331 run: () => Effect.runSync(program),
332 validate: (values: ReadonlyArray<{ readonly name: string; readonly age: number }>) => {
333 assert.equal(values.length, 128)
334 assert.equal(values.every((value) => value.name === "Ada" || value.name === "Grace"), true)
335 }
336 }
337}
338
339export const flatMapSample128 = () => {
340 const flatMapArbitrary = makeFlatMapArbitrary()
341 const program = Arbitrary.sampleEffect(flatMapArbitrary, { count: 128, maxDiscards: 0, seed, size })
342 return {
343 run: () => Effect.runSync(program),
344 validate: (values: ReadonlyArray<FlatMapValue>) => {
345 assert.equal(values.length, 128)
346 assert.equal(values.every((value) => value.values.length === value.length), true)
347 }
348 }
349}
350
351export const flatMapCheckFalsifyAndShrink = () => {
352 const flatMapArbitrary = makeFlatMapArbitrary()
353 const program = Arbitrary.checkEffect(flatMapArbitrary, () => false, { runs: 1, seed, size })
354 return {
355 run: () => Effect.runSync(program),
356 validate: (result: Arbitrary.CheckResult<FlatMapValue, never>) => {
357 assert.equal(result._tag, "Falsified")
358 if (result._tag !== "Falsified") return
359 assert.equal(result.shrunkInput.length, 1)
360 assert.equal(result.shrunkInput.values.length, 1)
361 }
362 }
363}
364
365export const flatMapCheckReplay = () => {
366 const flatMapArbitrary = makeFlatMapArbitrary()
367 const property = () => false
368 const initial = Effect.runSync(Arbitrary.checkEffect(flatMapArbitrary, property, { runs: 1, seed, size }))
369 assert.equal(initial._tag, "Falsified")
370 if (initial._tag !== "Falsified") throw new Error("Expected the flatMap replay setup to falsify")
371 const program = Arbitrary.checkEffect(flatMapArbitrary, property, { replay: initial.replay })
372 return {
373 run: () => Effect.runSync(program),
374 validate: (result: Arbitrary.CheckResult<FlatMapValue, never>) => {
375 assert.equal(result._tag, "Falsified")
376 if (result._tag !== "Falsified") return
377 assert.equal(result.shrunkInput.length, 1)
378 assert.equal(result.shrunkInput.values.length, 1)
379 }
380 }
381}
382
383export const checkPass100 = () => {
384 const arbitrary = Arbitrary.schema(Schema.Int)
385 const program = Arbitrary.checkEffect(arbitrary, () => true, { runs: 100, seed, size })
386 return {
387 run: () => Effect.runSync(program),
388 validate: (result: Arbitrary.CheckResult<number, never>) => {
389 assert.deepEqual(result, { _tag: "Passed", runs: 100, discards: 0 })
390 }
391 }
392}
393
394export const arrayCheckPass100 = () => {
395 const arbitrary = Arbitrary.schema(Schema.Array(Schema.Int))
396 const program = Arbitrary.checkEffect(arbitrary, () => true, { runs: 100, seed, size: 50 })
397 return {
398 run: () => Effect.runSync(program),
399 validate: (result: Arbitrary.CheckResult<ReadonlyArray<number>, never>) => {
400 assert.deepEqual(result, { _tag: "Passed", runs: 100, discards: 0 })
401 }
402 }
403}
404
405export const arrayCheckFalsifyAndShrink = () => {
406 const arbitrary = Arbitrary.schema(Schema.Array(Schema.Literals([8, 27, 0, 1])))
407 const property = (values: ReadonlyArray<number>) => !(values.includes(27) && values.indexOf(0) > values.indexOf(27))
408 const program = Arbitrary.checkEffect(arbitrary, property, { runs: 1, seed: 1967, size: 4 })
409 return {
410 run: () => Effect.runSync(program),
411 validate: (result: Arbitrary.CheckResult<ReadonlyArray<number>, never>) => {
412 assert.equal(result._tag, "Falsified")
413 if (result._tag !== "Falsified") return
414 assert.deepEqual(result.initialInput, [8, 27, 0, 1])
415 // Both revisions must preserve the failure; the regression test checks the new, smaller result.
416 assert.equal(property(result.shrunkInput), false)
417 assert.ok(result.shrunkInput.length <= 3)
418 }
419 }
420}
421
422export const testSchemaVerifyGeneration100 = () => {
423 const asserts = new TestSchema.Asserts(Schema.Int)
424 return {
425 run: () => asserts.arbitrary().verifyGeneration({ runs: 100, seed }),
426 validate: (result: void) => assert.equal(result, undefined)
427 }
428}
429
430export const checkFalsifyAndShrink = () => {
431 const arbitrary = Arbitrary.schema(
432 Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 1_000 }))
433 )
434 const program = Arbitrary.checkEffect(arbitrary, (value) => value < 0, { runs: 1, seed: 47, size })
435 return {
436 run: () => Effect.runSync(program),
437 validate: (result: Arbitrary.CheckResult<number, never>) => {
438 assert.equal(result._tag, "Falsified")
439 if (result._tag !== "Falsified") return
440 assert.equal(result.initialInput, 1_000)
441 assert.equal(result.shrunkInput, 1)
442 assert.equal(result.shrinks, 1)
443 }
444 }
445}
446
447export const checkReplay = () => {
448 const arbitrary = Arbitrary.schema(
449 Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 1_000 }))
450 )
451 const property = (value: number) => value < 0
452 const initial = Effect.runSync(Arbitrary.checkEffect(arbitrary, property, { runs: 1, seed: 47, size }))
453 assert.equal(initial._tag, "Falsified")
454 if (initial._tag !== "Falsified") throw new Error("Expected the replay setup to falsify")
455 const program = Arbitrary.checkEffect(arbitrary, property, { replay: initial.replay })
456 return {
457 run: () => Effect.runSync(program),
458 validate: (result: Arbitrary.CheckResult<number, never>) => {
459 assert.equal(result._tag, "Falsified")
460 if (result._tag !== "Falsified") return
461 assert.equal(result.initialInput, 1_000)
462 assert.equal(result.shrunkInput, 1)
463 assert.equal(result.shrinks, 1)
464 }
465 }
466}