diff --git a/src/experimental.ts b/src/experimental.ts new file mode 100644 index 0000000..3202b91 --- /dev/null +++ b/src/experimental.ts @@ -0,0 +1,20 @@ +import { Fuzzer } from './fuzzer'; +import * as t from 'io-ts'; +import { NonEmptyArray, cons } from 'fp-ts/lib/NonEmptyArray'; + +/** + * @experimental 4.1.0 This will be superseded by a generic handler for NonEmptyArrays in io-ts-types when https://github.com/gcanti/io-ts-types/issues/102 is fixed. + */ +export const nonEmptyArrayFuzzer = ( + c: t.Type +): Fuzzer, unknown> => ({ + id: `NonEmptyArray<${c.name}>`, + idType: 'name', + impl: { + type: 'fuzzer', + children: [c, t.array(c)], + func: (ctx, n0, hc, ha) => { + return cons(hc.encode([n0, ctx]) as T, ha.encode([n0, ctx]) as T[]); + }, + }, +}); diff --git a/src/index.ts b/src/index.ts index 9ef6155..66cafe2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -26,4 +26,5 @@ export * from './fuzzer'; export * from './extras'; import * as core from './core'; -export { core }; +import * as experimental from './experimental'; +export { core, experimental }; diff --git a/test/test-experimental.ts b/test/test-experimental.ts new file mode 100644 index 0000000..dc45799 --- /dev/null +++ b/test/test-experimental.ts @@ -0,0 +1,78 @@ +import * as assert from 'assert'; +import * as lib from '../src/experimental'; + +import { + exampleGenerator, + fuzzContext, + FuzzContext, + createCoreRegistry, + Fuzzer, +} from '../src/'; +import * as t from 'io-ts'; +import { nonEmptyArray } from 'io-ts-types/lib/nonEmptyArray'; +import { Encode } from 'io-ts'; +import { rngi } from '../src/rng'; +import { isRight, Right } from 'fp-ts/lib/Either'; + +const count = 100; + +// tslint:disable-next-line:no-any +const types: Array> = [ + nonEmptyArray(t.string), + nonEmptyArray(t.union([t.string, t.number])), + t.partial({ a: nonEmptyArray(t.string) }), + t.type({ a: nonEmptyArray(t.string) }), + t.partial({ a: nonEmptyArray(t.union([t.string, t.number])) }), + t.type({ a: nonEmptyArray(t.union([t.string, t.number])) }), + t.type({ a: nonEmptyArray(t.union([t.string, t.number])) }), + nonEmptyArray(t.type({ a: nonEmptyArray(t.union([t.string, t.number])) })), +]; + +describe('experimental', () => { + describe('nonEmptyArrayFuzzer', () => { + for (const b of types) { + describe(`\`${b.name}\` codec`, () => { + let p: Encode<[number, FuzzContext], unknown>; + const old: unknown[] = []; + it(`loads extra fuzzers and builds an example generator`, async () => { + const r = createCoreRegistry()!; + const fz: Fuzzer[] = [ + lib.nonEmptyArrayFuzzer(t.string), + lib.nonEmptyArrayFuzzer(t.union([t.string, t.number])), + lib.nonEmptyArrayFuzzer( + t.type({ a: nonEmptyArray(t.union([t.string, t.number])) }) + ), + ] as Fuzzer[]; + r.register(...fz); + p = exampleGenerator(r, b).encode; + }); + it(`generates decodable examples for seeds '[0, ${count})`, () => { + for (const n of new Array(count).keys()) { + const v = p([rngi(n) / Math.PI, fuzzContext()]); + const d = b.decode(v); + assert.ok( + isRight(d), + `must decode ${JSON.stringify(v)}, instead ${JSON.stringify(d)}` + ); + old.push(v); + const y = b.encode((d as Right).right); + // can't use deepStrictEqual because io-ts uses {...X} to encode + // certain objects, thus they don't have null prototypes. + // tslint:disable-next-line:deprecation + assert.deepEqual(y, v, `must encode back to ${JSON.stringify(v)}`); + } + }) + .timeout(5000) + .slow(500); + it(`generates same examples 2nd time`, () => { + for (const n of new Array(count).keys()) { + const v = p([rngi(n) / Math.PI, fuzzContext()]); + assert.deepStrictEqual(v, old[n]); + } + }) + .timeout(5000) + .slow(500); + }); + } + }); +}).timeout(1000000);