/ test / packages / io-ts / test.js
test.js
  1  import * as fc from 'fast-check';
  2  import * as t from 'io-ts';
  3  import { ThrowReporter } from 'io-ts/lib/ThrowReporter';
  4  
  5  const expandToIoAndInstance = rawValues => {
  6    const io = rawValues.reduce((p, e) => {
  7      p[e[0]] = e[1][0];
  8      return p;
  9    }, {});
 10    const instance = rawValues.reduce((p, e) => {
 11      p[e[0]] = e[1][1];
 12      return p;
 13    }, {});
 14    return { io, instance };
 15  };
 16  
 17  const ValidTypeDefValueArb = fc
 18    .set(
 19      fc.tuple(
 20        fc.fullUnicodeString(),
 21        fc.oneof(
 22          fc.tuple(fc.constant(t.null), fc.constant(null)),
 23          fc.tuple(fc.constant(t.undefined), fc.constant(undefined)),
 24          fc.tuple(fc.constant(t.string), fc.fullUnicodeString()),
 25          fc.tuple(fc.constant(t.number), fc.double()),
 26          fc.tuple(fc.constant(t.boolean), fc.boolean()),
 27          fc.tuple(fc.constant(t.any), fc.anything()),
 28          fc.tuple(fc.constant(t.object), fc.object()),
 29          fc.tuple(fc.constant(t.Integer), fc.integer(Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER)),
 30          fc.tuple(fc.constant(t.Array), fc.array(fc.anything())),
 31          fc.tuple(fc.constant(t.array(t.number)), fc.array(fc.integer())),
 32          fc.tuple(fc.constant(t.Dictionary), fc.dictionary(fc.fullUnicodeString(), fc.anything())),
 33          fc.tuple(fc.constant(t.Function), fc.func(fc.integer()))
 34        )
 35      ),
 36      (a, b) => a[0] === b[0]
 37    )
 38    .map(expandToIoAndInstance);
 39  
 40  const generationChoices = {
 41    null: fc.constant(null),
 42    undefined: fc.constant(undefined),
 43    string: fc.fullUnicodeString(),
 44    number: fc.double(),
 45    boolean: fc.boolean(),
 46    object: fc.object(),
 47    integer: fc.integer(Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER),
 48    array: fc.array(fc.integer()),
 49    dict: fc.dictionary(fc.fullUnicodeString(), fc.anything()),
 50    func: fc.func(fc.integer())
 51  };
 52  const allBut = (...forbidden) => {
 53    return fc.oneof(
 54      ...Object.keys(generationChoices)
 55        .filter(k => forbidden.indexOf(k) === -1)
 56        .map(k => generationChoices[k])
 57    );
 58  };
 59  const InvalidTypeDefValueArb = fc
 60    .set(
 61      fc.tuple(
 62        fc.fullUnicodeString(),
 63        fc.oneof(
 64          fc.tuple(fc.constant(t.null), allBut('null')),
 65          fc.tuple(fc.constant(t.undefined), allBut('undefined')),
 66          fc.tuple(fc.constant(t.string), allBut('string')),
 67          fc.tuple(fc.constant(t.number), allBut('number', 'integer')),
 68          fc.tuple(fc.constant(t.boolean), allBut('boolean')),
 69          fc.tuple(fc.constant(t.object), allBut('object', 'dict', 'array')), //array??
 70          fc.tuple(fc.constant(t.Integer), allBut('integer')),
 71          fc.tuple(fc.constant(t.Array), allBut('array')),
 72          fc.tuple(fc.constant(t.array(t.number)), allBut('array')),
 73          fc.tuple(fc.constant(t.Dictionary), allBut('object', 'dict', 'array')), //array??
 74          fc.tuple(fc.constant(t.Function), allBut('func'))
 75        )
 76      ),
 77      1,
 78      10,
 79      (a, b) => a[0] === b[0]
 80    )
 81    .map(expandToIoAndInstance);
 82  
 83  describe('io-ts', () => {
 84    describe('t.type', () => {
 85      it('should fail to decode instance with missing keys', () => {
 86        fc.assert(
 87          fc.property(ValidTypeDefValueArb, ValidTypeDefValueArb, (v1, v2) => {
 88            const foundExtra = Object.keys(v2.instance).find(
 89              k => !v1.instance.hasOwnProperty(k) && v2.io[k] !== t.any && v2.io[k] !== t.undefined
 90            );
 91            fc.pre(foundExtra);
 92            const io = Object.assign(Object.assign({}, v2.io), v1.io);
 93            const Schema = t.type(io);
 94            try {
 95              ThrowReporter.report(Schema.decode(v1.instance));
 96              return false;
 97            } catch (err) {
 98              return true;
 99            }
100          })
101        );
102      });
103      it('should fail to decode instance with invalid values', () => {
104        fc.assert(
105          fc.property(ValidTypeDefValueArb, InvalidTypeDefValueArb, (v1, v2) => {
106            const io = Object.assign(Object.assign({}, v1.io), v2.io);
107            const instance = Object.assign(Object.assign({}, v1.instance), v2.instance);
108            const Schema = t.type(io);
109            try {
110              ThrowReporter.report(Schema.decode(instance));
111              return false;
112            } catch (err) {
113              return true;
114            }
115          }),
116          { verbose: true }
117        );
118      });
119      it('should successfully decode exact instance', () => {
120        fc.assert(
121          fc.property(ValidTypeDefValueArb, ({ io, instance }) => {
122            const Schema = t.type(io);
123            ThrowReporter.report(Schema.decode(instance));
124          })
125        );
126      });
127      it('should successfully decode valid instance', () => {
128        fc.assert(
129          fc.property(ValidTypeDefValueArb, fc.object(), ({ io, instance }, instance2) => {
130            const Schema = t.type(io);
131            const obj = Object.assign(Object.assign({}, instance2), instance);
132            ThrowReporter.report(Schema.decode(obj));
133          })
134        );
135      });
136    });
137    describe('t.exact', () => {
138      it('should fail to decode instance with missing keys', () => {
139        fc.assert(
140          fc.property(ValidTypeDefValueArb, ValidTypeDefValueArb, (v1, v2) => {
141            const foundExtra = Object.keys(v2.instance).find(
142              k => !v1.instance.hasOwnProperty(k) && v2.io[k] !== t.any && v2.io[k] !== t.undefined
143            );
144            fc.pre(foundExtra);
145            const io = Object.assign(Object.assign({}, v2.io), v1.io);
146            const Schema = t.exact(t.type(io));
147            try {
148              ThrowReporter.report(Schema.decode(v1.instance));
149              return false;
150            } catch (err) {
151              return true;
152            }
153          })
154        );
155      });
156      it('should fail to decode instance with invalid values', () => {
157        fc.assert(
158          fc.property(ValidTypeDefValueArb, InvalidTypeDefValueArb, (v1, v2) => {
159            const io = Object.assign(Object.assign({}, v1.io), v2.io);
160            const instance = Object.assign(Object.assign({}, v1.instance), v2.instance);
161            const Schema = t.exact(t.type(io));
162            try {
163              ThrowReporter.report(Schema.decode(instance));
164              return false;
165            } catch (err) {
166              return true;
167            }
168          }),
169          { verbose: true }
170        );
171      });
172      it('should fail to decode instance with more keys', () => {
173        fc.assert(
174          fc.property(ValidTypeDefValueArb, fc.object(), ({ io, instance }, instance2) => {
175            const Schema = t.exact(t.type(io));
176            const obj = Object.assign(Object.assign({}, instance2), instance);
177            const foundExtra = Object.keys(instance2).find(k => !instance.hasOwnProperty(k));
178            fc.pre(foundExtra);
179            try {
180              ThrowReporter.report(Schema.decode(obj));
181              return false;
182            } catch (err) {
183              return true;
184            }
185          })
186        );
187      });
188      it('should successfully decode exact instance', () => {
189        fc.assert(
190          fc.property(ValidTypeDefValueArb, ({ io, instance }) => {
191            const Schema = t.exact(t.type(io));
192            ThrowReporter.report(Schema.decode(instance));
193          })
194        );
195      });
196    });
197    describe('t.union', () => {
198      it('should successfully decode exact instance', () => {
199        fc.assert(
200          fc.property(fc.array(ValidTypeDefValueArb, 1, 10), vs => {
201            const Schema = t.union(vs.map(v => t.type(v.io)));
202            ThrowReporter.report(Schema.decode(vs[0].instance));
203          })
204        );
205      });
206    });
207  });