monastery 2.2.3 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +10 -1
- package/changelog.md +1 -1
- package/docs/_config.yml +2 -2
- package/docs/assets/imgs/monastery.jpg +0 -0
- package/docs/definition/index.md +1 -2
- package/docs/manager/index.md +19 -11
- package/docs/manager/model.md +1 -1
- package/docs/manager/models.md +2 -3
- package/docs/model/...rawMethods.md +289 -0
- package/docs/model/count.md +25 -0
- package/docs/model/find.md +5 -9
- package/docs/model/findOne.md +1 -1
- package/docs/model/findOneAndUpdate.md +1 -1
- package/docs/model/index.md +5 -30
- package/docs/model/insert.md +4 -6
- package/docs/model/remove.md +4 -6
- package/docs/model/update.md +4 -6
- package/docs/readme.md +80 -47
- package/lib/collection.js +324 -0
- package/lib/index.js +207 -67
- package/lib/model-crud.js +605 -619
- package/lib/model-validate.js +227 -245
- package/lib/model.js +70 -91
- package/lib/rules.js +36 -35
- package/lib/util.js +69 -15
- package/package.json +12 -12
- package/plugins/images/index.js +15 -26
- package/test/blacklisting.js +506 -537
- package/test/collection.js +445 -0
- package/test/crud.js +810 -730
- package/test/index.test.js +26 -0
- package/test/manager.js +77 -0
- package/test/mock/blacklisting.js +23 -23
- package/test/model.js +611 -572
- package/test/plugin-images.js +880 -965
- package/test/populate.js +249 -262
- package/test/util.js +126 -45
- package/test/validate.js +1074 -1121
- package/test/virtuals.js +222 -227
- package/lib/monk-monkey-patches.js +0 -90
- package/test/monk.js +0 -53
- package/test/test.js +0 -38
package/test/validate.js
CHANGED
|
@@ -1,1190 +1,1143 @@
|
|
|
1
1
|
// Todo: split out basic 'type' tests
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
const monastery = require('../lib/index.js')
|
|
3
|
+
const Model = require('../lib/model.js')
|
|
4
|
+
|
|
5
|
+
let db
|
|
6
|
+
beforeAll(async () => { db = monastery('127.0.0.1/monastery', { timestamps: false }) })
|
|
7
|
+
afterAll(async () => { db.close() })
|
|
8
|
+
|
|
9
|
+
test('validation basic errors', async () => {
|
|
10
|
+
let user = db.model('user', { fields: {
|
|
11
|
+
date: { type: 'date' },
|
|
12
|
+
name: { type: 'string', required: true },
|
|
13
|
+
colors: [{ type: 'string' }],
|
|
14
|
+
animals: { dog: { type: 'string' }},
|
|
15
|
+
}})
|
|
16
|
+
|
|
17
|
+
// Required error (insert)
|
|
18
|
+
await expect(user.validate({})).rejects.toContainEqual({
|
|
19
|
+
status: '400',
|
|
20
|
+
title: 'name',
|
|
21
|
+
detail: 'This field is required.',
|
|
22
|
+
meta: { rule: 'required', model: 'user', field: 'name' },
|
|
23
|
+
})
|
|
24
|
+
await expect(user.validate({ name : '' })).rejects.toContainEqual({
|
|
25
|
+
status: '400',
|
|
26
|
+
title: 'name',
|
|
27
|
+
detail: 'This field is required.',
|
|
28
|
+
meta: { rule: 'required', model: 'user', field: 'name' },
|
|
29
|
+
})
|
|
5
30
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
31
|
+
// No required error (update)
|
|
32
|
+
await expect(user.validate({}, { update: true })).resolves.toEqual({})
|
|
33
|
+
|
|
34
|
+
// Type error (string)
|
|
35
|
+
await expect(user.validate({ name: 1 })).resolves.toEqual({ name: '1' })
|
|
36
|
+
await expect(user.validate({ name: 1.123 })).resolves.toEqual({ name: '1.123' })
|
|
37
|
+
await expect(user.validate({ name: undefined })).rejects.toContainEqual({
|
|
38
|
+
status: '400',
|
|
39
|
+
title: 'name',
|
|
40
|
+
detail: 'This field is required.',
|
|
41
|
+
meta: { rule: 'required', model: 'user', field: 'name' },
|
|
42
|
+
})
|
|
43
|
+
await expect(user.validate({ name: null })).rejects.toContainEqual({
|
|
44
|
+
status: '400',
|
|
45
|
+
title: 'name',
|
|
46
|
+
detail: 'This field is required.',
|
|
47
|
+
meta: { rule: 'required', model: 'user', field: 'name' },
|
|
48
|
+
})
|
|
49
|
+
await expect(user.validate({ name: true })).rejects.toContainEqual({
|
|
50
|
+
status: '400',
|
|
51
|
+
title: 'name',
|
|
52
|
+
detail: 'Value was not a string.',
|
|
53
|
+
meta: { rule: 'isString', model: 'user', field: 'name' },
|
|
54
|
+
})
|
|
29
55
|
|
|
30
|
-
// No required error (update)
|
|
31
|
-
await expect(user.validate({}, { update: true })).resolves.toEqual({})
|
|
32
|
-
|
|
33
|
-
// Type error (string)
|
|
34
|
-
await expect(user.validate({ name: 1 })).resolves.toEqual({ name: '1' })
|
|
35
|
-
await expect(user.validate({ name: 1.123 })).resolves.toEqual({ name: '1.123' })
|
|
36
|
-
await expect(user.validate({ name: undefined })).rejects.toContainEqual({
|
|
37
|
-
status: '400',
|
|
38
|
-
title: 'name',
|
|
39
|
-
detail: 'This field is required.',
|
|
40
|
-
meta: { rule: 'required', model: 'user', field: 'name' }
|
|
41
|
-
})
|
|
42
|
-
await expect(user.validate({ name: null })).rejects.toContainEqual({
|
|
43
|
-
status: '400',
|
|
44
|
-
title: 'name',
|
|
45
|
-
detail: 'This field is required.',
|
|
46
|
-
meta: { rule: 'required', model: 'user', field: 'name' }
|
|
47
|
-
})
|
|
48
|
-
await expect(user.validate({ name: true })).rejects.toContainEqual({
|
|
49
|
-
status: '400',
|
|
50
|
-
title: 'name',
|
|
51
|
-
detail: 'Value was not a string.',
|
|
52
|
-
meta: { rule: 'isString', model: 'user', field: 'name' }
|
|
53
|
-
})
|
|
54
56
|
|
|
57
|
+
// Type error (date)
|
|
58
|
+
let userdate = db.model('userdate', { fields: { amount: { type: 'date', required: true }}})
|
|
59
|
+
let userdate2 = db.model('userdate2', { fields: { amount: { type: 'date' }}})
|
|
60
|
+
await expect(userdate.validate({ amount: 0 })).resolves.toEqual({ amount: 0 })
|
|
61
|
+
await expect(userdate.validate({ amount: '0' })).resolves.toEqual({ amount: 0 })
|
|
62
|
+
await expect(userdate.validate({ amount: '1646778655000' })).resolves.toEqual({ amount: 1646778655000 })
|
|
63
|
+
await expect(userdate2.validate({ amount: '' })).resolves.toEqual({ amount: null })
|
|
64
|
+
await expect(userdate2.validate({ amount: null })).resolves.toEqual({ amount: null })
|
|
65
|
+
await expect(userdate.validate({ amount: 'badnum' })).rejects.toEqual([{
|
|
66
|
+
status: '400',
|
|
67
|
+
title: 'amount',
|
|
68
|
+
detail: 'Value was not a unix timestamp.',
|
|
69
|
+
meta: { rule: 'isDate', model: 'userdate', field: 'amount' },
|
|
70
|
+
}])
|
|
71
|
+
await expect(userdate.validate({ amount: false })).rejects.toEqual([{
|
|
72
|
+
status: '400',
|
|
73
|
+
title: 'amount',
|
|
74
|
+
detail: 'Value was not a unix timestamp.',
|
|
75
|
+
meta: { rule: 'isDate', model: 'userdate', field: 'amount' },
|
|
76
|
+
}])
|
|
77
|
+
await expect(userdate.validate({ amount: undefined })).rejects.toEqual([{
|
|
78
|
+
status: '400',
|
|
79
|
+
title: 'amount',
|
|
80
|
+
detail: 'This field is required.',
|
|
81
|
+
meta: { rule: 'required', model: 'userdate', field: 'amount' },
|
|
82
|
+
}])
|
|
83
|
+
await expect(userdate.validate({ amount: null })).rejects.toEqual([{
|
|
84
|
+
status: '400',
|
|
85
|
+
title: 'amount',
|
|
86
|
+
detail: 'This field is required.',
|
|
87
|
+
meta: { rule: 'required', model: 'userdate', field: 'amount' },
|
|
88
|
+
}])
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
// Type error (number)
|
|
92
|
+
let usernum = db.model('usernum', { fields: { amount: { type: 'number', required: true }}})
|
|
93
|
+
let usernum2 = db.model('usernum2', { fields: { amount: { type: 'number' }}})
|
|
94
|
+
await expect(usernum.validate({ amount: 0 })).resolves.toEqual({ amount: 0 })
|
|
95
|
+
await expect(usernum.validate({ amount: '0' })).resolves.toEqual({ amount: 0 })
|
|
96
|
+
await expect(usernum.validate({ amount: '1646778655000' })).resolves.toEqual({ amount: 1646778655000 })
|
|
97
|
+
await expect(usernum2.validate({ amount: '' })).resolves.toEqual({ amount: null })
|
|
98
|
+
await expect(usernum2.validate({ amount: null })).resolves.toEqual({ amount: null })
|
|
99
|
+
await expect(usernum.validate({ amount: 'badnum' })).rejects.toEqual([{
|
|
100
|
+
status: '400',
|
|
101
|
+
title: 'amount',
|
|
102
|
+
detail: 'Value was not a number.',
|
|
103
|
+
meta: { rule: 'isNumber', model: 'usernum', field: 'amount' },
|
|
104
|
+
}])
|
|
105
|
+
await expect(usernum.validate({ amount: false })).rejects.toEqual([{
|
|
106
|
+
status: '400',
|
|
107
|
+
title: 'amount',
|
|
108
|
+
detail: 'Value was not a number.',
|
|
109
|
+
meta: { rule: 'isNumber', model: 'usernum', field: 'amount' },
|
|
110
|
+
}])
|
|
111
|
+
await expect(usernum.validate({ amount: undefined })).rejects.toEqual([{
|
|
112
|
+
status: '400',
|
|
113
|
+
title: 'amount',
|
|
114
|
+
detail: 'This field is required.',
|
|
115
|
+
meta: { rule: 'required', model: 'usernum', field: 'amount' },
|
|
116
|
+
}])
|
|
117
|
+
await expect(usernum.validate({ amount: null })).rejects.toEqual([{
|
|
118
|
+
status: '400',
|
|
119
|
+
title: 'amount',
|
|
120
|
+
detail: 'This field is required.',
|
|
121
|
+
meta: { rule: 'required', model: 'usernum', field: 'amount' },
|
|
122
|
+
}])
|
|
123
|
+
|
|
124
|
+
// Type error (array)
|
|
125
|
+
await expect(user.validate({ name: 'a', colors: 1 })).rejects.toContainEqual({
|
|
126
|
+
status: '400',
|
|
127
|
+
title: 'colors',
|
|
128
|
+
detail: 'Value was not an array.',
|
|
129
|
+
meta: { rule: 'isArray', model: 'user', field: 'colors' },
|
|
130
|
+
})
|
|
55
131
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
await expect(userdate2.validate({ amount: null })).resolves.toEqual({ amount: null })
|
|
64
|
-
await expect(userdate.validate({ amount: 'badnum' })).rejects.toEqual([{
|
|
65
|
-
status: '400',
|
|
66
|
-
title: 'amount',
|
|
67
|
-
detail: 'Value was not a unix timestamp.',
|
|
68
|
-
meta: { rule: 'isDate', model: 'userdate', field: 'amount' }
|
|
69
|
-
}])
|
|
70
|
-
await expect(userdate.validate({ amount: false })).rejects.toEqual([{
|
|
71
|
-
status: '400',
|
|
72
|
-
title: 'amount',
|
|
73
|
-
detail: 'Value was not a unix timestamp.',
|
|
74
|
-
meta: { rule: 'isDate', model: 'userdate', field: 'amount' }
|
|
75
|
-
}])
|
|
76
|
-
await expect(userdate.validate({ amount: undefined })).rejects.toEqual([{
|
|
77
|
-
status: '400',
|
|
78
|
-
title: 'amount',
|
|
79
|
-
detail: 'This field is required.',
|
|
80
|
-
meta: { rule: 'required', model: 'userdate', field: 'amount' },
|
|
81
|
-
}])
|
|
82
|
-
await expect(userdate.validate({ amount: null })).rejects.toEqual([{
|
|
83
|
-
status: '400',
|
|
84
|
-
title: 'amount',
|
|
85
|
-
detail: 'This field is required.',
|
|
86
|
-
meta: { rule: 'required', model: 'userdate', field: 'amount' },
|
|
87
|
-
}])
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
// Type error (number)
|
|
91
|
-
let usernum = db.model('usernum', { fields: { amount: { type: 'number', required: true }}})
|
|
92
|
-
let usernum2 = db.model('usernum2', { fields: { amount: { type: 'number' }}})
|
|
93
|
-
await expect(usernum.validate({ amount: 0 })).resolves.toEqual({ amount: 0 })
|
|
94
|
-
await expect(usernum.validate({ amount: '0' })).resolves.toEqual({ amount: 0 })
|
|
95
|
-
await expect(usernum.validate({ amount: '1646778655000' })).resolves.toEqual({ amount: 1646778655000 })
|
|
96
|
-
await expect(usernum2.validate({ amount: '' })).resolves.toEqual({ amount: null })
|
|
97
|
-
await expect(usernum2.validate({ amount: null })).resolves.toEqual({ amount: null })
|
|
98
|
-
await expect(usernum.validate({ amount: 'badnum' })).rejects.toEqual([{
|
|
99
|
-
status: '400',
|
|
100
|
-
title: 'amount',
|
|
101
|
-
detail: 'Value was not a number.',
|
|
102
|
-
meta: { rule: 'isNumber', model: 'usernum', field: 'amount' }
|
|
103
|
-
}])
|
|
104
|
-
await expect(usernum.validate({ amount: false })).rejects.toEqual([{
|
|
105
|
-
status: '400',
|
|
106
|
-
title: 'amount',
|
|
107
|
-
detail: 'Value was not a number.',
|
|
108
|
-
meta: { rule: 'isNumber', model: 'usernum', field: 'amount' }
|
|
109
|
-
}])
|
|
110
|
-
await expect(usernum.validate({ amount: undefined })).rejects.toEqual([{
|
|
111
|
-
status: '400',
|
|
112
|
-
title: 'amount',
|
|
113
|
-
detail: 'This field is required.',
|
|
114
|
-
meta: { rule: 'required', model: 'usernum', field: 'amount' },
|
|
115
|
-
}])
|
|
116
|
-
await expect(usernum.validate({ amount: null })).rejects.toEqual([{
|
|
117
|
-
status: '400',
|
|
118
|
-
title: 'amount',
|
|
119
|
-
detail: 'This field is required.',
|
|
120
|
-
meta: { rule: 'required', model: 'usernum', field: 'amount' },
|
|
121
|
-
}])
|
|
122
|
-
|
|
123
|
-
// Type error (array)
|
|
124
|
-
await expect(user.validate({ name: 'a', colors: 1 })).rejects.toContainEqual({
|
|
125
|
-
status: '400',
|
|
126
|
-
title: 'colors',
|
|
127
|
-
detail: 'Value was not an array.',
|
|
128
|
-
meta: { rule: 'isArray', model: 'user', field: 'colors' }
|
|
129
|
-
})
|
|
132
|
+
// Type error (array)
|
|
133
|
+
await expect(user.validate({ name: 'a', colors: null })).rejects.toContainEqual({
|
|
134
|
+
status: '400',
|
|
135
|
+
title: 'colors',
|
|
136
|
+
detail: 'Value was not an array.',
|
|
137
|
+
meta: { rule: 'isArray', model: 'user', field: 'colors' },
|
|
138
|
+
})
|
|
130
139
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
140
|
+
// Type error (object)
|
|
141
|
+
await expect(user.validate({ name: 'a', animals: [] })).rejects.toContainEqual({
|
|
142
|
+
status: '400',
|
|
143
|
+
title: 'animals',
|
|
144
|
+
detail: 'Value was not an object.',
|
|
145
|
+
meta: { rule: 'isObject', model: 'user', field: 'animals' },
|
|
146
|
+
})
|
|
138
147
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
148
|
+
// Type error (object)
|
|
149
|
+
await expect(user.validate({ name: 'a', animals: null })).rejects.toContainEqual({
|
|
150
|
+
status: '400',
|
|
151
|
+
title: 'animals',
|
|
152
|
+
detail: 'Value was not an object.',
|
|
153
|
+
meta: { rule: 'isObject', model: 'user', field: 'animals' },
|
|
154
|
+
})
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
test('validation subdocument errors', async () => {
|
|
158
|
+
let user = db.model('user', { fields: {
|
|
159
|
+
animals: {
|
|
160
|
+
cat: { type: 'string', required: true }, // {} = required on insert
|
|
161
|
+
dog: {
|
|
162
|
+
name: { type: 'string' },
|
|
163
|
+
color: { type: 'string', required: true }, // {} = required on insert
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
}})
|
|
167
|
+
|
|
168
|
+
// Insert: Required subdocument properties
|
|
169
|
+
await expect(user.validate({})).rejects.toEqual(
|
|
170
|
+
expect.arrayContaining([
|
|
171
|
+
expect.objectContaining({
|
|
172
|
+
status: '400',
|
|
173
|
+
title: 'animals.cat',
|
|
174
|
+
detail: 'This field is required.',
|
|
175
|
+
meta: { rule: 'required', model: 'user', field: 'cat' },
|
|
176
|
+
}),
|
|
177
|
+
expect.objectContaining({
|
|
178
|
+
status: '400',
|
|
179
|
+
title: 'animals.dog.color',
|
|
180
|
+
detail: 'This field is required.',
|
|
181
|
+
meta: { rule: 'required', model: 'user', field: 'color' },
|
|
182
|
+
}),
|
|
183
|
+
])
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
// Insert: Required subdocument properties
|
|
187
|
+
await expect(user.validate({ animals: {} })).rejects.toEqual(
|
|
188
|
+
expect.arrayContaining([
|
|
189
|
+
expect.objectContaining({
|
|
190
|
+
status: '400',
|
|
191
|
+
title: 'animals.cat',
|
|
192
|
+
detail: 'This field is required.',
|
|
193
|
+
meta: { rule: 'required', model: 'user', field: 'cat' },
|
|
194
|
+
}),
|
|
195
|
+
expect.objectContaining({
|
|
196
|
+
status: '400',
|
|
197
|
+
title: 'animals.dog.color',
|
|
198
|
+
detail: 'This field is required.',
|
|
199
|
+
meta: { rule: 'required', model: 'user', field: 'color' },
|
|
200
|
+
}),
|
|
201
|
+
])
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
// Insert: Invalid subdocument type
|
|
205
|
+
await expect(user.validate({ animals: { dog: 1 }})).rejects.toEqual(
|
|
206
|
+
expect.arrayContaining([
|
|
207
|
+
expect.objectContaining({
|
|
208
|
+
status: '400',
|
|
209
|
+
title: 'animals.cat',
|
|
210
|
+
detail: 'This field is required.',
|
|
211
|
+
meta: { rule: 'required', model: 'user', field: 'cat' },
|
|
212
|
+
}),
|
|
213
|
+
expect.objectContaining({
|
|
214
|
+
status: '400',
|
|
215
|
+
title: 'animals.dog',
|
|
216
|
+
detail: 'Value was not an object.',
|
|
217
|
+
meta: { rule: 'isObject', model: 'user', field: 'dog' },
|
|
218
|
+
}),
|
|
219
|
+
])
|
|
220
|
+
)
|
|
146
221
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
title: 'animals',
|
|
151
|
-
detail: 'Value was not an object.',
|
|
152
|
-
meta: { rule: 'isObject', model: 'user', field: 'animals' }
|
|
153
|
-
})
|
|
222
|
+
// Insert: Ignore required subdocument property with a defined parent
|
|
223
|
+
await expect(user.validate({ animals: {} }, { validateUndefined: false })).resolves.toEqual({
|
|
224
|
+
animals: {},
|
|
154
225
|
})
|
|
155
226
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
status: '400',
|
|
192
|
-
title: 'animals.cat',
|
|
193
|
-
detail: 'This field is required.',
|
|
194
|
-
meta: { rule: 'required', model: 'user', field: 'cat' }
|
|
195
|
-
}),
|
|
196
|
-
expect.objectContaining({
|
|
197
|
-
status: '400',
|
|
198
|
-
title: 'animals.dog.color',
|
|
199
|
-
detail: 'This field is required.',
|
|
200
|
-
meta: { rule: 'required', model: 'user', field: 'color' }
|
|
201
|
-
}),
|
|
202
|
-
])
|
|
203
|
-
)
|
|
204
|
-
|
|
205
|
-
// Insert: Invalid subdocument type
|
|
206
|
-
await expect(user.validate({ animals: { dog: 1 }})).rejects.toEqual(
|
|
207
|
-
expect.arrayContaining([
|
|
208
|
-
expect.objectContaining({
|
|
209
|
-
status: '400',
|
|
210
|
-
title: 'animals.cat',
|
|
211
|
-
detail: 'This field is required.',
|
|
212
|
-
meta: { rule: 'required', model: 'user', field: 'cat' }
|
|
213
|
-
}),
|
|
214
|
-
expect.objectContaining({
|
|
215
|
-
status: '400',
|
|
216
|
-
title: 'animals.dog',
|
|
217
|
-
detail: 'Value was not an object.',
|
|
218
|
-
meta: { rule: 'isObject', model: 'user', field: 'dog' }
|
|
219
|
-
}),
|
|
220
|
-
])
|
|
221
|
-
)
|
|
222
|
-
|
|
223
|
-
// Insert: Ignore required subdocument property with a defined parent
|
|
224
|
-
await expect(user.validate({ animals: {} }, { validateUndefined: false })).resolves.toEqual({
|
|
225
|
-
animals: {}
|
|
226
|
-
})
|
|
227
|
+
// Update: Required subdocument property when a parent/grandparent is specified
|
|
228
|
+
await expect(user.validate({ animals: {} }, { update: true })).rejects.toEqual(
|
|
229
|
+
expect.arrayContaining([
|
|
230
|
+
expect.objectContaining({
|
|
231
|
+
status: '400',
|
|
232
|
+
title: 'animals.cat',
|
|
233
|
+
detail: 'This field is required.',
|
|
234
|
+
meta: { rule: 'required', model: 'user', field: 'cat' },
|
|
235
|
+
}),
|
|
236
|
+
expect.objectContaining({
|
|
237
|
+
status: '400',
|
|
238
|
+
title: 'animals.dog.color',
|
|
239
|
+
detail: 'This field is required.',
|
|
240
|
+
meta: { rule: 'required', model: 'user', field: 'color' },
|
|
241
|
+
}),
|
|
242
|
+
])
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
// Update: Required subdocument property when a parent is specified
|
|
246
|
+
await expect(user.validate({ animals: { dog: {}} }, { update: true })).rejects.toEqual(
|
|
247
|
+
expect.arrayContaining([
|
|
248
|
+
expect.objectContaining({
|
|
249
|
+
status: '400',
|
|
250
|
+
title: 'animals.cat',
|
|
251
|
+
detail: 'This field is required.',
|
|
252
|
+
meta: { rule: 'required', model: 'user', field: 'cat' },
|
|
253
|
+
}),
|
|
254
|
+
expect.objectContaining({
|
|
255
|
+
status: '400',
|
|
256
|
+
title: 'animals.dog.color',
|
|
257
|
+
detail: 'This field is required.',
|
|
258
|
+
meta: { rule: 'required', model: 'user', field: 'color' },
|
|
259
|
+
}),
|
|
260
|
+
])
|
|
261
|
+
)
|
|
227
262
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
expect.arrayContaining([
|
|
231
|
-
expect.objectContaining({
|
|
232
|
-
status: '400',
|
|
233
|
-
title: 'animals.cat',
|
|
234
|
-
detail: 'This field is required.',
|
|
235
|
-
meta: { rule: 'required', model: 'user', field: 'cat' }
|
|
236
|
-
}),
|
|
237
|
-
expect.objectContaining({
|
|
238
|
-
status: '400',
|
|
239
|
-
title: 'animals.dog.color',
|
|
240
|
-
detail: 'This field is required.',
|
|
241
|
-
meta: { rule: 'required', model: 'user', field: 'color' }
|
|
242
|
-
}),
|
|
243
|
-
])
|
|
244
|
-
)
|
|
245
|
-
|
|
246
|
-
// Update: Required subdocument property when a parent is specified
|
|
247
|
-
await expect(user.validate({ animals: { dog: {}} }, { update: true })).rejects.toEqual(
|
|
248
|
-
expect.arrayContaining([
|
|
249
|
-
expect.objectContaining({
|
|
250
|
-
status: '400',
|
|
251
|
-
title: 'animals.cat',
|
|
252
|
-
detail: 'This field is required.',
|
|
253
|
-
meta: { rule: 'required', model: 'user', field: 'cat' }
|
|
254
|
-
}),
|
|
255
|
-
expect.objectContaining({
|
|
256
|
-
status: '400',
|
|
257
|
-
title: 'animals.dog.color',
|
|
258
|
-
detail: 'This field is required.',
|
|
259
|
-
meta: { rule: 'required', model: 'user', field: 'color' }
|
|
260
|
-
}),
|
|
261
|
-
])
|
|
262
|
-
)
|
|
263
|
-
|
|
264
|
-
// Update: Ignore required subdocument property when root parent is undefined
|
|
265
|
-
await expect(user.validate({}, { update: true })).resolves.toEqual({})
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
// Update: Ignore required subdocument property with a defined parent when validateUndefined = false
|
|
269
|
-
await expect(user.validate({ animals: {} }, { update: true, validateUndefined: false })).resolves.toEqual({
|
|
270
|
-
animals: {}
|
|
271
|
-
})
|
|
263
|
+
// Update: Ignore required subdocument property when root parent is undefined
|
|
264
|
+
await expect(user.validate({}, { update: true })).resolves.toEqual({})
|
|
272
265
|
|
|
273
|
-
// Update: Required defined subdocument property when validateUndefined = false
|
|
274
|
-
await expect(user.validate({ animals: { cat: '' }}, { update: true, validateUndefined: false }))
|
|
275
|
-
.rejects.toContainEqual({
|
|
276
|
-
status: '400',
|
|
277
|
-
title: 'animals.cat',
|
|
278
|
-
detail: 'This field is required.',
|
|
279
|
-
meta: { rule: 'required', model: 'user', field: 'cat' }
|
|
280
|
-
})
|
|
281
|
-
})
|
|
282
266
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
animals: {
|
|
288
|
-
cats: [{ type: 'string' }],
|
|
289
|
-
dogs: [{
|
|
290
|
-
name: { type: 'string' },
|
|
291
|
-
color: { type: 'string', required: true }
|
|
292
|
-
}]
|
|
293
|
-
}
|
|
294
|
-
}})
|
|
295
|
-
|
|
296
|
-
// Type error within an array (string)
|
|
297
|
-
await expect(user.validate({
|
|
298
|
-
animals: { cats: [true] }
|
|
299
|
-
})).rejects.toContainEqual({
|
|
300
|
-
status: '400',
|
|
301
|
-
title: 'animals.cats.0',
|
|
302
|
-
detail: 'Value was not a string.',
|
|
303
|
-
meta: { rule: 'isString', model: 'user', field: '0' }
|
|
304
|
-
})
|
|
267
|
+
// Update: Ignore required subdocument property with a defined parent when validateUndefined = false
|
|
268
|
+
await expect(user.validate({ animals: {} }, { update: true, validateUndefined: false })).resolves.toEqual({
|
|
269
|
+
animals: {},
|
|
270
|
+
})
|
|
305
271
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
{ update: true, validateUndefined: false }
|
|
336
|
-
))
|
|
337
|
-
.resolves.toEqual({ animals: { dogs: [{ name: 'sparky' }] }})
|
|
338
|
-
|
|
339
|
-
// Requried error within an array subdocument (even during update when parent defined && validateUndefined = false)
|
|
340
|
-
await expect(user.validate(
|
|
341
|
-
{ animals: { dogs: [{ name: 'sparky', color: '' }] }},
|
|
342
|
-
{ update: true, validateUndefined: false }
|
|
343
|
-
))
|
|
344
|
-
.rejects.toContainEqual(error)
|
|
272
|
+
// Update: Required defined subdocument property when validateUndefined = false
|
|
273
|
+
await expect(user.validate({ animals: { cat: '' }}, { update: true, validateUndefined: false }))
|
|
274
|
+
.rejects.toContainEqual({
|
|
275
|
+
status: '400',
|
|
276
|
+
title: 'animals.cat',
|
|
277
|
+
detail: 'This field is required.',
|
|
278
|
+
meta: { rule: 'required', model: 'user', field: 'cat' },
|
|
279
|
+
})
|
|
280
|
+
})
|
|
281
|
+
|
|
282
|
+
test('validation array errors', async () => {
|
|
283
|
+
let user = db.model('user', { fields: {
|
|
284
|
+
animals: {
|
|
285
|
+
cats: [{ type: 'string' }],
|
|
286
|
+
dogs: [{
|
|
287
|
+
name: { type: 'string' },
|
|
288
|
+
color: { type: 'string', required: true },
|
|
289
|
+
}],
|
|
290
|
+
},
|
|
291
|
+
}})
|
|
292
|
+
|
|
293
|
+
// Type error within an array (string)
|
|
294
|
+
await expect(user.validate({
|
|
295
|
+
animals: { cats: [true] },
|
|
296
|
+
})).rejects.toContainEqual({
|
|
297
|
+
status: '400',
|
|
298
|
+
title: 'animals.cats.0',
|
|
299
|
+
detail: 'Value was not a string.',
|
|
300
|
+
meta: { rule: 'isString', model: 'user', field: '0' },
|
|
345
301
|
})
|
|
346
302
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
}})
|
|
360
|
-
|
|
361
|
-
// MinLength error
|
|
362
|
-
await expect(user.validate({
|
|
363
|
-
animals: [],
|
|
364
|
-
})).rejects.toContainEqual({
|
|
365
|
-
status: '400',
|
|
366
|
-
title: 'animals',
|
|
367
|
-
detail: 'This field is required.',
|
|
368
|
-
meta: { rule: 'required', model: 'user', field: 'animals' }
|
|
303
|
+
// Type error within an array subdocument (string)
|
|
304
|
+
let error = {
|
|
305
|
+
status: '400',
|
|
306
|
+
title: 'animals.dogs.0.color',
|
|
307
|
+
detail: 'This field is required.',
|
|
308
|
+
meta: { rule: 'required', model: 'user', field: 'color' },
|
|
309
|
+
}
|
|
310
|
+
await expect(user.validate({ animals: { dogs: [{ name: 'sparky', color: false }] }}))
|
|
311
|
+
.rejects.toContainEqual({
|
|
312
|
+
...error,
|
|
313
|
+
detail: 'Value was not a string.',
|
|
314
|
+
meta: { rule: 'isString', model: 'user', field: 'color' },
|
|
369
315
|
})
|
|
370
316
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
317
|
+
// Requried error within an array subdocument
|
|
318
|
+
await expect(user.validate({ animals: { dogs: [{ name: 'sparky' }] }}))
|
|
319
|
+
.rejects.toContainEqual(error)
|
|
320
|
+
|
|
321
|
+
// Requried error within an array subdocument (even during update when parent defined)
|
|
322
|
+
await expect(user.validate({ animals: { dogs: [{ name: 'sparky' }] }}, { update: true }))
|
|
323
|
+
.rejects.toContainEqual(error)
|
|
324
|
+
|
|
325
|
+
// No item errors for empty arrays
|
|
326
|
+
await expect(user.validate({ animals: { dogs: [] }}))
|
|
327
|
+
.resolves.toEqual({ animals: { dogs: [] }})
|
|
328
|
+
|
|
329
|
+
// No undefined item errors with validateUndefined=false
|
|
330
|
+
await expect(user.validate(
|
|
331
|
+
{ animals: { dogs: [{ name: 'sparky' }] }},
|
|
332
|
+
{ update: true, validateUndefined: false }
|
|
333
|
+
))
|
|
334
|
+
.resolves.toEqual({ animals: { dogs: [{ name: 'sparky' }] }})
|
|
335
|
+
|
|
336
|
+
// Requried error within an array subdocument (even during update when parent defined && validateUndefined = false)
|
|
337
|
+
await expect(user.validate(
|
|
338
|
+
{ animals: { dogs: [{ name: 'sparky', color: '' }] }},
|
|
339
|
+
{ update: true, validateUndefined: false }
|
|
340
|
+
))
|
|
341
|
+
.rejects.toContainEqual(error)
|
|
342
|
+
})
|
|
343
|
+
|
|
344
|
+
test('validation array schema errors', async () => {
|
|
345
|
+
function arrayWithSchema(array, schema) {
|
|
346
|
+
array.schema = schema
|
|
347
|
+
return array
|
|
348
|
+
}
|
|
349
|
+
let user = db.model('user', { fields: {
|
|
350
|
+
animals: arrayWithSchema(
|
|
351
|
+
[{ type: 'string' }],
|
|
352
|
+
{ required: true, minLength: 2 }
|
|
353
|
+
),
|
|
354
|
+
}})
|
|
355
|
+
|
|
356
|
+
// MinLength error
|
|
357
|
+
await expect(user.validate({
|
|
358
|
+
animals: [],
|
|
359
|
+
})).rejects.toContainEqual({
|
|
360
|
+
status: '400',
|
|
361
|
+
title: 'animals',
|
|
362
|
+
detail: 'This field is required.',
|
|
363
|
+
meta: { rule: 'required', model: 'user', field: 'animals' },
|
|
380
364
|
})
|
|
381
365
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
'dogs.name': {},
|
|
393
|
-
'dogs.$.name': {},
|
|
394
|
-
|
|
395
|
-
'pigs.name': {},
|
|
396
|
-
'pigs.$.name': {},
|
|
397
|
-
'pigs.1.name': {},
|
|
398
|
-
'pigs.2.name': {},
|
|
399
|
-
|
|
400
|
-
'gulls.$': {},
|
|
401
|
-
'gulls.$.$': {},
|
|
402
|
-
'gulls.name': {},
|
|
403
|
-
'gulls.$.name': {},
|
|
404
|
-
},
|
|
405
|
-
})
|
|
366
|
+
// MinLength error
|
|
367
|
+
await expect(user.validate({
|
|
368
|
+
animals: ['dog'],
|
|
369
|
+
})).rejects.toContainEqual({
|
|
370
|
+
status: '400',
|
|
371
|
+
title: 'animals',
|
|
372
|
+
detail: 'Value needs to contain a minimum of 2 items.',
|
|
373
|
+
meta: { rule: 'minLength', model: 'user', field: 'animals' },
|
|
374
|
+
})
|
|
375
|
+
})
|
|
406
376
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
377
|
+
test('validation getMostSpecificKeyMatchingPath', async () => {
|
|
378
|
+
let user = db.model('user', {
|
|
379
|
+
fields: {
|
|
380
|
+
name: { type: 'string' },
|
|
381
|
+
},
|
|
382
|
+
messages: {
|
|
383
|
+
// these are sorted when trhe model's initialised
|
|
384
|
+
'cats.name': {},
|
|
385
|
+
|
|
386
|
+
'dogs.name': {},
|
|
387
|
+
'dogs.$.name': {},
|
|
388
|
+
|
|
389
|
+
'pigs.name': {},
|
|
390
|
+
'pigs.$.name': {},
|
|
391
|
+
'pigs.1.name': {},
|
|
392
|
+
'pigs.2.name': {},
|
|
393
|
+
|
|
394
|
+
'gulls.$': {},
|
|
395
|
+
'gulls.$.$': {},
|
|
396
|
+
'gulls.name': {},
|
|
397
|
+
'gulls.$.name': {},
|
|
398
|
+
},
|
|
420
399
|
})
|
|
421
400
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
401
|
+
let fn = Model.prototype._getMostSpecificKeyMatchingPath
|
|
402
|
+
// subdocument
|
|
403
|
+
expect(fn(user.messages, 'cats.name')).toEqual('cats.name')
|
|
404
|
+
// array subdocuments
|
|
405
|
+
// expect(fn(user.messages, 'cats.1.name')).toEqual('cats.name') // no longer matches
|
|
406
|
+
expect(fn(user.messages, 'dogs.1.name')).toEqual('dogs.$.name')
|
|
407
|
+
expect(fn(user.messages, 'dogs.2.name')).toEqual('dogs.$.name')
|
|
408
|
+
expect(fn(user.messages, 'pigs.1.name')).toEqual('pigs.1.name')
|
|
409
|
+
expect(fn(user.messages, 'pigs.2.name')).toEqual('pigs.2.name')
|
|
410
|
+
expect(fn(user.messages, 'pigs.3.name')).toEqual('pigs.$.name')
|
|
411
|
+
// array
|
|
412
|
+
expect(fn(user.messages, 'gulls.1.2')).toEqual('gulls.$.$')
|
|
413
|
+
expect(fn(user.messages, 'gulls.1')).toEqual('gulls.$')
|
|
414
|
+
})
|
|
415
|
+
|
|
416
|
+
test('validation default messages', async () => {
|
|
417
|
+
let user = db.model('user', {
|
|
418
|
+
fields: {
|
|
419
|
+
name: { type: 'string', minLength: 4 },
|
|
420
|
+
dog: { name: { type: 'string', minLength: 4 }},
|
|
421
|
+
dogNames: [{ type: 'string', minLength: 4 }],
|
|
422
|
+
animals: [{
|
|
427
423
|
name: { type: 'string', minLength: 4 },
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
name: { type: 'string', minLength: 4 }
|
|
432
|
-
}]
|
|
433
|
-
}
|
|
434
|
-
})
|
|
435
|
-
|
|
436
|
-
let mock = {
|
|
437
|
-
status: '400',
|
|
438
|
-
title: 'name',
|
|
439
|
-
detail: 'Value needs to be at least 4 characters long.',
|
|
440
|
-
meta: { rule: 'minLength', model: 'user', field: 'name' }
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
// basic error
|
|
444
|
-
await expect(user.validate({ name: 'ben' })).rejects.toContainEqual(
|
|
445
|
-
mock
|
|
446
|
-
)
|
|
447
|
-
|
|
448
|
-
// subdocument error
|
|
449
|
-
await expect(user.validate({ dog: { name: 'ben' } })).rejects.toContainEqual({
|
|
450
|
-
...mock,
|
|
451
|
-
title: 'dog.name'
|
|
452
|
-
})
|
|
453
|
-
|
|
454
|
-
// array error
|
|
455
|
-
await expect(user.validate({ dogNames: ['ben'] })).rejects.toContainEqual({
|
|
456
|
-
...mock,
|
|
457
|
-
title: 'dogNames.0',
|
|
458
|
-
meta: { ...mock.meta, field: '0' }
|
|
459
|
-
})
|
|
424
|
+
}],
|
|
425
|
+
},
|
|
426
|
+
})
|
|
460
427
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
}
|
|
428
|
+
let mock = {
|
|
429
|
+
status: '400',
|
|
430
|
+
title: 'name',
|
|
431
|
+
detail: 'Value needs to be at least 4 characters long.',
|
|
432
|
+
meta: { rule: 'minLength', model: 'user', field: 'name' },
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// basic error
|
|
436
|
+
await expect(user.validate({ name: 'ben' })).rejects.toContainEqual(
|
|
437
|
+
mock
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
// subdocument error
|
|
441
|
+
await expect(user.validate({ dog: { name: 'ben' } })).rejects.toContainEqual({
|
|
442
|
+
...mock,
|
|
443
|
+
title: 'dog.name',
|
|
444
|
+
})
|
|
466
445
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
}
|
|
446
|
+
// array error
|
|
447
|
+
await expect(user.validate({ dogNames: ['ben'] })).rejects.toContainEqual({
|
|
448
|
+
...mock,
|
|
449
|
+
title: 'dogNames.0',
|
|
450
|
+
meta: { ...mock.meta, field: '0' },
|
|
472
451
|
})
|
|
473
452
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
let user = db.model('user', {
|
|
480
|
-
fields: {
|
|
481
|
-
name: { type: 'string', minLength: 4 },
|
|
482
|
-
dog: { name: { type: 'string', minLength: 4 }},
|
|
483
|
-
dogNames: [{ type: 'string', minLength: 4 }],
|
|
484
|
-
},
|
|
485
|
-
messages: {
|
|
486
|
-
'name': { minLength: 'Oops min length is 4' },
|
|
487
|
-
'dog.name': { minLength: 'Oops min length is 4' },
|
|
488
|
-
'dogNames.$': { minLength: 'Oops min length is 4' },
|
|
489
|
-
}
|
|
490
|
-
})
|
|
453
|
+
// subdocument in an array error
|
|
454
|
+
await expect(user.validate({ animals: [{ name: 'ben' }] })).rejects.toContainEqual({
|
|
455
|
+
...mock,
|
|
456
|
+
title: 'animals.0.name',
|
|
457
|
+
})
|
|
491
458
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
})
|
|
459
|
+
// subdocument in an array error (different index)
|
|
460
|
+
await expect(user.validate({ animals: [{ name: 'carla' }, { name: 'ben' }] })).rejects.toContainEqual({
|
|
461
|
+
...mock,
|
|
462
|
+
title: 'animals.1.name',
|
|
463
|
+
})
|
|
464
|
+
})
|
|
465
|
+
|
|
466
|
+
test('validation custom messages', async () => {
|
|
467
|
+
// Todo: Setup testing for array array subdocument field messages
|
|
468
|
+
// let arrayWithSchema = (array, schema) => { array.schema = schema; return array }
|
|
469
|
+
let user = db.model('user', {
|
|
470
|
+
fields: {
|
|
471
|
+
name: { type: 'string', minLength: 4 },
|
|
472
|
+
dog: { name: { type: 'string', minLength: 4 }},
|
|
473
|
+
dogNames: [{ type: 'string', minLength: 4 }],
|
|
474
|
+
},
|
|
475
|
+
messages: {
|
|
476
|
+
'name': { minLength: 'Oops min length is 4' },
|
|
477
|
+
'dog.name': { minLength: 'Oops min length is 4' },
|
|
478
|
+
'dogNames.$': { minLength: 'Oops min length is 4' },
|
|
479
|
+
},
|
|
514
480
|
})
|
|
515
481
|
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
482
|
+
let mock = {
|
|
483
|
+
status: '400',
|
|
484
|
+
title: 'name',
|
|
485
|
+
detail: 'Oops min length is 4',
|
|
486
|
+
meta: { rule: 'minLength', model: 'user', field: 'name' },
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// basic error
|
|
490
|
+
await expect(user.validate({ name: 'ben' })).rejects.toContainEqual(
|
|
491
|
+
mock
|
|
492
|
+
)
|
|
493
|
+
// subdocument error
|
|
494
|
+
await expect(user.validate({ dog: { name: 'ben' } })).rejects.toContainEqual({
|
|
495
|
+
...mock,
|
|
496
|
+
title: 'dog.name',
|
|
497
|
+
})
|
|
498
|
+
// array error
|
|
499
|
+
await expect(user.validate({ dogNames: ['ben'] })).rejects.toContainEqual({
|
|
500
|
+
...mock,
|
|
501
|
+
title: 'dogNames.0',
|
|
502
|
+
meta: { ...mock.meta, field: '0' },
|
|
503
|
+
})
|
|
504
|
+
})
|
|
505
|
+
|
|
506
|
+
test('validation custom messages for arrays', async () => {
|
|
507
|
+
// Todo: Setup testing for array array subdocument field messages
|
|
508
|
+
let arrayWithSchema = (array, schema) => { array.schema = schema; return array }
|
|
509
|
+
let user = db.model('user', {
|
|
510
|
+
fields: {
|
|
511
|
+
dogNames: arrayWithSchema([
|
|
512
|
+
arrayWithSchema([{ type: 'string' }], { minLength: 1 }),
|
|
513
|
+
], { minLength: 1 }),
|
|
514
|
+
catNames: [{
|
|
515
|
+
name: { type: 'string', minLength: 4 },
|
|
516
|
+
}],
|
|
517
|
+
pigNames: [
|
|
518
|
+
[{
|
|
519
|
+
name: { type: 'string', minLength: 4 },
|
|
528
520
|
}],
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
'pigNames.0.2.name': { minLength: 'min length error (0 2)' },
|
|
548
|
-
'pigNames.$.2.name': { minLength: 'min length error ($ 2)' },
|
|
549
|
-
}
|
|
550
|
-
})
|
|
521
|
+
],
|
|
522
|
+
},
|
|
523
|
+
messages: {
|
|
524
|
+
'dogNames': { minLength: 'add one dog name' },
|
|
525
|
+
'dogNames.$': { minLength: 'add one sub dog name' },
|
|
526
|
+
|
|
527
|
+
'catNames.$.name': { minLength: 'min length error (name)' },
|
|
528
|
+
'catNames.1.name': { minLength: 'min length error (1)' },
|
|
529
|
+
'catNames.2.name': { minLength: 'min length error (2)' },
|
|
530
|
+
|
|
531
|
+
// 'pigNames.$.$.name': { minLength: 'min length error (name)' },
|
|
532
|
+
'pigNames.$.$.name': { minLength: 'min length error ($ $)' }, // catches
|
|
533
|
+
'pigNames.$.1.name': { minLength: 'min length error ($ 1)' },
|
|
534
|
+
'pigNames.2.$.name': { minLength: 'min length error (2 $)' },
|
|
535
|
+
'pigNames.0.2.name': { minLength: 'min length error (0 2)' },
|
|
536
|
+
'pigNames.$.2.name': { minLength: 'min length error ($ 2)' },
|
|
537
|
+
},
|
|
538
|
+
})
|
|
551
539
|
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
540
|
+
// Empty array
|
|
541
|
+
await expect(user.validate({ dogNames: [] })).rejects.toContainEqual({
|
|
542
|
+
status: '400',
|
|
543
|
+
title: 'dogNames',
|
|
544
|
+
detail: 'add one dog name',
|
|
545
|
+
meta: { rule: 'minLength', model: 'user', field: 'dogNames' },
|
|
546
|
+
})
|
|
547
|
+
// Empty sub array
|
|
548
|
+
await expect(user.validate({ dogNames: [['carla']] })).resolves.toEqual({ dogNames: [['carla']] })
|
|
549
|
+
await expect(user.validate({ dogNames: [[]] })).rejects.toContainEqual({
|
|
550
|
+
status: '400',
|
|
551
|
+
title: 'dogNames.0',
|
|
552
|
+
detail: 'add one sub dog name',
|
|
553
|
+
meta: { rule: 'minLength', model: 'user', field: '0' },
|
|
554
|
+
})
|
|
567
555
|
|
|
568
556
|
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
557
|
+
// array-subdocument-field error (loose match)
|
|
558
|
+
await expect(user.validate({ catNames: [{ name: 'ben' }] })).rejects.toContainEqual({
|
|
559
|
+
status: '400',
|
|
560
|
+
title: 'catNames.0.name',
|
|
561
|
+
detail: 'min length error (name)',
|
|
562
|
+
meta: { rule: 'minLength', model: 'user', field: 'name' },
|
|
563
|
+
})
|
|
564
|
+
// array-subdocument-1-field error
|
|
565
|
+
await expect(user.validate({ catNames: [{ name: 'carla' }, { name: 'ben' }] }))
|
|
566
|
+
.rejects.toContainEqual({
|
|
567
|
+
status: '400',
|
|
568
|
+
title: 'catNames.1.name',
|
|
569
|
+
detail: 'min length error (1)',
|
|
570
|
+
meta: { rule: 'minLength', model: 'user', field: 'name' },
|
|
571
|
+
})
|
|
572
|
+
// array-subdocument-2-field error
|
|
573
|
+
await expect(user.validate({ catNames: [{ name: 'carla' }, { name: 'carla' }, { name: 'ben' }] }))
|
|
574
|
+
.rejects.toContainEqual({
|
|
575
|
+
status: '400',
|
|
576
|
+
title: 'catNames.2.name',
|
|
577
|
+
detail: 'min length error (2)',
|
|
578
|
+
meta: { rule: 'minLength', model: 'user', field: 'name' },
|
|
579
|
+
})
|
|
592
580
|
|
|
593
581
|
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
})
|
|
617
|
-
// array-subdocument-0-2-field error
|
|
618
|
-
await expect(user.validate({ pigNames: [[{ name: 'carla' }, { name: 'carla' }, { name: 'ben' }]] }))
|
|
619
|
-
.rejects.toContainEqual({
|
|
620
|
-
status: '400',
|
|
621
|
-
title: 'pigNames.0.2.name',
|
|
622
|
-
detail: 'min length error (0 2)',
|
|
623
|
-
meta: { rule: 'minLength', model: 'user', field: 'name' }
|
|
624
|
-
})
|
|
625
|
-
// array-subdocument-2-0-field error (fallback)
|
|
626
|
-
await expect(user.validate({ pigNames: [[], [{ name: 'carla' },{ name: 'carla' },{ name: 'ben' }], []] }))
|
|
627
|
-
.rejects.toContainEqual({
|
|
628
|
-
status: '400',
|
|
629
|
-
title: 'pigNames.1.2.name',
|
|
630
|
-
detail: 'min length error ($ 2)',
|
|
631
|
-
meta: { rule: 'minLength', model: 'user', field: 'name' }
|
|
632
|
-
})
|
|
582
|
+
// array-subdocument-field error (loose $ match)
|
|
583
|
+
await expect(user.validate({ pigNames: [[{ name: 'ben' }]] }))
|
|
584
|
+
.rejects.toContainEqual({
|
|
585
|
+
status: '400',
|
|
586
|
+
title: 'pigNames.0.0.name',
|
|
587
|
+
detail: 'min length error ($ $)',
|
|
588
|
+
meta: { rule: 'minLength', model: 'user', field: 'name' },
|
|
589
|
+
})
|
|
590
|
+
// array-subdocument-1-field error
|
|
591
|
+
await expect(user.validate({ pigNames: [[{ name: 'carla' }, { name: 'ben' }]] }))
|
|
592
|
+
.rejects.toContainEqual({
|
|
593
|
+
status: '400',
|
|
594
|
+
title: 'pigNames.0.1.name',
|
|
595
|
+
detail: 'min length error ($ 1)',
|
|
596
|
+
meta: { rule: 'minLength', model: 'user', field: 'name' },
|
|
597
|
+
})
|
|
598
|
+
// array-subdocument-2-0-field error (lower fallback)
|
|
599
|
+
await expect(user.validate({ pigNames: [[],[],[{ name: 'ben' }]] })).rejects.toContainEqual({
|
|
600
|
+
status: '400',
|
|
601
|
+
title: 'pigNames.2.0.name',
|
|
602
|
+
detail: 'min length error (2 $)',
|
|
603
|
+
meta: { rule: 'minLength', model: 'user', field: 'name' },
|
|
633
604
|
})
|
|
605
|
+
// array-subdocument-0-2-field error
|
|
606
|
+
await expect(user.validate({ pigNames: [[{ name: 'carla' }, { name: 'carla' }, { name: 'ben' }]] }))
|
|
607
|
+
.rejects.toContainEqual({
|
|
608
|
+
status: '400',
|
|
609
|
+
title: 'pigNames.0.2.name',
|
|
610
|
+
detail: 'min length error (0 2)',
|
|
611
|
+
meta: { rule: 'minLength', model: 'user', field: 'name' },
|
|
612
|
+
})
|
|
613
|
+
// array-subdocument-2-0-field error (fallback)
|
|
614
|
+
await expect(user.validate({ pigNames: [[], [{ name: 'carla' },{ name: 'carla' },{ name: 'ben' }], []] }))
|
|
615
|
+
.rejects.toContainEqual({
|
|
616
|
+
status: '400',
|
|
617
|
+
title: 'pigNames.1.2.name',
|
|
618
|
+
detail: 'min length error ($ 2)',
|
|
619
|
+
meta: { rule: 'minLength', model: 'user', field: 'name' },
|
|
620
|
+
})
|
|
621
|
+
})
|
|
634
622
|
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
623
|
+
test('validation custom rules', async () => {
|
|
624
|
+
let user = db.model('user', {
|
|
625
|
+
fields: {
|
|
626
|
+
name: { type: 'string', bigName: 8 },
|
|
627
|
+
animals: [{
|
|
640
628
|
name: { type: 'string', bigName: 8 },
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
629
|
+
}],
|
|
630
|
+
},
|
|
631
|
+
rules: {
|
|
632
|
+
bigName: function(value, ruleArg) {
|
|
633
|
+
return value.length >= ruleArg
|
|
644
634
|
},
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
635
|
+
},
|
|
636
|
+
})
|
|
637
|
+
let user2 = db.model('user2', {
|
|
638
|
+
fields: {
|
|
639
|
+
name: { type: 'string' },
|
|
640
|
+
nickname: { type: 'string', requiredIfNoName: true },
|
|
641
|
+
age: { type: 'number', required: true },
|
|
642
|
+
},
|
|
643
|
+
rules: {
|
|
644
|
+
requiredIfNoName: {
|
|
645
|
+
validateUndefined: true,
|
|
646
|
+
fn: function(value, ruleArg) {
|
|
647
|
+
return value || this.name
|
|
648
|
+
},
|
|
656
649
|
},
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
validateUndefined: true,
|
|
660
|
-
fn: function(value, ruleArg) {
|
|
661
|
-
return value || this.name
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
})
|
|
666
|
-
|
|
667
|
-
// Basic field
|
|
668
|
-
await expect(user.validate({ name: 'benjamin' })).resolves.toEqual({ name: 'benjamin' })
|
|
669
|
-
await expect(user.validate({ name: 'ben' })).rejects.toContainEqual({
|
|
670
|
-
status: '400',
|
|
671
|
-
title: 'name',
|
|
672
|
-
detail: 'Invalid data property for rule "bigName".',
|
|
673
|
-
meta: { rule: 'bigName', model: 'user', field: 'name' }
|
|
674
|
-
})
|
|
650
|
+
},
|
|
651
|
+
})
|
|
675
652
|
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
meta: { rule: 'bigName', model: 'user', field: 'name' }
|
|
685
|
-
})
|
|
653
|
+
// Basic field
|
|
654
|
+
await expect(user.validate({ name: 'benjamin' })).resolves.toEqual({ name: 'benjamin' })
|
|
655
|
+
await expect(user.validate({ name: 'ben' })).rejects.toContainEqual({
|
|
656
|
+
status: '400',
|
|
657
|
+
title: 'name',
|
|
658
|
+
detail: 'Invalid data property for rule "bigName".',
|
|
659
|
+
meta: { rule: 'bigName', model: 'user', field: 'name' },
|
|
660
|
+
})
|
|
686
661
|
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
{
|
|
698
|
-
'detail': 'Invalid data property for rule "requiredIfNoName".',
|
|
699
|
-
'meta': { 'field': 'nickname', 'model': 'user2', 'rule': 'requiredIfNoName'},
|
|
700
|
-
'status': '400',
|
|
701
|
-
'title': 'nickname'
|
|
702
|
-
}, {
|
|
703
|
-
'detail': 'This field is required.',
|
|
704
|
-
'meta': { 'field': 'age', 'model': 'user2', 'rule': 'required'},
|
|
705
|
-
'status': '400',
|
|
706
|
-
'title': 'age'
|
|
707
|
-
}
|
|
708
|
-
])
|
|
709
|
-
await expect(user2.validate({ }, { validateUndefined: false })).resolves.toEqual({})
|
|
662
|
+
// subdocument in an array
|
|
663
|
+
await expect(user.validate({ animals: [{ name: 'benjamin' }] })).resolves.toEqual({
|
|
664
|
+
animals: [{ name: 'benjamin' }],
|
|
665
|
+
})
|
|
666
|
+
await expect(user.validate({ animals: [{ name: 'ben' }] })).rejects.toContainEqual({
|
|
667
|
+
status: '400',
|
|
668
|
+
title: 'animals.0.name',
|
|
669
|
+
detail: 'Invalid data property for rule "bigName".',
|
|
670
|
+
meta: { rule: 'bigName', model: 'user', field: 'name' },
|
|
671
|
+
})
|
|
710
672
|
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
673
|
+
// Required rule based off another field (create)
|
|
674
|
+
await expect(user2.validate({ name: 'benjamin', age: 12 })).resolves.toEqual({
|
|
675
|
+
name: 'benjamin',
|
|
676
|
+
age: 12,
|
|
677
|
+
})
|
|
678
|
+
await expect(user2.validate({ nickname: 'benny', age: 12 })).resolves.toEqual({
|
|
679
|
+
nickname: 'benny',
|
|
680
|
+
age: 12,
|
|
681
|
+
})
|
|
682
|
+
await expect(user2.validate({ })).rejects.toEqual([
|
|
683
|
+
{
|
|
714
684
|
'detail': 'Invalid data property for rule "requiredIfNoName".',
|
|
715
685
|
'meta': { 'field': 'nickname', 'model': 'user2', 'rule': 'requiredIfNoName'},
|
|
716
686
|
'status': '400',
|
|
717
|
-
'title': 'nickname'
|
|
718
|
-
}
|
|
687
|
+
'title': 'nickname',
|
|
688
|
+
}, {
|
|
689
|
+
'detail': 'This field is required.',
|
|
690
|
+
'meta': { 'field': 'age', 'model': 'user2', 'rule': 'required'},
|
|
691
|
+
'status': '400',
|
|
692
|
+
'title': 'age',
|
|
693
|
+
},
|
|
694
|
+
])
|
|
695
|
+
await expect(user2.validate({ }, { validateUndefined: false })).resolves.toEqual({})
|
|
696
|
+
|
|
697
|
+
// Required rule based off another field (update)
|
|
698
|
+
await expect(user2.validate({ }, { update: true })).resolves.toEqual({})
|
|
699
|
+
await expect(user2.validate({ nickname: '' }, { update: true })).rejects.toEqual([{
|
|
700
|
+
'detail': 'Invalid data property for rule "requiredIfNoName".',
|
|
701
|
+
'meta': { 'field': 'nickname', 'model': 'user2', 'rule': 'requiredIfNoName'},
|
|
702
|
+
'status': '400',
|
|
703
|
+
'title': 'nickname',
|
|
704
|
+
}])
|
|
705
|
+
})
|
|
706
|
+
|
|
707
|
+
test('validated data', async () => {
|
|
708
|
+
let fields = {
|
|
709
|
+
name: { type: 'string' },
|
|
710
|
+
names: [{ type: 'string' }],
|
|
711
|
+
animals: {
|
|
712
|
+
dog: { type: 'string' },
|
|
713
|
+
dogs: [{ name: { type: 'string' } }],
|
|
714
|
+
},
|
|
715
|
+
}
|
|
716
|
+
fields.names.schema = { nullObject: true }
|
|
717
|
+
fields.animals.schema = { nullObject: true }
|
|
718
|
+
let user = db.model('user', { fields: fields })
|
|
719
|
+
|
|
720
|
+
// No data
|
|
721
|
+
await expect(user.validate({})).resolves.toEqual({})
|
|
722
|
+
|
|
723
|
+
// Ignores invalid data
|
|
724
|
+
await expect(user.validate({ badprop: true, schema: {} })).resolves.toEqual({})
|
|
725
|
+
|
|
726
|
+
// String data
|
|
727
|
+
await expect(user.validate({ name: 'Martin Luther' })).resolves.toEqual({ name: 'Martin Luther' })
|
|
728
|
+
await expect(user.validate({ name: null })).resolves.toEqual({ name: null })
|
|
729
|
+
|
|
730
|
+
// Array data
|
|
731
|
+
await expect(user.validate({ names: ['blue'] })).resolves.toEqual({ names: ['blue'] })
|
|
732
|
+
|
|
733
|
+
// Array data (empty)
|
|
734
|
+
await expect(user.validate({ names: [] })).resolves.toEqual({ names: [] })
|
|
735
|
+
|
|
736
|
+
// Subdocument data
|
|
737
|
+
await expect(user.validate({ animals: { dog: 'sparky' } }))
|
|
738
|
+
.resolves.toEqual({ animals: { dog: 'sparky' } })
|
|
739
|
+
|
|
740
|
+
// Subdocument data (empty)
|
|
741
|
+
await expect(user.validate({ animals: {} })).resolves.toEqual({ animals: {} })
|
|
742
|
+
|
|
743
|
+
// Subdocument data (null/string)
|
|
744
|
+
await expect(user.validate({ animals: '', names: null })).resolves.toEqual({ animals: null, names: null })
|
|
745
|
+
|
|
746
|
+
// Subdocument property data (null)
|
|
747
|
+
await expect(user.validate({ animals: { dog: null }}))
|
|
748
|
+
.resolves.toEqual({ animals: { dog: null }})
|
|
749
|
+
|
|
750
|
+
// Subdocument property data (unknown data)
|
|
751
|
+
await expect(user.validate({ animals: { dog: 'sparky', cat: 'grumpy' } }))
|
|
752
|
+
.resolves.toEqual({ animals: { dog: 'sparky' } })
|
|
753
|
+
|
|
754
|
+
// Subdocument -> array -> subdocument data
|
|
755
|
+
await expect(user.validate({ animals: { dogs: [{ name: 'sparky' }] }}))
|
|
756
|
+
.resolves.toEqual({ animals: { dogs: [{ name: 'sparky' }] }})
|
|
757
|
+
|
|
758
|
+
// Subdocument -> array -> subdocument data (empty)
|
|
759
|
+
await expect(user.validate({ animals: { dogs: [{}] }}))
|
|
760
|
+
.resolves.toEqual({ animals: { dogs: [{}] }})
|
|
761
|
+
|
|
762
|
+
// _id is blacklisted by default
|
|
763
|
+
let id = db.id()
|
|
764
|
+
await expect(user.validate({ _id: id })).resolves.toEqual({})
|
|
765
|
+
await expect(user.validate({ _id: id }, { update: true })).resolves.toEqual({})
|
|
766
|
+
})
|
|
767
|
+
|
|
768
|
+
test('schema options', async () => {
|
|
769
|
+
let user = db.model('user', { fields: {
|
|
770
|
+
name: { type: 'string', 'insertOnly': true },
|
|
771
|
+
}})
|
|
772
|
+
let user2 = db.model('user2', { fields: {
|
|
773
|
+
name: { type: 'string', defaultOverride: true, default: 'Martin Luther' },
|
|
774
|
+
}})
|
|
775
|
+
let user4 = db.model('user4', { fields: {
|
|
776
|
+
name: { model: true },
|
|
777
|
+
}})
|
|
778
|
+
|
|
779
|
+
// Ignore insertOnly fields when updating
|
|
780
|
+
await expect(user.validate({ name: 'Martin Luther' }, { update: true })).resolves.toEqual({})
|
|
781
|
+
|
|
782
|
+
// Default
|
|
783
|
+
await expect(user2.validate({})).resolves.toEqual({ name: 'Martin Luther' })
|
|
784
|
+
|
|
785
|
+
// Default override
|
|
786
|
+
await expect(user2.validate({ name : 'temp' })).resolves.toEqual({ name: 'Martin Luther' })
|
|
787
|
+
|
|
788
|
+
// Model id (ObjectId)
|
|
789
|
+
let data = await user4.validate({ name: '5d4356299d0f010017602f6b' })
|
|
790
|
+
await expect(data.name.toString()).toEqual(db.id('5d4356299d0f010017602f6b').toString())
|
|
791
|
+
await expect(data.name).toEqual(expect.any(Object))
|
|
792
|
+
|
|
793
|
+
// Bad model id (ObjectId)
|
|
794
|
+
await expect(user4.validate({ name: 'badid' })).rejects.toContainEqual({
|
|
795
|
+
status: '400',
|
|
796
|
+
title: 'name',
|
|
797
|
+
detail: 'Value was not a valid ObjectId.',
|
|
798
|
+
meta: { rule: 'isId', model: 'user4', field: 'name' },
|
|
719
799
|
})
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
// Array data (empty)
|
|
750
|
-
await expect(user.validate({ names: [] })).resolves.toEqual({ names: [] })
|
|
751
|
-
|
|
752
|
-
// Subdocument data
|
|
753
|
-
await expect(user.validate({ animals: { dog: 'sparky' } }))
|
|
754
|
-
.resolves.toEqual({ animals: { dog: 'sparky' } })
|
|
755
|
-
|
|
756
|
-
// Subdocument data (empty)
|
|
757
|
-
await expect(user.validate({ animals: {} })).resolves.toEqual({ animals: {} })
|
|
758
|
-
|
|
759
|
-
// Subdocument data (null/string)
|
|
760
|
-
await expect(user.validate({ animals: '', names: null })).resolves.toEqual({ animals: null, names: null })
|
|
761
|
-
|
|
762
|
-
// Subdocument property data (null)
|
|
763
|
-
await expect(user.validate({ animals: { dog: null }}))
|
|
764
|
-
.resolves.toEqual({ animals: { dog: null }})
|
|
765
|
-
|
|
766
|
-
// Subdocument property data (unknown data)
|
|
767
|
-
await expect(user.validate({ animals: { dog: 'sparky', cat: 'grumpy' } }))
|
|
768
|
-
.resolves.toEqual({ animals: { dog: 'sparky' } })
|
|
769
|
-
|
|
770
|
-
// Subdocument -> array -> subdocument data
|
|
771
|
-
await expect(user.validate({ animals: { dogs: [{ name: 'sparky' }] }}))
|
|
772
|
-
.resolves.toEqual({ animals: { dogs: [{ name: 'sparky' }] }})
|
|
773
|
-
|
|
774
|
-
// Subdocument -> array -> subdocument data (empty)
|
|
775
|
-
await expect(user.validate({ animals: { dogs: [{}] }}))
|
|
776
|
-
.resolves.toEqual({ animals: { dogs: [{}] }})
|
|
777
|
-
|
|
778
|
-
// _id is blacklisted by default
|
|
779
|
-
let id = db.id()
|
|
780
|
-
await expect(user.validate({ _id: id })).resolves.toEqual({})
|
|
781
|
-
await expect(user.validate({ _id: id }, { update: true })).resolves.toEqual({})
|
|
800
|
+
})
|
|
801
|
+
|
|
802
|
+
test('schema options default', async () => {
|
|
803
|
+
// todo: test objects
|
|
804
|
+
// todo: change test name
|
|
805
|
+
let user = db.model('user', { fields: {
|
|
806
|
+
name: { type: 'string', minLength: 7 },
|
|
807
|
+
email: { type: 'string', isEmail: true },
|
|
808
|
+
amount: { type: 'number' },
|
|
809
|
+
}})
|
|
810
|
+
let user2 = db.model('user', { fields: {
|
|
811
|
+
amount: { type: 'number', required: true },
|
|
812
|
+
}})
|
|
813
|
+
let user3 = db.model('user', { fields: {
|
|
814
|
+
names: { type: 'string', enum: ['Martin', 'Luther'], default: 'Martin' },
|
|
815
|
+
}})
|
|
816
|
+
|
|
817
|
+
// MinLength
|
|
818
|
+
await expect(user.validate({ name: 'Martin Luther' })).resolves.toEqual({name: 'Martin Luther'})
|
|
819
|
+
await expect(user.validate({ name: 'Carl' })).rejects.toContainEqual({
|
|
820
|
+
detail: 'Value needs to be at least 7 characters long.',
|
|
821
|
+
status: '400',
|
|
822
|
+
title: 'name',
|
|
823
|
+
meta: {
|
|
824
|
+
model: 'user',
|
|
825
|
+
field: 'name',
|
|
826
|
+
rule: 'minLength',
|
|
827
|
+
},
|
|
782
828
|
})
|
|
783
829
|
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
name: { model: true }
|
|
796
|
-
}})
|
|
797
|
-
|
|
798
|
-
// Ignore insertOnly fields when updating
|
|
799
|
-
await expect(user.validate({ name: 'Martin Luther' }, { update: true })).resolves.toEqual({})
|
|
800
|
-
|
|
801
|
-
// Default
|
|
802
|
-
await expect(user2.validate({})).resolves.toEqual({ name: 'Martin Luther' })
|
|
803
|
-
|
|
804
|
-
// Default override
|
|
805
|
-
await expect(user2.validate({ name : 'temp' })).resolves.toEqual({ name: 'Martin Luther' })
|
|
806
|
-
|
|
807
|
-
// Index, mongodb connection error
|
|
808
|
-
await expect(user3._setupIndexes({ name: { type: 'string', index: 'text' }})).rejects
|
|
809
|
-
.toEqual({ type: 'info', detail: 'Skipping createIndex on the \'user3\' model, no mongodb connection found.' })
|
|
810
|
-
|
|
811
|
-
// Model id (Monk ObjectId)
|
|
812
|
-
let data = await user4.validate({ name: '5d4356299d0f010017602f6b' })
|
|
813
|
-
await expect(data.name.toString()).toEqual(db.id('5d4356299d0f010017602f6b').toString())
|
|
814
|
-
await expect(data.name).toEqual(expect.any(Object))
|
|
815
|
-
|
|
816
|
-
// Bad model id (Monk ObjectId)
|
|
817
|
-
await expect(user4.validate({ name: 'badid' })).rejects.toContainEqual({
|
|
818
|
-
status: '400',
|
|
819
|
-
title: 'name',
|
|
820
|
-
detail: 'Value was not a valid ObjectId.',
|
|
821
|
-
meta: { rule: 'isId', model: 'user4', field: 'name' }
|
|
822
|
-
})
|
|
830
|
+
// isEmail
|
|
831
|
+
await expect(user.validate({ email: 'good@g.com' })).resolves.toEqual({email: 'good@g.com'})
|
|
832
|
+
await expect(user.validate({ email: 'bad email' })).rejects.toContainEqual({
|
|
833
|
+
detail: 'Please enter a valid email address.',
|
|
834
|
+
status: '400',
|
|
835
|
+
title: 'email',
|
|
836
|
+
meta: {
|
|
837
|
+
model: 'user',
|
|
838
|
+
field: 'email',
|
|
839
|
+
rule: 'isEmail',
|
|
840
|
+
},
|
|
823
841
|
})
|
|
824
842
|
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
}})
|
|
838
|
-
let user3 = db.model('user', { fields: {
|
|
839
|
-
names: { type: 'string', enum: ['Martin', 'Luther'], default: 'Martin' },
|
|
840
|
-
}})
|
|
841
|
-
|
|
842
|
-
// MinLength
|
|
843
|
-
await expect(user.validate({ name: 'Martin Luther' })).resolves.toEqual({name: 'Martin Luther'})
|
|
844
|
-
await expect(user.validate({ name: 'Carl' })).rejects.toContainEqual({
|
|
845
|
-
detail: 'Value needs to be at least 7 characters long.',
|
|
846
|
-
status: '400',
|
|
847
|
-
title: 'name',
|
|
848
|
-
meta: {
|
|
849
|
-
model: 'user',
|
|
850
|
-
field: 'name',
|
|
851
|
-
rule: 'minLength'
|
|
852
|
-
}
|
|
853
|
-
})
|
|
854
|
-
|
|
855
|
-
// isEmail
|
|
856
|
-
await expect(user.validate({ email: 'good@g.com' })).resolves.toEqual({email: 'good@g.com'})
|
|
857
|
-
await expect(user.validate({ email: 'bad email' })).rejects.toContainEqual({
|
|
858
|
-
detail: 'Please enter a valid email address.',
|
|
859
|
-
status: '400',
|
|
860
|
-
title: 'email',
|
|
861
|
-
meta: {
|
|
862
|
-
model: 'user',
|
|
863
|
-
field: 'email',
|
|
864
|
-
rule: 'isEmail'
|
|
865
|
-
}
|
|
866
|
-
})
|
|
867
|
-
|
|
868
|
-
// Enum
|
|
869
|
-
await expect(user3.validate({})).resolves.toEqual({ names: 'Martin' })
|
|
870
|
-
await expect(user3.validate({ names: 'Luther' })).resolves.toEqual({ names: 'Luther' })
|
|
871
|
-
await expect(user3.validate({ names: 'bad name' })).rejects.toContainEqual({
|
|
872
|
-
detail: 'Invalid enum value',
|
|
873
|
-
status: '400',
|
|
874
|
-
title: 'names',
|
|
875
|
-
meta: {
|
|
876
|
-
model: 'user',
|
|
877
|
-
field: 'names',
|
|
878
|
-
rule: 'enum'
|
|
879
|
-
}
|
|
880
|
-
})
|
|
881
|
-
|
|
882
|
-
// Number valid
|
|
883
|
-
await expect(user2.validate({ amount: 0 })).resolves.toEqual({ amount: 0 })
|
|
884
|
-
await expect(user2.validate({ amount: '0' })).resolves.toEqual({ amount: 0 })
|
|
885
|
-
await expect(user.validate({ amount: undefined })).resolves.toEqual({})
|
|
886
|
-
await expect(user.validate({ amount: null })).resolves.toEqual({ amount: null })
|
|
887
|
-
|
|
888
|
-
// Number required
|
|
889
|
-
let mock1 = {
|
|
890
|
-
detail: 'This field is required.',
|
|
891
|
-
status: '400',
|
|
892
|
-
title: 'amount',
|
|
893
|
-
meta: { model: 'user', field: 'amount', rule: 'required' }
|
|
894
|
-
}
|
|
895
|
-
await expect(user2.validate({})).rejects.toContainEqual(mock1)
|
|
896
|
-
await expect(user2.validate({ amount: '' })).rejects.toContainEqual(mock1)
|
|
897
|
-
await expect(user2.validate({ amount: undefined })).rejects.toContainEqual(mock1)
|
|
898
|
-
await expect(user2.validate({ amount: null })).rejects.toContainEqual(mock1)
|
|
899
|
-
|
|
900
|
-
// Number invalid
|
|
901
|
-
let mock2 = {
|
|
902
|
-
detail: 'Value was not a number.',
|
|
903
|
-
status: '400',
|
|
904
|
-
title: 'amount',
|
|
905
|
-
meta: { model: 'user', field: 'amount', rule: 'isNumber' }
|
|
906
|
-
}
|
|
907
|
-
await expect(user.validate({ amount: false })).rejects.toContainEqual(mock2)
|
|
908
|
-
await expect(user.validate({ amount: 'bad' })).rejects.toContainEqual(mock2)
|
|
843
|
+
// Enum
|
|
844
|
+
await expect(user3.validate({})).resolves.toEqual({ names: 'Martin' })
|
|
845
|
+
await expect(user3.validate({ names: 'Luther' })).resolves.toEqual({ names: 'Luther' })
|
|
846
|
+
await expect(user3.validate({ names: 'bad name' })).rejects.toContainEqual({
|
|
847
|
+
detail: 'Invalid enum value',
|
|
848
|
+
status: '400',
|
|
849
|
+
title: 'names',
|
|
850
|
+
meta: {
|
|
851
|
+
model: 'user',
|
|
852
|
+
field: 'names',
|
|
853
|
+
rule: 'enum',
|
|
854
|
+
},
|
|
909
855
|
})
|
|
910
856
|
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
857
|
+
// Number valid
|
|
858
|
+
await expect(user2.validate({ amount: 0 })).resolves.toEqual({ amount: 0 })
|
|
859
|
+
await expect(user2.validate({ amount: '0' })).resolves.toEqual({ amount: 0 })
|
|
860
|
+
await expect(user.validate({ amount: undefined })).resolves.toEqual({})
|
|
861
|
+
await expect(user.validate({ amount: null })).resolves.toEqual({ amount: null })
|
|
862
|
+
|
|
863
|
+
// Number required
|
|
864
|
+
let mock1 = {
|
|
865
|
+
detail: 'This field is required.',
|
|
866
|
+
status: '400',
|
|
867
|
+
title: 'amount',
|
|
868
|
+
meta: { model: 'user', field: 'amount', rule: 'required' },
|
|
869
|
+
}
|
|
870
|
+
await expect(user2.validate({})).rejects.toContainEqual(mock1)
|
|
871
|
+
await expect(user2.validate({ amount: '' })).rejects.toContainEqual(mock1)
|
|
872
|
+
await expect(user2.validate({ amount: undefined })).rejects.toContainEqual(mock1)
|
|
873
|
+
await expect(user2.validate({ amount: null })).rejects.toContainEqual(mock1)
|
|
874
|
+
|
|
875
|
+
// Number invalid
|
|
876
|
+
let mock2 = {
|
|
877
|
+
detail: 'Value was not a number.',
|
|
878
|
+
status: '400',
|
|
879
|
+
title: 'amount',
|
|
880
|
+
meta: { model: 'user', field: 'amount', rule: 'isNumber' },
|
|
881
|
+
}
|
|
882
|
+
await expect(user.validate({ amount: false })).rejects.toContainEqual(mock2)
|
|
883
|
+
await expect(user.validate({ amount: 'bad' })).rejects.toContainEqual(mock2)
|
|
884
|
+
})
|
|
885
|
+
|
|
886
|
+
test('schema options objects', async () => {
|
|
887
|
+
const db2 = monastery('127.0.0.1/monastery', { nullObjects: true, timestamps: false })
|
|
888
|
+
let user = db2.model('user', {
|
|
889
|
+
fields: {
|
|
890
|
+
location: {
|
|
891
|
+
lat: { type: 'number' },
|
|
892
|
+
lng: { type: 'number' },
|
|
893
|
+
schema: { required: true },
|
|
924
894
|
},
|
|
925
|
-
}
|
|
926
|
-
let requiredError = [{
|
|
927
|
-
'detail': 'This field is required.',
|
|
928
|
-
'meta': {'detailLong': undefined, 'field': 'location', 'model': 'user', 'rule': 'required'},
|
|
929
|
-
'status': '400',
|
|
930
|
-
'title': 'location'
|
|
931
|
-
}]
|
|
932
|
-
// required errors
|
|
933
|
-
await expect(user.validate({ location: null })).rejects.toEqual(requiredError)
|
|
934
|
-
await expect(user.validate({ location: '' })).rejects.toEqual(requiredError)
|
|
935
|
-
await expect(user.validate({ location: undefined })).rejects.toEqual(requiredError)
|
|
936
|
-
// required no error
|
|
937
|
-
await expect(user.validate({ location: {} })).resolves.toEqual({ location: {} })
|
|
938
|
-
db.close()
|
|
895
|
+
},
|
|
939
896
|
})
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
897
|
+
let requiredError = [{
|
|
898
|
+
'detail': 'This field is required.',
|
|
899
|
+
'meta': {'detailLong': undefined, 'field': 'location', 'model': 'user', 'rule': 'required'},
|
|
900
|
+
'status': '400',
|
|
901
|
+
'title': 'location',
|
|
902
|
+
}]
|
|
903
|
+
// required errors
|
|
904
|
+
await expect(user.validate({ location: null })).rejects.toEqual(requiredError)
|
|
905
|
+
await expect(user.validate({ location: '' })).rejects.toEqual(requiredError)
|
|
906
|
+
await expect(user.validate({ location: undefined })).rejects.toEqual(requiredError)
|
|
907
|
+
// required no error
|
|
908
|
+
await expect(user.validate({ location: {} })).resolves.toEqual({ location: {} })
|
|
909
|
+
db2.close()
|
|
910
|
+
})
|
|
911
|
+
|
|
912
|
+
test('validate defaultObjects', async () => {
|
|
913
|
+
const db2 = monastery('127.0.0.1/monastery', { defaultObjects: true, timestamps: false })
|
|
914
|
+
// let base = { names: [], animals: { dogs: [] }}
|
|
915
|
+
let user = db2.model('user', { fields: {
|
|
916
|
+
name: { type: 'string' },
|
|
917
|
+
names: [{ type: 'string' }],
|
|
918
|
+
animals: {
|
|
919
|
+
dog: { type: 'string' },
|
|
920
|
+
dogs: [{ name: { type: 'string' } }],
|
|
921
|
+
},
|
|
922
|
+
}})
|
|
923
|
+
|
|
924
|
+
// Array/subdocument defaults
|
|
925
|
+
await expect(user.validate({})).resolves.toEqual({
|
|
926
|
+
names: [],
|
|
927
|
+
animals: { dogs: [] },
|
|
965
928
|
})
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
}
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
await expect(user.validate({ animals: '', names: null })).resolves.toEqual({ animals: null, names: null })
|
|
989
|
-
|
|
990
|
-
db.close()
|
|
929
|
+
db2.close()
|
|
930
|
+
})
|
|
931
|
+
|
|
932
|
+
test('validate nullObjects', async () => {
|
|
933
|
+
const db2 = monastery('127.0.0.1/monastery', { nullObjects: true, timestamps: false })
|
|
934
|
+
let user = db2.model('user', { fields: {
|
|
935
|
+
names: [{ type: 'string' }],
|
|
936
|
+
animals: {
|
|
937
|
+
dog: { type: 'string' },
|
|
938
|
+
dogs: [{ name: { type: 'string' } }],
|
|
939
|
+
},
|
|
940
|
+
}})
|
|
941
|
+
|
|
942
|
+
// Subdocument data (null/string)
|
|
943
|
+
await expect(user.validate({ animals: 'notAnObject' })).rejects.toEqual([{
|
|
944
|
+
'detail': 'Value was not an object.',
|
|
945
|
+
'meta': {'detailLong': undefined, 'field': 'animals', 'model': 'user', 'rule': 'isObject'},
|
|
946
|
+
'status': '400',
|
|
947
|
+
'title': 'animals',
|
|
948
|
+
}])
|
|
949
|
+
await expect(user.validate({ animals: '', names: null })).resolves.toEqual({
|
|
950
|
+
animals: null, names: null,
|
|
991
951
|
})
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
}
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
}
|
|
1003
|
-
}
|
|
1004
|
-
}
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
952
|
+
db2.close()
|
|
953
|
+
})
|
|
954
|
+
|
|
955
|
+
test('validation option skipValidation', async () => {
|
|
956
|
+
let user = db.model('user', { fields: {
|
|
957
|
+
name: { type: 'string', required: true },
|
|
958
|
+
}})
|
|
959
|
+
let user2 = db.model('user2', { fields: {
|
|
960
|
+
my: {
|
|
961
|
+
name: {
|
|
962
|
+
is: { type: 'string', required: true },
|
|
963
|
+
},
|
|
964
|
+
},
|
|
965
|
+
}})
|
|
966
|
+
let user3 = db.model('user3', { fields: {
|
|
967
|
+
people: [{
|
|
968
|
+
name: { type: 'string', required: true },
|
|
969
|
+
}],
|
|
970
|
+
people2: [{
|
|
971
|
+
people3: [{
|
|
972
|
+
name: { type: 'string', required: true },
|
|
1008
973
|
}],
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
model: 'user3',
|
|
1053
|
-
rule: 'required'
|
|
1054
|
-
}
|
|
1055
|
-
})
|
|
974
|
+
}],
|
|
975
|
+
}})
|
|
976
|
+
let user4 = db.model('user4', { fields: {
|
|
977
|
+
code: { type: 'string', required: true },
|
|
978
|
+
address: {
|
|
979
|
+
city: { type: 'string', required: true },
|
|
980
|
+
country: { type: 'string', required: true },
|
|
981
|
+
},
|
|
982
|
+
}})
|
|
983
|
+
|
|
984
|
+
// Skip validation on the required fields
|
|
985
|
+
await expect(user.validate({}, { skipValidation: true })).resolves.toEqual({})
|
|
986
|
+
await expect(user.validate({}, { skipValidation: ['name'] })).resolves.toEqual({})
|
|
987
|
+
await expect(user2.validate({}, { skipValidation: ['my.name'] })).resolves.toEqual({})
|
|
988
|
+
await expect(user3.validate({ people: [{}] }, { skipValidation: ['people.name'] }))
|
|
989
|
+
.resolves.toEqual({ people: [{}] })
|
|
990
|
+
|
|
991
|
+
// Skip all array items
|
|
992
|
+
await expect(user3.validate(
|
|
993
|
+
{ people2: [{ people3: [{}, {}] }] },
|
|
994
|
+
{ skipValidation: ['people2.$.people3.$.name'] }
|
|
995
|
+
)).resolves.toEqual({ people2: [{ people3: [{}, {}] }] })
|
|
996
|
+
|
|
997
|
+
// Skip all array items (array expanding)
|
|
998
|
+
await expect(user3.validate(
|
|
999
|
+
{ people2: [{ people3: [{}, {}] }] },
|
|
1000
|
+
{ skipValidation: ['people2.people3.name'] }
|
|
1001
|
+
)).resolves.toEqual({ people2: [{ people3: [{}, {}] }] })
|
|
1002
|
+
|
|
1003
|
+
// Skip a certain array index
|
|
1004
|
+
await expect(user3.validate(
|
|
1005
|
+
{ people2: [{ people3: [{}, {}] }] },
|
|
1006
|
+
{ skipValidation: ['people2.$.people3.0.name'] }
|
|
1007
|
+
)).rejects.toContainEqual({
|
|
1008
|
+
detail: 'This field is required.',
|
|
1009
|
+
status: '400',
|
|
1010
|
+
title: 'people2.0.people3.1.name',
|
|
1011
|
+
meta: {
|
|
1012
|
+
field: 'name',
|
|
1013
|
+
model: 'user3',
|
|
1014
|
+
rule: 'required',
|
|
1015
|
+
},
|
|
1016
|
+
})
|
|
1056
1017
|
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1018
|
+
// Skip multiple fields
|
|
1019
|
+
await expect(user4.validate(
|
|
1020
|
+
{ address: { city: 'christchurch', country: 'ewf' }},
|
|
1021
|
+
{ skipValidation: ['code'] }
|
|
1022
|
+
)).resolves.toEqual({
|
|
1023
|
+
address: { city: 'christchurch', country: 'ewf' },
|
|
1024
|
+
})
|
|
1064
1025
|
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1026
|
+
// Non existing validation field entries
|
|
1027
|
+
await expect(user3.validate({ people: [{}] }, { skipValidation: ['people.badField'] })).rejects.toContainEqual({
|
|
1028
|
+
detail: 'This field is required.',
|
|
1029
|
+
status: '400',
|
|
1030
|
+
title: 'people.0.name',
|
|
1031
|
+
meta: {
|
|
1032
|
+
model: 'user3',
|
|
1033
|
+
field: 'name',
|
|
1034
|
+
rule: 'required',
|
|
1035
|
+
},
|
|
1036
|
+
})
|
|
1037
|
+
})
|
|
1038
|
+
|
|
1039
|
+
test('validation option validateUndefined', async () => {
|
|
1040
|
+
// ValidateUndefined runs required rules on all fields, `true` for insert, `false` for update.
|
|
1041
|
+
let user = db.model('user', { fields: {
|
|
1042
|
+
date: { type: 'number' },
|
|
1043
|
+
name: { type: 'string', required: true },
|
|
1044
|
+
}})
|
|
1045
|
+
let usernum = db.model('usernum', { fields: {
|
|
1046
|
+
amount: { type: 'number', required: true },
|
|
1047
|
+
}})
|
|
1048
|
+
let userdeep = db.model('userdeep', { fields: {
|
|
1049
|
+
date: { type: 'number' },
|
|
1050
|
+
name: {
|
|
1051
|
+
first: { type: 'string', required: true },
|
|
1052
|
+
},
|
|
1053
|
+
names: [{
|
|
1054
|
+
first: { type: 'string', required: true },
|
|
1055
|
+
}],
|
|
1056
|
+
}})
|
|
1057
|
+
let errorRequired = {
|
|
1058
|
+
status: '400',
|
|
1059
|
+
title: 'name',
|
|
1060
|
+
detail: 'This field is required.',
|
|
1061
|
+
meta: expect.any(Object),
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
// Required error for undefined
|
|
1065
|
+
await expect(user.validate({}))
|
|
1066
|
+
.rejects.toEqual([errorRequired])
|
|
1067
|
+
await expect(user.validate({}, { update: true, validateUndefined: true }))
|
|
1068
|
+
.rejects.toEqual([errorRequired])
|
|
1069
|
+
await expect(userdeep.validate({}))
|
|
1070
|
+
.rejects.toEqual([{ ...errorRequired, title: 'name.first' }])
|
|
1071
|
+
await expect(userdeep.validate({ name: {} }, { update: true }))
|
|
1072
|
+
.rejects.toEqual([{ ...errorRequired, title: 'name.first' }])
|
|
1073
|
+
await expect(userdeep.validate({ names: [{}] }, { update: true }))
|
|
1074
|
+
.rejects.toEqual([{ ...errorRequired, title: 'names.0.first' }])
|
|
1075
|
+
|
|
1076
|
+
// Required error for null
|
|
1077
|
+
await expect(user.validate({ name: null }, { update: true }))
|
|
1078
|
+
.rejects.toEqual([errorRequired])
|
|
1079
|
+
await expect(usernum.validate({ amount: null }, { update: true }))
|
|
1080
|
+
.rejects.toEqual([{ ...errorRequired, title: 'amount' }])
|
|
1081
|
+
await expect(user.validate({ name: null }, { update: true, validateUndefined: true }))
|
|
1082
|
+
.rejects.toEqual([errorRequired])
|
|
1083
|
+
|
|
1084
|
+
// Skip required error
|
|
1085
|
+
await expect(user.validate({ name: undefined }, { validateUndefined: false })).resolves.toEqual({})
|
|
1086
|
+
await expect(user.validate({}, { validateUndefined: false })).resolves.toEqual({})
|
|
1087
|
+
await expect(user.validate({}, { update: true })).resolves.toEqual({})
|
|
1088
|
+
await expect(user.validate({}, { update: true, validateUndefined: false })).resolves.toEqual({})
|
|
1089
|
+
await expect(userdeep.validate({}, { update: true })).resolves.toEqual({})
|
|
1090
|
+
await expect(userdeep.validate({ name: {} }, { update: true, validateUndefined: false }))
|
|
1091
|
+
.resolves.toEqual({ name: {} })
|
|
1092
|
+
await expect(userdeep.validate({ names: [{}] }, { update: true, validateUndefined: false }))
|
|
1093
|
+
.resolves.toEqual({ names: [{}] })
|
|
1094
|
+
})
|
|
1095
|
+
|
|
1096
|
+
test('validation hooks', async () => {
|
|
1097
|
+
let user = db.model('user', {
|
|
1098
|
+
fields: {
|
|
1099
|
+
first: { type: 'string'},
|
|
1100
|
+
last: { type: 'string'},
|
|
1101
|
+
},
|
|
1102
|
+
beforeValidate: [(data, next) => {
|
|
1103
|
+
if (!data.first) {
|
|
1104
|
+
next(new Error('beforeValidate error 1..'))
|
|
1105
|
+
} else if (!data.last) {
|
|
1106
|
+
setTimeout(function() {
|
|
1107
|
+
next(new Error('beforeValidate error 2..'))
|
|
1108
|
+
}, 100)
|
|
1109
|
+
} else {
|
|
1110
|
+
next()
|
|
1074
1111
|
}
|
|
1075
|
-
}
|
|
1112
|
+
}],
|
|
1076
1113
|
})
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
name: { type: 'string', required: true },
|
|
1086
|
-
}})
|
|
1087
|
-
let usernum = db.model('usernum', { fields: {
|
|
1088
|
-
amount: { type: 'number', required: true }
|
|
1089
|
-
}})
|
|
1090
|
-
let userdeep = db.model('userdeep', { fields: {
|
|
1091
|
-
date: { type: 'number' },
|
|
1092
|
-
name: {
|
|
1093
|
-
first: { type: 'string', required: true },
|
|
1094
|
-
},
|
|
1095
|
-
names: [{
|
|
1096
|
-
first: { type: 'string', required: true },
|
|
1097
|
-
}]
|
|
1098
|
-
}})
|
|
1099
|
-
let errorRequired = {
|
|
1100
|
-
status: '400',
|
|
1101
|
-
title: 'name',
|
|
1102
|
-
detail: 'This field is required.',
|
|
1103
|
-
meta: expect.any(Object),
|
|
1104
|
-
}
|
|
1105
|
-
|
|
1106
|
-
// Required error for undefined
|
|
1107
|
-
await expect(user.validate({}))
|
|
1108
|
-
.rejects.toEqual([errorRequired])
|
|
1109
|
-
await expect(user.validate({}, { update: true, validateUndefined: true }))
|
|
1110
|
-
.rejects.toEqual([errorRequired])
|
|
1111
|
-
await expect(userdeep.validate({}))
|
|
1112
|
-
.rejects.toEqual([{ ...errorRequired, title: 'name.first' }])
|
|
1113
|
-
await expect(userdeep.validate({ name: {} }, { update: true }))
|
|
1114
|
-
.rejects.toEqual([{ ...errorRequired, title: 'name.first' }])
|
|
1115
|
-
await expect(userdeep.validate({ names: [{}] }, { update: true }))
|
|
1116
|
-
.rejects.toEqual([{ ...errorRequired, title: 'names.0.first' }])
|
|
1117
|
-
|
|
1118
|
-
// Required error for null
|
|
1119
|
-
await expect(user.validate({ name: null }, { update: true }))
|
|
1120
|
-
.rejects.toEqual([errorRequired])
|
|
1121
|
-
await expect(usernum.validate({ amount: null }, { update: true }))
|
|
1122
|
-
.rejects.toEqual([{ ...errorRequired, title: 'amount' }])
|
|
1123
|
-
await expect(user.validate({ name: null }, { update: true, validateUndefined: true }))
|
|
1124
|
-
.rejects.toEqual([errorRequired])
|
|
1125
|
-
|
|
1126
|
-
// Skip required error
|
|
1127
|
-
await expect(user.validate({ name: undefined }, { validateUndefined: false })).resolves.toEqual({})
|
|
1128
|
-
await expect(user.validate({}, { validateUndefined: false })).resolves.toEqual({})
|
|
1129
|
-
await expect(user.validate({}, { update: true })).resolves.toEqual({})
|
|
1130
|
-
await expect(user.validate({}, { update: true, validateUndefined: false })).resolves.toEqual({})
|
|
1131
|
-
await expect(userdeep.validate({}, { update: true })).resolves.toEqual({})
|
|
1132
|
-
await expect(userdeep.validate({ name: {} }, { update: true, validateUndefined: false }))
|
|
1133
|
-
.resolves.toEqual({ name: {} })
|
|
1134
|
-
await expect(userdeep.validate({ names: [{}] }, { update: true, validateUndefined: false }))
|
|
1135
|
-
.resolves.toEqual({ names: [{}] })
|
|
1114
|
+
let userDoc = await user.insert({ data: { first: 'Martin', last: 'Luther' }})
|
|
1115
|
+
|
|
1116
|
+
// Catch validate (a)synchronous errors thrown in function or through `next(err)`
|
|
1117
|
+
await expect(user.validate({ first: '' })).rejects.toThrow('beforeValidate error 1..')
|
|
1118
|
+
await expect(user.validate({ first: 'Martin' })).rejects.toThrow('beforeValidate error 2..')
|
|
1119
|
+
await expect(user.validate({ first: 'Martin', last: 'Luther' })).resolves.toEqual({
|
|
1120
|
+
first: 'Martin',
|
|
1121
|
+
last: 'Luther',
|
|
1136
1122
|
})
|
|
1137
1123
|
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
if (!data.first) {
|
|
1147
|
-
next(new Error('beforeValidate error 1..'))
|
|
1148
|
-
} else if (!data.last) {
|
|
1149
|
-
setTimeout(function() {
|
|
1150
|
-
next(new Error('beforeValidate error 2..'))
|
|
1151
|
-
}, 100)
|
|
1152
|
-
} else {
|
|
1153
|
-
next()
|
|
1154
|
-
}
|
|
1155
|
-
}],
|
|
1156
|
-
})
|
|
1157
|
-
let userDoc = await user.insert({ data: { first: 'Martin', last: 'Luther' }})
|
|
1158
|
-
|
|
1159
|
-
// Catch validate (a)synchronous errors thrown in function or through `next(err)`
|
|
1160
|
-
await expect(user.validate({ first: '' })).rejects.toThrow('beforeValidate error 1..')
|
|
1161
|
-
await expect(user.validate({ first: 'Martin' })).rejects.toThrow('beforeValidate error 2..')
|
|
1162
|
-
await expect(user.validate({ first: 'Martin', last: 'Luther' })).resolves.toEqual({
|
|
1163
|
-
first: 'Martin',
|
|
1164
|
-
last: 'Luther'
|
|
1165
|
-
})
|
|
1124
|
+
// Catch insert (a)synchronous errors thrown in function or through `next(err)`
|
|
1125
|
+
await expect(user.insert({ data: { first: '' } })).rejects.toThrow('beforeValidate error 1..')
|
|
1126
|
+
await expect(user.insert({ data: { first: 'Martin' } })).rejects.toThrow('beforeValidate error 2..')
|
|
1127
|
+
await expect(user.insert({ data: { first: 'Martin', last: 'Luther' } })).resolves.toEqual({
|
|
1128
|
+
_id: expect.any(Object),
|
|
1129
|
+
first: 'Martin',
|
|
1130
|
+
last: 'Luther',
|
|
1131
|
+
})
|
|
1166
1132
|
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1133
|
+
// Catch update (a)synchronous errors thrown in function or through `next(err)`
|
|
1134
|
+
await expect(user.update({ query: userDoc._id, data: { first: '' } }))
|
|
1135
|
+
.rejects.toThrow('beforeValidate error 1..')
|
|
1136
|
+
await expect(user.update({ query: userDoc._id, data: { first: 'Martin' } }))
|
|
1137
|
+
.rejects.toThrow('beforeValidate error 2..')
|
|
1138
|
+
await expect(user.update({ query: userDoc._id, data: { first: 'Martin', last: 'Luther' } }))
|
|
1139
|
+
.resolves.toEqual({
|
|
1172
1140
|
first: 'Martin',
|
|
1173
|
-
last: 'Luther'
|
|
1141
|
+
last: 'Luther',
|
|
1174
1142
|
})
|
|
1175
|
-
|
|
1176
|
-
// Catch update (a)synchronous errors thrown in function or through `next(err)`
|
|
1177
|
-
await expect(user.update({ query: userDoc._id, data: { first: '' } }))
|
|
1178
|
-
.rejects.toThrow('beforeValidate error 1..')
|
|
1179
|
-
await expect(user.update({ query: userDoc._id, data: { first: 'Martin' } }))
|
|
1180
|
-
.rejects.toThrow('beforeValidate error 2..')
|
|
1181
|
-
await expect(user.update({ query: userDoc._id, data: { first: 'Martin', last: 'Luther' } }))
|
|
1182
|
-
.resolves.toEqual({
|
|
1183
|
-
first: 'Martin',
|
|
1184
|
-
last: 'Luther'
|
|
1185
|
-
})
|
|
1186
|
-
|
|
1187
|
-
db.close()
|
|
1188
|
-
})
|
|
1189
|
-
|
|
1190
|
-
}
|
|
1143
|
+
})
|