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/test/validate.js CHANGED
@@ -1,1190 +1,1143 @@
1
1
  // Todo: split out basic 'type' tests
2
- let validate = require('../lib/model-validate.js')
3
-
4
- module.exports = function(monastery, opendb) {
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
- test('validation basic errors', async () => {
7
- // Setup
8
- let db = (await opendb(false)).db
9
- let user = db.model('user', { fields: {
10
- date: { type: 'date' },
11
- name: { type: 'string', required: true },
12
- colors: [{ type: 'string' }],
13
- animals: { dog: { type: 'string' }}
14
- }})
15
-
16
- // Required error (insert)
17
- await expect(user.validate({})).rejects.toContainEqual({
18
- status: '400',
19
- title: 'name',
20
- detail: 'This field is required.',
21
- meta: { rule: 'required', model: 'user', field: 'name' }
22
- })
23
- await expect(user.validate({ name : '' })).rejects.toContainEqual({
24
- status: '400',
25
- title: 'name',
26
- detail: 'This field is required.',
27
- meta: { rule: 'required', model: 'user', field: 'name' }
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
- // Type error (date)
57
- let userdate = db.model('userdate', { fields: { amount: { type: 'date', required: true }}})
58
- let userdate2 = db.model('userdate2', { fields: { amount: { type: 'date' }}})
59
- await expect(userdate.validate({ amount: 0 })).resolves.toEqual({ amount: 0 })
60
- await expect(userdate.validate({ amount: '0' })).resolves.toEqual({ amount: 0 })
61
- await expect(userdate.validate({ amount: '1646778655000' })).resolves.toEqual({ amount: 1646778655000 })
62
- await expect(userdate2.validate({ amount: '' })).resolves.toEqual({ amount: null })
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
- // Type error (array)
132
- await expect(user.validate({ name: 'a', colors: null })).rejects.toContainEqual({
133
- status: '400',
134
- title: 'colors',
135
- detail: 'Value was not an array.',
136
- meta: { rule: 'isArray', model: 'user', field: 'colors' }
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
- // Type error (object)
140
- await expect(user.validate({ name: 'a', animals: [] })).rejects.toContainEqual({
141
- status: '400',
142
- title: 'animals',
143
- detail: 'Value was not an object.',
144
- meta: { rule: 'isObject', model: 'user', field: 'animals' }
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
- // Type error (object)
148
- await expect(user.validate({ name: 'a', animals: null })).rejects.toContainEqual({
149
- status: '400',
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
- test('validation subdocument errors', async () => {
157
- // Setup
158
- let db = (await opendb(false)).db
159
- let user = db.model('user', { fields: {
160
- animals: {
161
- cat: { type: 'string', required: true }, // {} = required on insert
162
- dog: {
163
- name: { type: 'string' },
164
- color: { type: 'string', required: true } // {} = required on insert
165
- }
166
- }
167
- }})
168
-
169
- // Insert: Required subdocument properties
170
- await expect(user.validate({})).rejects.toEqual(
171
- expect.arrayContaining([
172
- expect.objectContaining({
173
- status: '400',
174
- title: 'animals.cat',
175
- detail: 'This field is required.',
176
- meta: { rule: 'required', model: 'user', field: 'cat' }
177
- }),
178
- expect.objectContaining({
179
- status: '400',
180
- title: 'animals.dog.color',
181
- detail: 'This field is required.',
182
- meta: { rule: 'required', model: 'user', field: 'color' }
183
- }),
184
- ])
185
- )
186
-
187
- // Insert: Required subdocument properties
188
- await expect(user.validate({ animals: {} })).rejects.toEqual(
189
- expect.arrayContaining([
190
- expect.objectContaining({
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
- // Update: Required subdocument property when a parent/grandparent is specified
229
- await expect(user.validate({ animals: {} }, { update: true })).rejects.toEqual(
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
- test('validation array errors', async () => {
284
- // Setup
285
- let db = (await opendb(false)).db
286
- let user = db.model('user', { fields: {
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
- // Type error within an array subdocument (string)
307
- let error = {
308
- status: '400',
309
- title: 'animals.dogs.0.color',
310
- detail: 'This field is required.',
311
- meta: { rule: 'required', model: 'user', field: 'color' }
312
- }
313
- await expect(user.validate({ animals: { dogs: [{ name: 'sparky', color: false }] }}))
314
- .rejects.toContainEqual({
315
- ...error,
316
- detail: 'Value was not a string.',
317
- meta: { rule: 'isString', model: 'user', field: 'color' }
318
- })
319
-
320
- // Requried error within an array subdocument
321
- await expect(user.validate({ animals: { dogs: [{ name: 'sparky' }] }}))
322
- .rejects.toContainEqual(error)
323
-
324
- // Requried error within an array subdocument (even during update when parent defined)
325
- await expect(user.validate({ animals: { dogs: [{ name: 'sparky' }] }}, { update: true }))
326
- .rejects.toContainEqual(error)
327
-
328
- // No item errors for empty arrays
329
- await expect(user.validate({ animals: { dogs: [] }}))
330
- .resolves.toEqual({ animals: { dogs: [] }})
331
-
332
- // No undefined item errors with validateUndefined=false
333
- await expect(user.validate(
334
- { animals: { dogs: [{ name: 'sparky' }] }},
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
- test('validation array schema errors', async () => {
348
- // Setup
349
- let db = (await opendb(false)).db
350
- function arrayWithSchema(array, schema) {
351
- array.schema = schema
352
- return array
353
- }
354
- let user = db.model('user', { fields: {
355
- animals: arrayWithSchema(
356
- [{ type: 'string' }],
357
- { required: true, minLength: 2 },
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
- // MinLength error
372
- await expect(user.validate({
373
- animals: ['dog'],
374
- })).rejects.toContainEqual({
375
- status: '400',
376
- title: 'animals',
377
- detail: 'Value needs to contain a minimum of 2 items.',
378
- meta: { rule: 'minLength', model: 'user', field: 'animals' }
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
- test('validation getMostSpecificKeyMatchingPath', async () => {
383
- let db = (await opendb(false)).db
384
- let user = db.model('user', {
385
- fields: {
386
- name: { type: 'string' },
387
- },
388
- messages: {
389
- // these are sorted when trhe model's initialised
390
- 'cats.name': {},
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
- let fn = validate._getMostSpecificKeyMatchingPath
408
- // subdocument
409
- expect(fn(user.messages, 'cats.name')).toEqual('cats.name')
410
- // array subdocuments
411
- // expect(fn(user.messages, 'cats.1.name')).toEqual('cats.name') // no longer matches
412
- expect(fn(user.messages, 'dogs.1.name')).toEqual('dogs.$.name')
413
- expect(fn(user.messages, 'dogs.2.name')).toEqual('dogs.$.name')
414
- expect(fn(user.messages, 'pigs.1.name')).toEqual('pigs.1.name')
415
- expect(fn(user.messages, 'pigs.2.name')).toEqual('pigs.2.name')
416
- expect(fn(user.messages, 'pigs.3.name')).toEqual('pigs.$.name')
417
- // array
418
- expect(fn(user.messages, 'gulls.1.2')).toEqual('gulls.$.$')
419
- expect(fn(user.messages, 'gulls.1')).toEqual('gulls.$')
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
- test('validation default messages', async () => {
423
- // Setup
424
- let db = (await opendb(false)).db
425
- let user = db.model('user', {
426
- fields: {
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
- dog: { name: { type: 'string', minLength: 4 }},
429
- dogNames: [{ type: 'string', minLength: 4 }],
430
- animals: [{
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
- // subdocument in an array error
462
- await expect(user.validate({ animals: [{ name: 'ben' }] })).rejects.toContainEqual({
463
- ...mock,
464
- title: 'animals.0.name'
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
- // subdocument in an array error (different index)
468
- await expect(user.validate({ animals: [{ name: 'carla' }, { name: 'ben' }] })).rejects.toContainEqual({
469
- ...mock,
470
- title: 'animals.1.name'
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
- test('validation custom messages', async () => {
475
- // Setup
476
- // Todo: Setup testing for array array subdocument field messages
477
- let db = (await opendb(false)).db
478
- // let arrayWithSchema = (array, schema) => { array.schema = schema; return array }
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
- let mock = {
493
- status: '400',
494
- title: 'name',
495
- detail: 'Oops min length is 4',
496
- meta: { rule: 'minLength', model: 'user', field: 'name' }
497
- }
498
-
499
- // basic error
500
- await expect(user.validate({ name: 'ben' })).rejects.toContainEqual(
501
- mock
502
- )
503
- // subdocument error
504
- await expect(user.validate({ dog: { name: 'ben' } })).rejects.toContainEqual({
505
- ...mock,
506
- title: 'dog.name'
507
- })
508
- // array error
509
- await expect(user.validate({ dogNames: ['ben'] })).rejects.toContainEqual({
510
- ...mock,
511
- title: 'dogNames.0',
512
- meta: { ...mock.meta, field: '0' }
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
- test('validation custom messages for arrays', async () => {
517
- // Setup
518
- // Todo: Setup testing for array array subdocument field messages
519
- let db = (await opendb(false)).db
520
- let arrayWithSchema = (array, schema) => { array.schema = schema; return array }
521
- let user = db.model('user', {
522
- fields: {
523
- dogNames: arrayWithSchema([
524
- arrayWithSchema([{ type: 'string' }], { minLength: 1 })
525
- ], { minLength: 1 }),
526
- catNames: [{
527
- name: { type: 'string', minLength: 4 }
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
- pigNames: [
530
- [{
531
- name: { type: 'string', minLength: 4 },
532
- }]
533
- ],
534
- },
535
- messages: {
536
- 'dogNames': { minLength: 'add one dog name' },
537
- 'dogNames.$': { minLength: 'add one sub dog name' },
538
-
539
- 'catNames.$.name': { minLength: 'min length error (name)' },
540
- 'catNames.1.name': { minLength: 'min length error (1)' },
541
- 'catNames.2.name': { minLength: 'min length error (2)' },
542
-
543
- // 'pigNames.$.$.name': { minLength: 'min length error (name)' },
544
- 'pigNames.$.$.name': { minLength: 'min length error ($ $)' }, // catches
545
- 'pigNames.$.1.name': { minLength: 'min length error ($ 1)' },
546
- 'pigNames.2.$.name': { minLength: 'min length error (2 $)' },
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
- // Empty array
553
- await expect(user.validate({ dogNames: [] })).rejects.toContainEqual({
554
- status: '400',
555
- title: 'dogNames',
556
- detail: 'add one dog name',
557
- meta: { rule: 'minLength', model: 'user', field: 'dogNames' }
558
- })
559
- // Empty sub array
560
- await expect(user.validate({ dogNames: [['carla']] })).resolves.toEqual({ dogNames: [['carla']] })
561
- await expect(user.validate({ dogNames: [[]] })).rejects.toContainEqual({
562
- status: '400',
563
- title: 'dogNames.0',
564
- detail: 'add one sub dog name',
565
- meta: { rule: 'minLength', model: 'user', field: '0' }
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
- // array-subdocument-field error (loose match)
570
- await expect(user.validate({ catNames: [{ name: 'ben' }] })).rejects.toContainEqual({
571
- status: '400',
572
- title: 'catNames.0.name',
573
- detail: 'min length error (name)',
574
- meta: { rule: 'minLength', model: 'user', field: 'name' }
575
- })
576
- // array-subdocument-1-field error
577
- await expect(user.validate({ catNames: [{ name: 'carla' }, { name: 'ben' }] }))
578
- .rejects.toContainEqual({
579
- status: '400',
580
- title: 'catNames.1.name',
581
- detail: 'min length error (1)',
582
- meta: { rule: 'minLength', model: 'user', field: 'name' }
583
- })
584
- // array-subdocument-2-field error
585
- await expect(user.validate({ catNames: [{ name: 'carla' }, { name: 'carla' }, { name: 'ben' }] }))
586
- .rejects.toContainEqual({
587
- status: '400',
588
- title: 'catNames.2.name',
589
- detail: 'min length error (2)',
590
- meta: { rule: 'minLength', model: 'user', field: 'name' }
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
- // array-subdocument-field error (loose $ match)
595
- await expect(user.validate({ pigNames: [[{ name: 'ben' }]] }))
596
- .rejects.toContainEqual({
597
- status: '400',
598
- title: 'pigNames.0.0.name',
599
- detail: 'min length error ($ $)',
600
- meta: { rule: 'minLength', model: 'user', field: 'name' }
601
- })
602
- // array-subdocument-1-field error
603
- await expect(user.validate({ pigNames: [[{ name: 'carla' }, { name: 'ben' }]] }))
604
- .rejects.toContainEqual({
605
- status: '400',
606
- title: 'pigNames.0.1.name',
607
- detail: 'min length error ($ 1)',
608
- meta: { rule: 'minLength', model: 'user', field: 'name' }
609
- })
610
- // array-subdocument-2-0-field error (lower fallback)
611
- await expect(user.validate({ pigNames: [[],[],[{ name: 'ben' }]] })).rejects.toContainEqual({
612
- status: '400',
613
- title: 'pigNames.2.0.name',
614
- detail: 'min length error (2 $)',
615
- meta: { rule: 'minLength', model: 'user', field: 'name' }
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
- test('validation custom rules', async () => {
636
- // Setup
637
- let db = (await opendb(false)).db
638
- let user = db.model('user', {
639
- fields: {
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
- animals: [{
642
- name: { type: 'string', bigName: 8 }
643
- }],
629
+ }],
630
+ },
631
+ rules: {
632
+ bigName: function(value, ruleArg) {
633
+ return value.length >= ruleArg
644
634
  },
645
- rules: {
646
- bigName: function(value, ruleArg) {
647
- return value.length >= ruleArg
648
- }
649
- }
650
- })
651
- let user2 = db.model('user2', {
652
- fields: {
653
- name: { type: 'string' },
654
- nickname: { type: 'string', requiredIfNoName: true },
655
- age: { type: 'number', required: true },
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
- rules: {
658
- requiredIfNoName: {
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
- // subdocument in an array
677
- await expect(user.validate({ animals: [{ name: 'benjamin' }] })).resolves.toEqual({
678
- animals: [{ name: 'benjamin' }]
679
- })
680
- await expect(user.validate({ animals: [{ name: 'ben' }] })).rejects.toContainEqual({
681
- status: '400',
682
- title: 'animals.0.name',
683
- detail: 'Invalid data property for rule "bigName".',
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
- // Required rule based off another field (create)
688
- await expect(user2.validate({ name: 'benjamin', age: 12 })).resolves.toEqual({
689
- name: 'benjamin',
690
- age: 12
691
- })
692
- await expect(user2.validate({ nickname: 'benny', age: 12 })).resolves.toEqual({
693
- nickname: 'benny',
694
- age: 12
695
- })
696
- await expect(user2.validate({ })).rejects.toEqual([
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
- // Required rule based off another field (update)
712
- await expect(user2.validate({ }, { update: true })).resolves.toEqual({})
713
- await expect(user2.validate({ nickname: '' }, { update: true })).rejects.toEqual([{
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
- test('validated data', async () => {
722
- // Setup
723
- let db = (await opendb(false)).db
724
- let fields = {
725
- name: { type: 'string' },
726
- names: [{ type: 'string' }],
727
- animals: {
728
- dog: { type: 'string' },
729
- dogs: [{ name: { type: 'string' } }]
730
- }
731
- }
732
- fields.names.schema = { nullObject: true }
733
- fields.animals.schema = { nullObject: true }
734
- let user = db.model('user', { fields: fields })
735
-
736
- // No data
737
- await expect(user.validate({})).resolves.toEqual({})
738
-
739
- // Ignores invalid data
740
- await expect(user.validate({ badprop: true, schema: {} })).resolves.toEqual({})
741
-
742
- // String data
743
- await expect(user.validate({ name: 'Martin Luther' })).resolves.toEqual({ name: 'Martin Luther' })
744
- await expect(user.validate({ name: null })).resolves.toEqual({ name: null })
745
-
746
- // Array data
747
- await expect(user.validate({ names: ['blue'] })).resolves.toEqual({ names: ['blue'] })
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
- test('schema options', async () => {
785
- // Setup
786
- let db = (await opendb(false)).db
787
- let user = db.model('user', { fields: {
788
- name: { type: 'string', 'insertOnly': true }
789
- }})
790
- let user2 = db.model('user2', { fields: {
791
- name: { type: 'string', defaultOverride: true, default: 'Martin Luther' }
792
- }})
793
- let user3 = db.model('user3', { fields: {}})
794
- let user4 = db.model('user4', { fields: {
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
- test('schema options default', async () => {
826
- // Setup
827
- // todo: test objects
828
- // todo: change test name
829
- let db = (await opendb(false)).db
830
- let user = db.model('user', { fields: {
831
- name: { type: 'string', minLength: 7 },
832
- email: { type: 'string', isEmail: true },
833
- amount: { type: 'number' },
834
- }})
835
- let user2 = db.model('user', { fields: {
836
- amount: { type: 'number', required: true },
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
- test('schema options objects', async () => {
912
- let db = (await opendb(null, {
913
- timestamps: false,
914
- nullObjects: true,
915
- serverSelectionTimeoutMS: 2000
916
- })).db
917
- let user = db.model('user', {
918
- fields: {
919
- location: {
920
- lat: { type: 'number' },
921
- lng: { type: 'number' },
922
- schema: { required: true },
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
- test('validate defaultObjects', async () => {
942
- let db = (await opendb(null, {
943
- timestamps: false,
944
- defaultObjects: true,
945
- serverSelectionTimeoutMS: 2000
946
- })).db
947
-
948
- // let base = { names: [], animals: { dogs: [] }}
949
- let user = db.model('user', { fields: {
950
- name: { type: 'string' },
951
- names: [{ type: 'string' }],
952
- animals: {
953
- dog: { type: 'string' },
954
- dogs: [{ name: { type: 'string' } }]
955
- }
956
- }})
957
-
958
- // Array/subdocument defaults
959
- await expect(user.validate({})).resolves.toEqual({
960
- names: [],
961
- animals: { dogs: [] }
962
- })
963
-
964
- db.close()
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
- test('validate nullObjects', async () => {
968
- let db = (await opendb(null, {
969
- timestamps: false,
970
- nullObjects: true,
971
- serverSelectionTimeoutMS: 2000
972
- })).db
973
- let user = db.model('user', { fields: {
974
- names: [{ type: 'string' }],
975
- animals: {
976
- dog: { type: 'string' },
977
- dogs: [{ name: { type: 'string' } }]
978
- }
979
- }})
980
-
981
- // Subdocument data (null/string)
982
- await expect(user.validate({ animals: 'notAnObject' })).rejects.toEqual([{
983
- 'detail': 'Value was not an object.',
984
- 'meta': {'detailLong': undefined, 'field': 'animals', 'model': 'user', 'rule': 'isObject'},
985
- 'status': '400',
986
- 'title': 'animals'
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
- test('validation option skipValidation', async () => {
994
- let db = (await opendb(false)).db
995
- let user = db.model('user', { fields: {
996
- name: { type: 'string', required: true }
997
- }})
998
- let user2 = db.model('user2', { fields: {
999
- my: {
1000
- name: {
1001
- is: { type: 'string', required: true }
1002
- }
1003
- }
1004
- }})
1005
- let user3 = db.model('user3', { fields: {
1006
- people: [{
1007
- name: { type: 'string', required: true }
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
- people2: [{
1010
- people3: [{
1011
- name: { type: 'string', required: true }
1012
- }]
1013
- }]
1014
- }})
1015
- let user4 = db.model('user4', { fields: {
1016
- code: { type: 'string', required: true },
1017
- address: {
1018
- city: { type: 'string', required: true },
1019
- country: { type: 'string', required: true }
1020
- }
1021
- }})
1022
-
1023
- // Skip validation on the required fields
1024
- await expect(user.validate({}, { skipValidation: true })).resolves.toEqual({})
1025
- await expect(user.validate({}, { skipValidation: ['name'] })).resolves.toEqual({})
1026
- await expect(user2.validate({}, { skipValidation: ['my.name'] })).resolves.toEqual({})
1027
- await expect(user3.validate({ people: [{}] }, { skipValidation: ['people.name'] }))
1028
- .resolves.toEqual({ people: [{}] })
1029
-
1030
- // Skip all array items
1031
- await expect(user3.validate(
1032
- { people2: [{ people3: [{}, {}] }] },
1033
- { skipValidation: ['people2.$.people3.$.name'] }
1034
- )).resolves.toEqual({ people2: [{ people3: [{}, {}] }] })
1035
-
1036
- // Skip all array items (array expanding)
1037
- await expect(user3.validate(
1038
- { people2: [{ people3: [{}, {}] }] },
1039
- { skipValidation: ['people2.people3.name'] }
1040
- )).resolves.toEqual({ people2: [{ people3: [{}, {}] }] })
1041
-
1042
- // Skip a certain array index
1043
- await expect(user3.validate(
1044
- { people2: [{ people3: [{}, {}] }] },
1045
- { skipValidation: ['people2.$.people3.0.name'] }
1046
- )).rejects.toContainEqual({
1047
- detail: 'This field is required.',
1048
- status: '400',
1049
- title: 'people2.0.people3.1.name',
1050
- meta: {
1051
- field: 'name',
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
- // Skip multiple fields
1058
- await expect(user4.validate(
1059
- { address: { city: 'christchurch', country: 'ewf' }},
1060
- { skipValidation: ['code'] }
1061
- )).resolves.toEqual({
1062
- address: { city: 'christchurch', country: 'ewf' }
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
- // Non existing validation field entries
1066
- await expect(user3.validate({ people: [{}] }, { skipValidation: ['people.badField'] })).rejects.toContainEqual({
1067
- detail: 'This field is required.',
1068
- status: '400',
1069
- title: 'people.0.name',
1070
- meta: {
1071
- model: 'user3',
1072
- field: 'name',
1073
- rule: 'required'
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
- test('validation option validateUndefined', async () => {
1079
- // ValidateUndefined runs required rules on all fields, `true` for insert, `false` for update.
1080
-
1081
- // Setup
1082
- let db = (await opendb(false)).db
1083
- let user = db.model('user', { fields: {
1084
- date: { type: 'number' },
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
- test('validation hooks', async () => {
1139
- let db = (await opendb(null)).db
1140
- let user = db.model('user', {
1141
- fields: {
1142
- first: { type: 'string'},
1143
- last: { type: 'string'}
1144
- },
1145
- beforeValidate: [(data, next) => {
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
- // Catch insert (a)synchronous errors thrown in function or through `next(err)`
1168
- await expect(user.insert({ data: { first: '' } })).rejects.toThrow('beforeValidate error 1..')
1169
- await expect(user.insert({ data: { first: 'Martin' } })).rejects.toThrow('beforeValidate error 2..')
1170
- await expect(user.insert({ data: { first: 'Martin', last: 'Luther' } })).resolves.toEqual({
1171
- _id: expect.any(Object),
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
+ })