Skip to content
This repository was archived by the owner on Jan 2, 2024. It is now read-only.

Commit 15ae0cd

Browse files
committed
fix: intersection fuzzer emits only examples consistent with each child type
1 parent 6a9dab1 commit 15ae0cd

File tree

3 files changed

+67
-8
lines changed

3 files changed

+67
-8
lines changed

src/core/core.ts

+17-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as t from 'io-ts';
22
import { Fuzzer, ConcreteFuzzer, fuzzGenerator } from '../fuzzer';
3+
import { isLeft } from 'fp-ts/lib/Either';
34

45
export type BasicType =
56
| t.NumberType
@@ -266,14 +267,23 @@ export function fuzzIntersection(
266267
return {
267268
children: b.types,
268269
func: (n, ...h) => {
270+
let d = 0;
269271
let ret: unknown = undefined;
270-
h.forEach((v, i) => {
271-
if (ret === undefined) {
272-
ret = v.encode(n + i);
273-
} else {
274-
ret = { ...ret, ...v.encode(n + i) };
275-
}
276-
});
272+
do {
273+
h.forEach((v, i) => {
274+
let lp: unknown;
275+
lp = v.encode(n + i + d);
276+
if (typeof lp !== 'object') {
277+
throw new Error('fuzzIntersection cannot support non-object types');
278+
}
279+
if (ret === undefined) {
280+
ret = lp;
281+
} else {
282+
ret = { ...ret, ...lp };
283+
}
284+
});
285+
d++;
286+
} while (h.findIndex((_, i) => isLeft(b.types[i].decode(ret))) > -1);
277287
return ret;
278288
},
279289
};

test/helpers.ts

+37
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,38 @@ export const types = [
3030
}),
3131
t.tuple([t.number, t.string, t.boolean]),
3232
// Complex types
33+
t.intersection([
34+
t.type({ s: t.string, m: t.number, ___0000_extra_: t.boolean }),
35+
t.type({ s: t.string, j: t.boolean }),
36+
]),
37+
t.intersection([
38+
t.type({ s: t.string, m: t.number, ___0000_extra_: t.boolean }),
39+
t.type({ s2: t.string, j: t.boolean }),
40+
]),
41+
t.union([
42+
t.type({ s: t.string, m: t.number, ___0000_extra_: t.boolean }),
43+
t.type({ s: t.string, j: t.boolean }),
44+
]),
45+
t.union([
46+
t.type({ s: t.string, m: t.number, ___0000_extra_: t.boolean }),
47+
t.type({ s2: t.string, j: t.boolean }),
48+
]),
49+
t.intersection([
50+
t.partial({ s: t.string, m: t.number, ___0000_extra_: t.boolean }),
51+
t.partial({ s: t.string, j: t.boolean }),
52+
]),
53+
t.intersection([
54+
t.partial({ s: t.string, m: t.number, ___0000_extra_: t.boolean }),
55+
t.partial({ s2: t.string, j: t.boolean }),
56+
]),
57+
t.union([
58+
t.partial({ s: t.string, m: t.number, ___0000_extra_: t.boolean }),
59+
t.partial({ s: t.string, j: t.boolean }),
60+
]),
61+
t.union([
62+
t.partial({ s: t.string, m: t.number, ___0000_extra_: t.boolean }),
63+
t.partial({ s2: t.string, j: t.boolean }),
64+
]),
3365
t.type({ s: t.string, m: t.type({ n: t.Int }) }),
3466
t.type({
3567
s: t.union([t.string, t.number, t.partial({ n: t.number, z: t.string })]),
@@ -58,3 +90,8 @@ export const unknownTypes = [
5890
t.union([t.string, weirdString]),
5991
customStringDecoder,
6092
];
93+
94+
export const runtimeFailTypes = [
95+
t.intersection([t.string, t.type({ m: t.number })]),
96+
t.intersection([t.type({ m: t.number }), t.string]),
97+
];

test/test-fuzzer.ts

+13-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as assert from 'assert';
22
import * as lib from '../src/fuzzer';
33

4-
import { types, unknownTypes } from './helpers';
4+
import { types, unknownTypes, runtimeFailTypes } from './helpers';
55
import { createCoreRegistry } from '../src/registry';
66
import { isRight } from 'fp-ts/lib/Either';
77

@@ -13,6 +13,12 @@ describe('fuzzer', () => {
1313
assert.ok(lib.exampleGenerator(r, b));
1414
});
1515
}
16+
for (const b of runtimeFailTypes) {
17+
it(`can build a fuzzer for \`${b.name}\` type`, () => {
18+
const r = createCoreRegistry();
19+
assert.ok(lib.exampleGenerator(r, b));
20+
});
21+
}
1622
for (const b of unknownTypes) {
1723
it(`throws on unknown \`${b.name}\` type`, () => {
1824
const r = createCoreRegistry();
@@ -28,6 +34,12 @@ describe('fuzzer', () => {
2834
assert.ok(isRight(b.decode(lib.exampleOf(b, r, 0))));
2935
});
3036
}
37+
for (const b of runtimeFailTypes) {
38+
it(`throws on fuzzing \`${b.name}\` type`, () => {
39+
const r = createCoreRegistry();
40+
assert.throws(() => lib.exampleOf(b, r, 0));
41+
});
42+
}
3143
for (const b of unknownTypes) {
3244
it(`throws on unknown \`${b.name}\` type`, () => {
3345
const r = createCoreRegistry();

0 commit comments

Comments
 (0)