Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add Type to fast-check arbitrary adapter #1151

Merged
merged 30 commits into from
Oct 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
ccec856
Update error message for objects that are not reference equal
ShawnMorreau May 17, 2024
2b54b0b
update to main
ShawnMorreau May 17, 2024
d131754
Merge branch 'main' of github.com:arktypeio/arktype
ShawnMorreau May 20, 2024
214c18b
Merge branch 'main' of github.com:arktypeio/arktype
ShawnMorreau Jun 20, 2024
4083743
Merge branch 'main' of github.com:arktypeio/arktype
ShawnMorreau Aug 24, 2024
a18eba7
Merge branch 'main' of github.com:arktypeio/arktype
ShawnMorreau Sep 27, 2024
b2ea61a
fast-check
ShawnMorreau Sep 27, 2024
b25d246
remove unknown file
ShawnMorreau Sep 27, 2024
b621d2f
pr changes v1
ShawnMorreau Oct 2, 2024
0e33b0e
add nearestFloat
ssalbdivad Oct 2, 2024
d6b3092
no infinito
ssalbdivad Oct 2, 2024
864996c
pr changes
ShawnMorreau Oct 8, 2024
4a09e06
Merge branch 'main' of github.com:arktypeio/arktype into fast-check
ShawnMorreau Oct 8, 2024
dbe9841
arbitrary cache, change converted structure node to include ID
ShawnMorreau Oct 8, 2024
24f4658
pr
ShawnMorreau Oct 8, 2024
cb28d4d
change arfbork example
ShawnMorreau Oct 8, 2024
f41f944
remove large if, add new divisor logic
ShawnMorreau Oct 8, 2024
49b2662
remove unused import
ShawnMorreau Oct 8, 2024
486d660
fastcheck
ShawnMorreau Oct 19, 2024
b9fdc46
.
ShawnMorreau Oct 19, 2024
29c9204
Merge branch 'main' of github.com:arktypeio/arktype into fast-check
ShawnMorreau Oct 19, 2024
dabcff0
.
ShawnMorreau Oct 21, 2024
77c9a40
constrained array keyword
ShawnMorreau Oct 22, 2024
ff7611a
.
ShawnMorreau Oct 23, 2024
2632af5
fast-check
ShawnMorreau Oct 24, 2024
4b7f51b
re-add test
ShawnMorreau Oct 24, 2024
6be18fe
pass rintersection node and remove refinements from ctx
ShawnMorreau Oct 25, 2024
7188fc2
naming change
ShawnMorreau Oct 25, 2024
f187090
remove todo
ShawnMorreau Oct 25, 2024
e037728
pr updates
ShawnMorreau Oct 25, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
342 changes: 342 additions & 0 deletions ark/fast-check/__tests__/arktypeFastCheck.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,342 @@
import { attest } from "@ark/attest"
import { arkToArbitrary } from "@ark/fast-check/internal/arktypeFastCheck.ts"
import { scope, type } from "arktype"
import { type Arbitrary, assert, property } from "fast-check"
import { describe } from "mocha"

