-
Notifications
You must be signed in to change notification settings - Fork 0
/
custom.spec.ts
128 lines (118 loc) · 3.49 KB
/
custom.spec.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import * as t from 'tcomb'
import { generateAndCheck } from './index';
import * as jsc from 'jsverify';
import { range, zip } from 'lodash'
import { makeJsverifyArbitrary, addTypeToRegistry } from './index'
function contains(list, args, eqFn, lessFn, moreFn) {
const finded = list.filter(i => args.item.is(i))
const res = {
needCount: args.count === undefined ? 1 : args.count,
count: finded.length,
item: args.item
}
if (res.needCount === res.count) {
return eqFn(list, res)
} else if (res.needCount > res.count) {
return lessFn(list, res)
} else if (res.needCount < res.count) {
return moreFn(list, res)
}
}
const ArrayWithContains = (arrRt, item, count) => {
const args = {
item,
count
};
const predicate = list => {
const res = contains(
list, args,
() => true,
(_, res) => `Array contains less than ${res.needCount} items`,
(_, res) => `Array contains more than ${res.needCount} items`
);
return res;
}
(predicate as any).args = args;
return t.refinement(arrRt, predicate, contains.name);
}
function generatorContains(rt) {
const { meta: { predicate, type } } = rt
return makeJsverifyArbitrary(type).smap(coll => {
const res = contains(
coll, predicate.args,
list => list,
(list, res) => {
const howMuch = res.needCount - res.count
const idxs = list.length ?
jsc.sampler(jsc.elements(range(list.length)))(howMuch) as any as number[] :
range(howMuch)
const elements = jsc.sampler(makeJsverifyArbitrary(res.item))(howMuch) as any[]
zip(idxs, elements).forEach(([idx, element]) => {
list.splice(idx, 0, element)
});
return list
},
(list, res) => {
return list.reduce((acc, item) => {
const isItem = res.item.is(item)
if (!isItem || acc.count < res.needCount) {
acc.list.push(item)
}
if (isItem) {
acc.count++
}
return acc;
}, {list: [], count: 0}).list
}
)
return res;
}, x => x)
}
addTypeToRegistry(contains.name, generatorContains)
describe('FamilyObject', () => {
const StringOrVoid = t.maybe(t.String)
const Fio = t.interface({
firstname: StringOrVoid,
lastname: StringOrVoid,
middlename: StringOrVoid
})
const MemberWithRole = role => t.interface({
role,
fio: Fio,
dependant: t.maybe(t.Boolean)
})
const Spouse = MemberWithRole(t.enums.of('spouse'))
const NotSpouse = MemberWithRole(t.maybe(
t.enums.of([
'sibling',
'child',
'parent'
])
))
const Member = t.union([Spouse, NotSpouse])
const FamilyWithTypeAndMember = (type, countSpouse) => t.interface({
type,
members: ArrayWithContains(t.list(Member), Spouse, countSpouse)
})
const FamilyWithSpouse = FamilyWithTypeAndMember(
t.enums.of('espoused'),
1
)
const FamilyWithoutSpouse = FamilyWithTypeAndMember(
t.maybe(
t.enums.of([
'single',
'common_law_marriage'
])
),
0
)
const membersWithSpouse = ArrayWithContains(t.list(Member), Spouse, 1)
const FamilyObject = t.union([FamilyWithSpouse, FamilyWithoutSpouse])
test('Fio', generateAndCheck(Fio))
test('Member', generateAndCheck(Member))
test('MemberWithSpouse', generateAndCheck(membersWithSpouse))
test('FamilyWithSpouse', generateAndCheck(FamilyWithSpouse))
test('FamilyWithoutSpouse', generateAndCheck(FamilyWithoutSpouse))
test('FamilyObject', generateAndCheck(FamilyObject))
});