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 });