From dc667eab56f8af22aec80fa32ee305ac1be2a55e Mon Sep 17 00:00:00 2001
From: Paul Visscher
Date: Thu, 13 Feb 2025 15:45:10 +0100
Subject: [PATCH] feat: implement ZodSet visitor and add set type support
---
src/lib/primitives/zod/zod.ts | 9 +-
src/lib/visitor/arbitrary/arbitrary.ts | 10 +-
src/lib/visitor/arbitrary/zod.spec.ts | 716 +++++++++++++++++++++++++
3 files changed, 731 insertions(+), 4 deletions(-)
diff --git a/src/lib/primitives/zod/zod.ts b/src/lib/primitives/zod/zod.ts
index 3c51df35..31f44468 100644
--- a/src/lib/primitives/zod/zod.ts
+++ b/src/lib/primitives/zod/zod.ts
@@ -373,8 +373,13 @@ export const zodVisitor: {
ZodMap: () => {
throw new Error('Function not implemented.')
},
- ZodSet: () => {
- throw new Error('Function not implemented.')
+ ZodSet: (node, ctx) => {
+ const value = $array(ctx.render(node._def.valueType), {
+ minItems: node._def.minSize?.value,
+ maxItems: node._def.maxSize?.value,
+ set: true,
+ })
+ return value
},
ZodFunction: () => {
throw new Error('Function not implemented.')
diff --git a/src/lib/visitor/arbitrary/arbitrary.ts b/src/lib/visitor/arbitrary/arbitrary.ts
index aa0d2cd4..b4a190f2 100644
--- a/src/lib/visitor/arbitrary/arbitrary.ts
+++ b/src/lib/visitor/arbitrary/arbitrary.ts
@@ -397,15 +397,21 @@ export const arbitraryVisitor: ThereforeVisitor, ArbitraryCon
}
return composite
},
- array: ({ _children: [items], _options: options }, context) => {
+ array: ({ _children: [items], _options: options, _origin: { zod } }, context) => {
const { minLength, maxLength, ...restArbitrary } = options.arbitrary ?? {}
const child = context.arbitrary(items)
if (options.set === true) {
- return set(child, {
+ const value = set(child, {
minLength: options.minItems ?? minLength,
maxLength: options.maxItems ?? maxLength,
...restArbitrary,
})
+
+ if (zod !== undefined && (zod as ZodFirstPartySchemaTypes)._def.typeName === 'ZodSet') {
+ return value.map((x) => new Set(x))
+ }
+
+ return value
}
return array(child, {
minLength: options.minItems ?? minLength,
diff --git a/src/lib/visitor/arbitrary/zod.spec.ts b/src/lib/visitor/arbitrary/zod.spec.ts
index 52398950..beee153d 100644
--- a/src/lib/visitor/arbitrary/zod.spec.ts
+++ b/src/lib/visitor/arbitrary/zod.spec.ts
@@ -499,3 +499,719 @@ describe('formats', () => {
})
})
})
+
+describe('sets', () => {
+ it('basic set', () => {
+ const schema = z.set(z.string())
+ forAll(arbitrary(schema), (s) => {
+ schema.parse(s)
+ })
+ const schemaRef = $ref(schema)
+ forAll(arbitrary(schemaRef), (s) => {
+ schema.parse(s)
+ })
+
+ const ctx = arbitraryContext({ rng: xoroshiro128plus(1638968569864n) })
+ const arb = arbitrary(schema)
+ expect(Array.from({ length: 10 }, () => arb.sample(ctx))).toMatchInlineSnapshot(`
+ [
+ Set {
+ "L#",
+ "#'E1.9e+",
+ ",aM^sy{ITK",
+ "1dg",
+ "}x",
+ "Ko2",
+ },
+ Set {
+ "D.7Q",
+ "]k",
+ "hCz^<",
+ "U",
+ "",
+ "RK+}b\`c",
+ "ZDX|T8n!>!",
+ "v#.THNR|Lw",
+ "a)m[x7Fjw",
+ "]i#_JsG",
+ },
+ Set {
+ "CSN",
+ "",
+ "#_1@n",
+ ",uvdd&TsF%",
+ "-D[d6g",
+ ")a#\`)wU~v",
+ },
+ Set {
+ "",
+ "Bb|/H[f-?",
+ "#'A]1",
+ "4DEu:",
+ "[",
+ "Et{",
+ "c[F2D",
+ },
+ Set {
+ "R,/+YM",
+ "^",
+ },
+ Set {},
+ Set {
+ "8+"Q&",
+ "$ab#&r",
+ "z}P.t"M",
+ "]",
+ ",",
+ "w",
+ "001$S9",
+ "!z",
+ " "",
+ },
+ Set {
+ "|PQmG4{8",
+ ".",
+ ",L-~s",
+ "S=YoNx?WP{",
+ "q",
+ ":H,CSO@v$",
+ "Z1zxsSqu$",
+ "7H\\12W ",
+ "+:dI(/<",
+ "",
+ },
+ Set {},
+ Set {
+ "AUx\\\`53",
+ "w>YD3d+w",
+ "",
+ "EeHO8x",
+ },
+ ]
+ `)
+ })
+
+ it('min', () => {
+ const schema = z.set(z.number()).min(2)
+ forAll(arbitrary(schema), (s) => {
+ schema.parse(s)
+ })
+ const schemaRef = $ref(schema)
+ forAll(arbitrary(schemaRef), (s) => {
+ schema.parse(s)
+ })
+
+ const ctx = arbitraryContext({ rng: xoroshiro128plus(1638968569864n) })
+ const arb = arbitrary(schema)
+ expect(Array.from({ length: 10 }, () => arb.sample(ctx))).toMatchInlineSnapshot(`
+ [
+ Set {
+ -987316204.8333645,
+ -123414344.79032564,
+ -1991294021.561513,
+ 1312757734.419653,
+ -1984378057.5129266,
+ -1806577500.5932693,
+ -468159076.21629095,
+ -1373641555.943141,
+ -1507935663.7794366,
+ },
+ Set {
+ 1013760582.548079,
+ -1620441933.1598659,
+ 2070318677.3446665,
+ -1565533011.175784,
+ 813063012.5310383,
+ },
+ Set {
+ 668940130.0931497,
+ 1623133978.0225477,
+ 1890748741.9269,
+ 1983177254.438252,
+ -269736558.78650045,
+ 245579087.83319187,
+ -173710798.9892583,
+ -706694403.2852044,
+ },
+ Set {
+ 958710102.4790163,
+ 1066516724.4440823,
+ -1050002173.6265736,
+ 2071771911.388248,
+ },
+ Set {
+ -718783498.5660744,
+ -171808135.8375697,
+ 1434115809.2425957,
+ -1292184913.1745815,
+ 1761481668.0007043,
+ -544979035.656126,
+ -489540956.1762719,
+ -1470553104.2914085,
+ -1083004231.3472633,
+ 108001456.70452404,
+ -1197138287.2403808,
+ 643485404.8784003,
+ 1248739583.821772,
+ -63981123.49378252,
+ },
+ Set {
+ -556388852.7639756,
+ 1931838474.8187099,
+ 657548097.6579847,
+ -853270773.1088915,
+ -1736943443.7672043,
+ 288113004.3674216,
+ -1804458561.9595547,
+ -1855744631.5996609,
+ 814790767.1630516,
+ 148306379.282372,
+ -162437751.9466219,
+ },
+ Set {
+ 2082788080.8313985,
+ 873104903.2261963,
+ 757092431.4471917,
+ },
+ Set {
+ 1783843051.0749173,
+ 511601440.66549826,
+ -485650673.3291068,
+ 400714534.43508816,
+ 2015259342.1590161,
+ 225465824.22199202,
+ -1017295651.7632303,
+ 1403384847.8381042,
+ -2070941418.4332771,
+ -778923280.2561278,
+ -2076623848.786347,
+ },
+ Set {
+ 1772595984.6787534,
+ -1999885649.50419,
+ -1473510760.7297368,
+ 208516660.31804085,
+ -316874582.455606,
+ -49408233.40874815,
+ 157488335.38457298,
+ 2043432610.6591673,
+ -147398956.68561077,
+ 1790169149.8653378,
+ 1612897181.5513606,
+ 826532191.595715,
+ -1719456809.7754884,
+ 1338156961.456626,
+ },
+ Set {
+ 1836416613.8154063,
+ -1076044864.0926485,
+ -406153704.51364994,
+ 1243013409.1874938,
+ 1789845750.177021,
+ 901172883.5287242,
+ 619359793.6044474,
+ 1196332930.057732,
+ -1974849040.9160028,
+ 730639828.7849927,
+ },
+ ]
+ `)
+ })
+
+ it('max', () => {
+ const schema = z.set(z.number()).max(3)
+ forAll(arbitrary(schema), (s) => {
+ schema.parse(s)
+ })
+ const schemaRef = $ref(schema)
+ forAll(arbitrary(schemaRef), (s) => {
+ schema.parse(s)
+ })
+
+ const ctx = arbitraryContext({ rng: xoroshiro128plus(1638968569864n) })
+ const arb = arbitrary(schema)
+ expect(Array.from({ length: 10 }, () => arb.sample(ctx))).toMatchInlineSnapshot(`
+ [
+ Set {
+ -987316204.8333645,
+ -123414344.79032564,
+ },
+ Set {},
+ Set {
+ -1984378057.5129266,
+ -1806577500.5932693,
+ -468159076.21629095,
+ },
+ Set {},
+ Set {},
+ Set {
+ 1013760582.548079,
+ },
+ Set {},
+ Set {
+ -1565533011.175784,
+ 813063012.5310383,
+ -87348706.34546137,
+ },
+ Set {
+ 1623133978.0225477,
+ 1890748741.9269,
+ },
+ Set {
+ -269736558.78650045,
+ 245579087.83319187,
+ -173710798.9892583,
+ },
+ ]
+ `)
+ })
+
+ it('size', () => {
+ const schema = z.set(z.number()).size(2)
+ forAll(arbitrary(schema), (s) => {
+ schema.parse(s)
+ })
+ const schemaRef = $ref(schema)
+ forAll(arbitrary(schemaRef), (s) => {
+ schema.parse(s)
+ })
+
+ const ctx = arbitraryContext({ rng: xoroshiro128plus(1638968569864n) })
+ const arb = arbitrary(schema)
+ expect(Array.from({ length: 10 }, () => arb.sample(ctx))).toMatchInlineSnapshot(`
+ [
+ Set {
+ -987316204.8333645,
+ -123414344.79032564,
+ },
+ Set {
+ 1312757734.419653,
+ -1984378057.5129266,
+ },
+ Set {
+ -468159076.21629095,
+ -1373641555.943141,
+ },
+ Set {
+ -998293442.9723692,
+ 1013760582.548079,
+ },
+ Set {
+ 2070318677.3446665,
+ -1565533011.175784,
+ },
+ Set {
+ -87348706.34546137,
+ 668940130.0931497,
+ },
+ Set {
+ 1890748741.9269,
+ 1983177254.438252,
+ },
+ Set {
+ 245579087.83319187,
+ -173710798.9892583,
+ },
+ Set {
+ -1344762791.4300165,
+ 958710102.4790163,
+ },
+ Set {
+ -1050002173.6265736,
+ 2071771911.388248,
+ },
+ ]
+ `)
+ })
+
+ it('complex element', () => {
+ const schema = z.set(z.object({ id: z.number(), name: z.string() }))
+ forAll(arbitrary(schema), (s) => {
+ schema.parse(s)
+ })
+ const schemaRef = $ref(schema)
+ forAll(arbitrary(schemaRef), (s) => {
+ schema.parse(s)
+ })
+
+ const ctx = arbitraryContext({ rng: xoroshiro128plus(1638968569864n) })
+ const arb = arbitrary(schema)
+ expect(Array.from({ length: 10 }, () => arb.sample(ctx))).toMatchInlineSnapshot(`
+ [
+ Set {
+ {
+ "id": -987316204.8333645,
+ "name": "#l#'E",
+ },
+ {
+ "id": -1373641555.943141,
+ "name": "9",
+ },
+ {
+ "id": 1013760582.548079,
+ "name": "}",
+ },
+ {
+ "id": -1565533011.175784,
+ "name": "M^sy{IT",
+ },
+ {
+ "id": -173710798.9892583,
+ "name": "1dg",
+ },
+ {
+ "id": -1050002173.6265736,
+ "name": "x?Ko2vCD.7",
+ },
+ },
+ Set {
+ {
+ "id": -1197138287.2403808,
+ "name": "kNhCz^<",
+ },
+ {
+ "id": -1736943443.7672043,
+ "name": "'&aRK+",
+ },
+ {
+ "id": 2082788080.8313985,
+ "name": "\`cvZDX|",
+ },
+ {
+ "id": 225465824.22199202,
+ "name": "n!",
+ },
+ {
+ "id": -778923280.2561278,
+ "name": "",
+ },
+ },
+ Set {
+ {
+ "id": 1772595984.6787534,
+ "name": "",
+ },
+ {
+ "id": -1473510760.7297368,
+ "name": "HNR|Lw",
+ },
+ {
+ "id": 1612897181.5513606,
+ "name": ")m[x7Fj",
+ },
+ {
+ "id": 1789845750.177021,
+ "name": "]i#_JsG",
+ },
+ {
+ "id": 367591765.47046185,
+ "name": "CSN",
+ },
+ {
+ "id": -2076532314.9177723,
+ "name": "#_1@n",
+ },
+ {
+ "id": 1857212568.8128772,
+ "name": "u",
+ },
+ {
+ "id": 1747920687.5133138,
+ "name": "d&TsF%Z",
+ },
+ {
+ "id": -1555859368.257568,
+ "name": "[d6g",
+ },
+ {
+ "id": 1460794263.5207324,
+ "name": "a",
+ },
+ },
+ Set {},
+ Set {
+ {
+ "id": -1730467960.9301157,
+ "name": "U~vd&mBb|/",
+ },
+ {
+ "id": -329235582.1595564,
+ "name": "f-?N#'",
+ },
+ {
+ "id": -629042190.0540109,
+ "name": "1M4DEu:",
+ },
+ {
+ "id": -1573288205.8733597,
+ "name": ";Et{Mc",
+ },
+ {
+ "id": 564199740.9455872,
+ "name": "2D7T",
+ },
+ {
+ "id": 156495610.0297556,
+ "name": "/",
+ },
+ {
+ "id": -1637058935.3595414,
+ "name": "M)^"rM",
+ },
+ },
+ Set {
+ {
+ "id": -1647135979.9150243,
+ "name": "",
+ },
+ {
+ "id": 104233609.43998957,
+ "name": "",
+ },
+ },
+ Set {
+ {
+ "id": -1923966409.1305323,
+ "name": "b#&raz}",
+ },
+ {
+ "id": 49886494.71715784,
+ "name": "t",
+ },
+ {
+ "id": -2023340440.0599198,
+ "name": "/]*,+",
+ },
+ {
+ "id": 1790220552.1711726,
+ "name": "001$S9",
+ },
+ {
+ "id": -1342466782.9713006,
+ "name": "",
+ },
+ {
+ "id": 1921681185.660479,
+ "name": " "",
+ },
+ },
+ Set {
+ {
+ "id": 1136479527.6545281,
+ "name": "PQmG4{8,.R",
+ },
+ {
+ "id": -1566072390.7223964,
+ "name": "-~s|S",
+ },
+ {
+ "id": -817765546.9064288,
+ "name": "oNx?WP",
+ },
+ {
+ "id": 1999090627.9621153,
+ "name": "q",
+ },
+ {
+ "id": 1611170904.127273,
+ "name": "H,C",
+ },
+ {
+ "id": 191658644.70875168,
+ "name": "@v$qZ",
+ },
+ {
+ "id": -1375171649.2292619,
+ "name": "xsSqu$a7H\\",
+ },
+ {
+ "id": -1348401889.6536403,
+ "name": "W ",
+ },
+ {
+ "id": 771088789.4575377,
+ "name": ":",
+ },
+ {
+ "id": 957696727.8322158,
+ "name": "(/<+",
+ },
+ },
+ Set {
+ {
+ "id": -2027385576.1840696,
+ "name": "",
+ },
+ {
+ "id": -456612484.8197813,
+ "name": "AUx\\\`53",
+ },
+ {
+ "id": 1070710506.0100718,
+ "name": ">YD3d+w#VE",
+ },
+ {
+ "id": 980216050.091824,
+ "name": "O8x;",
+ },
+ {
+ "id": 2144213631.5816169,
+ "name": "yM=o%k",
+ },
+ {
+ "id": -1884015374.63066,
+ "name": "/.]0",
+ },
+ {
+ "id": 793367279.1719842,
+ "name": "<8uw,^/>-",
+ },
+ {
+ "id": 2101068270.8443651,
+ "name": "J7b$g&e9",
+ },
+ {
+ "id": -110583744.73535347,
+ "name": "kwz8 C",
+ },
+ },
+ Set {
+ {
+ "id": 769087190.2795157,
+ "name": "3T'",
+ },
+ {
+ "id": -672390145.7174225,
+ "name": "vMBj$",
+ },
+ {
+ "id": -1792699766.9753141,
+ "name": "0Rw0-CN[bN",
+ },
+ {
+ "id": -1153090133.0316849,
+ "name": ":L",
+ },
+ {
+ "id": -693167524.7748699,
+ "name": "P^Q0wpE.",
+ },
+ {
+ "id": 2024933488.1880827,
+ "name": "*Pz?e",
+ },
+ },
+ ]
+ `)
+ })
+
+ it('nonempty', () => {
+ const schema = z.set(z.string()).nonempty()
+ forAll(arbitrary(schema), (s) => {
+ schema.parse(s)
+ })
+ const schemaRef = $ref(schema)
+ forAll(arbitrary(schemaRef), (s) => {
+ schema.parse(s)
+ })
+
+ const ctx = arbitraryContext({ rng: xoroshiro128plus(1638968569864n) })
+ const arb = arbitrary(schema)
+ expect(Array.from({ length: 10 }, () => arb.sample(ctx))).toMatchInlineSnapshot(`
+ [
+ Set {
+ "L#",
+ "#'E1.9e+",
+ ",aM^sy{ITK",
+ "1dg",
+ "}x",
+ "Ko2",
+ "CD.7Q5]kNh",
+ },
+ Set {
+ "^<)U'&aRK+",
+ "b\`cvZDX|T8",
+ "!>!xv#.TH",
+ "R|Lws",
+ ")m[x7Fj",
+ },
+ Set {
+ "]i#_JsG",
+ "",
+ "D3d+w#",
+ "EeHO8x",
+ "~Vy",
+ "=o%k%",
+ "/.]0",
+ },
+ Set {
+ "<8uw,^/>-",
+ "lJ7b$g&e9M",
+ "kwz8 C",
+ "\`<3T'@",
+ "vMBj$",
+ "",
+ "0Rw0-CN[bN",
+ "5:",
+ "@iP^Q",
+ },
+ Set {
+ "pE.|K*Pz?e",
+ "})",
+ ") mmW{D&",
+ },
+ Set {
+ "Sz%XR",
+ "XTu.-",
+ "",
+ },
+ ]
+ `)
+ })
+})