describe("Arbitrary Generation", () => {
describe("union", () => {
it("boolean", () => {
const t = type("boolean")
const arbitrary = arkToArbitrary(t)
return assertProperty(arbitrary, t)
})
it("number|string", () => {
const t = type("number|string")
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
})
describe("number", () => {
it("number", () => {
const t = type("number")
const arbitrary = arkToArbitrary(t)
return assertProperty(arbitrary, t)
})
it("Tight Bound", () => {
const t = type("4<number<5")
const arbitrary = arkToArbitrary(t)
return assertProperty(arbitrary, t)
})
it("Integer", () => {
const t = type("number.integer")
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
it("Invalid Bound", () => {
const t = type("4<number.integer<5")
attest(() => assertProperty(arkToArbitrary(t), t)).throws(
"No integer value satisfies >5 & <4"
)
})
it("equals", () => {
const t = type("number==2")
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
it("divisible", () => {
const t = type("number%2")
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
it("large divisor", () => {
const t = type("number%7654321001>1")
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
it("divisible within range", () => {
const t = type("15<number%7<39")
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
it("non-divisible within range", () => {
const t = type("52<number%10<58")
attest(() => arkToArbitrary(t)).throws(
"No values within range 53 - 57 are divisible by 10."
)
})
})
describe("string", () => {
it("string", () => {
const t = type("string")
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
it("bounded string", () => {
const t = type("string < 5")
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
it("double bounded string", () => {
const t = type("3<string <= 8")
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
it("regex", () => {
const t = type("string.email")
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
it("multiple regexes", () => {
const t = type("string.email").and("string.alpha")
attest(() => arkToArbitrary(t)).throws(
"Multiple regexes on a single node is not supported."
)
})
it("bounded regex", () => {
const t = type("string.email<5")
attest(() => arkToArbitrary(t)).throws("Bounded regex is not supported.")
})
})
describe("misc", () => {
it("unknown", () => {
const t = type("unknown")
const arbitrary = arkToArbitrary(t)
return assertProperty(arbitrary, t)
})
it("unknown[]", () => {
const t = type("unknown[]")
const arbitrary = arkToArbitrary(t)
return assertProperty(arbitrary, t)
})
it("bigint", () => {
const t = type("bigint")
const arbitrary = arkToArbitrary(t)
return assertProperty(arbitrary, t)
})
it("symbol", () => {
const t = type("symbol")
const arbitrary = arkToArbitrary(t)
return assertProperty(arbitrary, t)
})
it("false", () => {
const t = type("false")
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
it("true", () => {
const t = type("true")
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
it("literal number", () => {
const t = type("0.5")
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
it("literal string", () => {
const t = type("'hello'")
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
it("morph", () => {
const t = type(["string<5", "=>", val => `${val}`])
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
})

describe("array", () => {
it("Array keyword", () => {
const t = type("Array")
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
it("constrained Array keyword", () => {
const t = type("Array<2")
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
it("string[]", () => {
const t = type("string[]")
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
it("number[][]", () => {
const t = type("number[][]")
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
it("bounded array", () => {
const t = type("3<number[]<=5")
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
it("union array", () => {
const t = type("(string|number)[]")
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
})
describe("tuple", () => {
it("empty tuple", () => {
const t = type([])
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
it("one element tuple", () => {
const t = type(["string"])
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
it("two element tuple", () => {
const t = type(["string", "number"])
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})

it("just variadic", () => {
const t = type(["...", "string[]"])
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
it("variadic", () => {
const t = type(["number", "...", "string[]"])
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
it("one element optional tuple", () => {
const t = type(["string?"])
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
it("tuple with optional", () => {
const t = type(["number", "string>2?"])
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
})
describe("object", () => {
it("{}", () => {
const t = type({})
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
it("object keyword", () => {
const t = type("object")
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
it("object with optional key", () => {
const t = type({ a: "string", "b?": "3<number<5" })
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
it("nested object", () => {
const t = type({
a: {
b: "string >= 2",
"c?": "string"
},
"d?": "number"
})
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
it("intersected object", () => {
const t = type([{ a: "string" }, "&", { b: "number" }])
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
it("symbol key", () => {
const s = Symbol()
const t = type({
[s]: "string"
})
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
it("index signature", () => {
const t = type({ "[string]": "number|string" })
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
it("multiple index signatures", () => {
const t = type({
"[string?]": "number|string",
"[symbol]": "string"
})
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
it("symbol index signature", () => {
const t = type({ "[symbol]": "number|string" })
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
it("contains alias", () => {
const example = {
user: {
name: "string",
friends: "user[]"
}
} as const
const t = scope(example).type("user")
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
it("cyclic", () => {
const $ = scope({
arf2: {
b: "bork2"
},
bork2: {
c: "arf2&bork2"
}
}).export()
attest(() => arkToArbitrary($.arf2)).throws(
"Infinitely deep cycles are not supported."
)
})
it("unknown array with additional props", () => {
const t = type({ name: "string" }).and("unknown[]")
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
it("array keyword with additional propss", () => {
const t = type({ name: "string" }).and("Array<4")
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
it("array with additional propss", () => {
const t = type({ name: "string" }).and("string[]")
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
})
describe("proto", () => {
it("Set", () => {
const t = type("Set")
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
it("Date", () => {
const t = type("Date")
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
it("bounded date", () => {
const t = type("d'2001/10/10'<Date<=d'2005/10/10'")
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
})
})

const assertProperty = (arbitrary: Arbitrary<unknown>, schema: type.Any) =>
assert(
property(arbitrary, value => {
schema.assert(value)
return true
})
)
53 changes: 53 additions & 0 deletions ark/fast-check/arbitraries/array.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import type { nodeOfKind, SequenceTuple } from "@ark/schema"
import * as fc from "fast-check"
import type { Ctx } from "../fastCheckContext.ts"

export const getPossiblyWeightedArray = (
arrArbitrary: fc.Arbitrary<unknown[]>,
node: nodeOfKind<"sequence">,
ctx: Ctx
): fc.Arbitrary<unknown[]> =>
ctx.tieStack.length ?
fc.oneof(
{
maxDepth: 2,
depthIdentifier: `id:${node.id}`
},
{ arbitrary: fc.constant([]), weight: 1 },
{
arbitrary: arrArbitrary,
weight: 2
}
)
: arrArbitrary

export const spreadVariadicElements = (
tupleArbitraries: fc.Arbitrary<unknown>[],
tupleElements: SequenceTuple
): fc.Arbitrary<unknown[]> =>
fc.tuple(...tupleArbitraries).chain(arr => {
const arrayWithoutOptionals = []
const arrayWithOptionals = []

for (const i in arr) {
if (tupleElements[i].kind === "variadic") {
const generatedValuesArray = (arr[i] as unknown[]).map(val =>
fc.constant(val)
)
arrayWithoutOptionals.push(...generatedValuesArray)
arrayWithOptionals.push(...generatedValuesArray)
} else if (tupleElements[i].kind === "optionals")
arrayWithOptionals.push(fc.constant(arr[i]))
else {
arrayWithoutOptionals.push(fc.constant(arr[i]))
arrayWithOptionals.push(fc.constant(arr[i]))
}
}
if (arrayWithOptionals.length !== arrayWithoutOptionals.length) {
return fc.oneof(
fc.tuple(...arrayWithoutOptionals),
fc.tuple(...arrayWithOptionals)
)
}
return fc.tuple(...arrayWithOptionals)
})
Loading