monastery 1.30.2 → 1.31.1
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/lib/model-crud.js +30 -15
- package/lib/model-validate.js +9 -6
- package/lib/model.js +2 -0
- package/lib/rules.js +48 -43
- package/package.json +4 -2
- package/test/blacklisting.js +4 -4
- package/test/crud.js +137 -91
- package/test/model.js +8 -8
- package/test/populate.js +3 -3
- package/test/util.js +2 -2
- package/test/validate.js +61 -42
- package/test/virtuals.js +2 -2
package/lib/model-crud.js
CHANGED
|
@@ -21,14 +21,14 @@ module.exports = {
|
|
|
21
21
|
opts = opts || {}
|
|
22
22
|
opts.insert = true
|
|
23
23
|
opts.model = this
|
|
24
|
-
let
|
|
24
|
+
let req = opts.req
|
|
25
|
+
let data = opts.data = util.isDefined(opts.data)? opts.data : util.isDefined((req||{}).body) ? req.body : {}
|
|
25
26
|
let options = util.omit(opts, [
|
|
26
27
|
'data', 'insert', 'model', 'respond', 'validateUndefined', 'skipValidation', 'blacklist'
|
|
27
28
|
])
|
|
28
29
|
if (cb && !util.isFunction(cb)) {
|
|
29
30
|
throw new Error(`The callback passed to ${this.name}.insert() is not a function`)
|
|
30
31
|
}
|
|
31
|
-
|
|
32
32
|
return util.parseData(data).then(data => {
|
|
33
33
|
opts.data = data
|
|
34
34
|
return this.validate(data, { ...opts })
|
|
@@ -124,12 +124,16 @@ module.exports = {
|
|
|
124
124
|
} else {
|
|
125
125
|
let modelName = (path.split('.').reduce((o,i) => o[i], this.fields) ||{}).model
|
|
126
126
|
if (!modelName) {
|
|
127
|
-
this.error(
|
|
128
|
-
|
|
127
|
+
this.error(
|
|
128
|
+
`The field "${path}" passed to populate is not of type model. You would ` +
|
|
129
|
+
'need to add the field option e.g. { model: \'comment\' } in your schema.'
|
|
130
|
+
)
|
|
129
131
|
continue
|
|
130
132
|
} else if (!this.manager.model[modelName]) {
|
|
131
|
-
this.error(
|
|
132
|
-
|
|
133
|
+
this.error(
|
|
134
|
+
`The field's model defined in your schema does not exist: ${path}: ` +
|
|
135
|
+
`{ model: "${modelName}" }`
|
|
136
|
+
)
|
|
133
137
|
continue
|
|
134
138
|
}
|
|
135
139
|
// Populate model (convert array into document & create lookup)
|
|
@@ -212,7 +216,8 @@ module.exports = {
|
|
|
212
216
|
opts = this._queryObject(opts)
|
|
213
217
|
opts.update = true
|
|
214
218
|
opts.model = this
|
|
215
|
-
|
|
219
|
+
let req = opts.req
|
|
220
|
+
data = opts.data = util.isDefined(opts.data)? opts.data : util.isDefined((req||{}).body) ? req.body : undefined
|
|
216
221
|
operators = util.pluck(opts, [/^\$/])
|
|
217
222
|
// Operation options
|
|
218
223
|
options = util.omit(opts, ['data', 'query', 'respond', 'validateUndefined', 'skipValidation', 'blacklist'])
|
|
@@ -224,23 +229,32 @@ module.exports = {
|
|
|
224
229
|
let order = (options.sort.match(/:(-?[0-9])/) || [])[1]
|
|
225
230
|
options.sort = { [name]: parseInt(order || 1) }
|
|
226
231
|
}
|
|
227
|
-
if (operators['$set'] && data) {
|
|
228
|
-
throw new Error(`Please only pass options.$set or options.data to ${this.name}.update()`)
|
|
229
|
-
}
|
|
230
232
|
util.parseData(data).then(d => resolve(d))
|
|
231
233
|
|
|
232
234
|
}).then(data => {
|
|
233
235
|
opts.data = data
|
|
234
|
-
if (util.
|
|
236
|
+
if (util.isDefined(data)) return this.validate(data, { ...opts })
|
|
237
|
+
else return Promise.resolve(data)
|
|
235
238
|
|
|
236
239
|
}).then(data => {
|
|
237
|
-
if (util.
|
|
238
|
-
throw new Error(`No valid data passed to ${this.name}.update()`)
|
|
240
|
+
if (util.isDefined(data) && (!data || util.isEmpty(data))) {
|
|
241
|
+
throw new Error(`No valid data passed to ${this.name}.update({ data: .. })`)
|
|
242
|
+
}
|
|
243
|
+
if (!util.isDefined(data) && util.isEmpty(operators)) {
|
|
244
|
+
throw new Error(`Please pass an update operator to ${this.name}.update(), e.g. data, $unset, etc`)
|
|
239
245
|
}
|
|
240
246
|
return util.runSeries(this.beforeUpdate.map(f => f.bind(opts, data||{}))).then(() => data)
|
|
241
247
|
|
|
242
248
|
}).then(data => {
|
|
243
|
-
if (data
|
|
249
|
+
if (data && operators['$set']) {
|
|
250
|
+
this.warn(`'$set' fields take precedence over the data fields for \`${this.name}.update()\``)
|
|
251
|
+
}
|
|
252
|
+
if (data || operators['$set']) {
|
|
253
|
+
operators['$set'] = {
|
|
254
|
+
...data,
|
|
255
|
+
...(operators['$set'] || {}),
|
|
256
|
+
}
|
|
257
|
+
}
|
|
244
258
|
return this._update(opts.query, operators, options).then(output => {
|
|
245
259
|
if (!output.n) return null
|
|
246
260
|
let response = Object.assign(Object.create({ _output: output }), operators['$set']||{})
|
|
@@ -377,7 +391,8 @@ module.exports = {
|
|
|
377
391
|
if (!this._pathInProjection(pathWithoutArrays, projection, true)) return
|
|
378
392
|
// Ignore default
|
|
379
393
|
let value = util.isFunction(schema.default)? schema.default(this.manager) : schema.default
|
|
380
|
-
|
|
394
|
+
// console.log(item.dataRef)
|
|
395
|
+
util.setDeepValue(item.dataRef, path.replace(/\.0(\.|$)/g, '.$$$1'), value, true, false, true)
|
|
381
396
|
})
|
|
382
397
|
}
|
|
383
398
|
// Collect all of the model's afterFind hooks
|
package/lib/model-validate.js
CHANGED
|
@@ -140,7 +140,7 @@ module.exports = {
|
|
|
140
140
|
let value = util.isArray(fields)? data : (data||{})[fieldName]
|
|
141
141
|
let indexOrFieldName = util.isArray(fields)? i : fieldName
|
|
142
142
|
let path2 = `${path}.${indexOrFieldName}`.replace(/^\./, '')
|
|
143
|
-
let path3 = path2.replace(/(^|\.)[0-9]+(\.|$)/, '$2') // no
|
|
143
|
+
let path3 = path2.replace(/(^|\.)[0-9]+(\.|$)/, '$2') // no numerical keys, e.g. pets.1.name
|
|
144
144
|
let isType = 'is' + util.ucFirst(schema.type)
|
|
145
145
|
let isTypeRule = this.rules[isType] || rules[isType]
|
|
146
146
|
|
|
@@ -162,7 +162,7 @@ module.exports = {
|
|
|
162
162
|
if (opts.update && schema.insertOnly) return
|
|
163
163
|
// Ignore virtual fields
|
|
164
164
|
if (schema.virtual) return
|
|
165
|
-
// Type cast the value if tryParse is
|
|
165
|
+
// Type cast the value if tryParse is available, .e.g. isInteger.tryParse
|
|
166
166
|
if (isTypeRule && util.isFunction(isTypeRule.tryParse)) {
|
|
167
167
|
value = isTypeRule.tryParse.call(dataRoot, value, fieldName, this)
|
|
168
168
|
}
|
|
@@ -266,13 +266,16 @@ module.exports = {
|
|
|
266
266
|
if (!ruleMessage) ruleMessage = rule.message
|
|
267
267
|
|
|
268
268
|
// Ignore undefined (if updated root property, or ignoring)
|
|
269
|
-
if ((!validateUndefined || (opts.update && !path.match(/\./)))
|
|
269
|
+
if (typeof value === 'undefined' && (!validateUndefined || (opts.update && !path.match(/\./)))) return
|
|
270
270
|
|
|
271
|
-
// Ignore null (if nullObject is set on objects or arrays)
|
|
272
|
-
if (
|
|
271
|
+
// Ignore null (if nullObject is set on objects or arrays)
|
|
272
|
+
if (value === null && (field.isObject || field.isArray) && field.nullObject) return
|
|
273
|
+
|
|
274
|
+
// Ignore null
|
|
275
|
+
if (value === null && !(field.isObject || field.isArray) && !rule.validateNull) return
|
|
273
276
|
|
|
274
277
|
// Ignore empty strings
|
|
275
|
-
if (
|
|
278
|
+
if (value === '' && !rule.validateEmptyString) return
|
|
276
279
|
|
|
277
280
|
// Rule failed
|
|
278
281
|
if (!rule.fn.call(dataRoot, value, ruleArg, path, this)) return {
|
package/lib/model.js
CHANGED
|
@@ -36,6 +36,7 @@ let Model = module.exports = function(name, opts, manager) {
|
|
|
36
36
|
beforeValidate: opts.beforeValidate || [],
|
|
37
37
|
error: manager.error,
|
|
38
38
|
info: manager.info,
|
|
39
|
+
warn: manager.warn,
|
|
39
40
|
insertBL: opts.insertBL || [],
|
|
40
41
|
fields: { ...(util.deepCopy(opts.fields) || {}) },
|
|
41
42
|
findBL: opts.findBL || ['password'],
|
|
@@ -61,6 +62,7 @@ let Model = module.exports = function(name, opts, manager) {
|
|
|
61
62
|
// Update with formatted rule
|
|
62
63
|
let formattedRule = util.isObject(rule)? rule : { fn: rule }
|
|
63
64
|
if (!formattedRule.message) formattedRule.message = `Invalid data property for rule "${ruleName}".`
|
|
65
|
+
if (typeof formattedRule.validateNull == 'undefined') formattedRule.validateNull = true
|
|
64
66
|
if (typeof formattedRule.validateEmptyString == 'undefined') formattedRule.validateEmptyString = true
|
|
65
67
|
this.rules[ruleName] = formattedRule
|
|
66
68
|
}
|
package/lib/rules.js
CHANGED
|
@@ -5,7 +5,8 @@ let validator = require('validator')
|
|
|
5
5
|
module.exports = {
|
|
6
6
|
|
|
7
7
|
required: {
|
|
8
|
-
validateUndefined: true,
|
|
8
|
+
validateUndefined: true, // (false for custom rules)
|
|
9
|
+
validateNull: true,
|
|
9
10
|
validateEmptyString: true,
|
|
10
11
|
message: 'This field is required.',
|
|
11
12
|
fn: function(x) {
|
|
@@ -14,12 +15,13 @@ module.exports = {
|
|
|
14
15
|
}
|
|
15
16
|
},
|
|
16
17
|
|
|
17
|
-
// Type rules below ignore undefined
|
|
18
|
+
// "Type" rules below ignore undefined and null
|
|
18
19
|
|
|
19
|
-
|
|
20
|
+
isBoolean: {
|
|
20
21
|
validateEmptyString: true,
|
|
21
22
|
message: 'Value was not a boolean.',
|
|
22
23
|
tryParse: function(x) {
|
|
24
|
+
if (x === '') return null
|
|
23
25
|
if (typeof x === 'string' && x === 'true') return true
|
|
24
26
|
else if (typeof x === 'string' && x === 'false') return false
|
|
25
27
|
else return x
|
|
@@ -28,7 +30,7 @@ module.exports = {
|
|
|
28
30
|
return typeof x === 'boolean'
|
|
29
31
|
}
|
|
30
32
|
},
|
|
31
|
-
|
|
33
|
+
isArray: {
|
|
32
34
|
validateEmptyString: true,
|
|
33
35
|
message: 'Value was not an array.',
|
|
34
36
|
tryParse: function(x) {
|
|
@@ -41,10 +43,11 @@ module.exports = {
|
|
|
41
43
|
return Array.isArray(x)
|
|
42
44
|
}
|
|
43
45
|
},
|
|
44
|
-
|
|
46
|
+
isDate: {
|
|
45
47
|
validateEmptyString: true,
|
|
46
48
|
message: 'Value was not a unix timestamp.',
|
|
47
49
|
tryParse: function(x) {
|
|
50
|
+
if (x === '') return null
|
|
48
51
|
if (util.isString(x) && x.match(/^[+-]?[0-9]+$/)) return x // keep string nums intact
|
|
49
52
|
return isNaN(parseInt(x))? x : parseInt(x)
|
|
50
53
|
},
|
|
@@ -53,7 +56,7 @@ module.exports = {
|
|
|
53
56
|
return typeof x === 'number' && (parseInt(x) === x)
|
|
54
57
|
}
|
|
55
58
|
},
|
|
56
|
-
|
|
59
|
+
isImageObject: {
|
|
57
60
|
validateEmptyString: true,
|
|
58
61
|
message: 'Invalid image value',
|
|
59
62
|
messageLong: 'Image fields need to either be null, undefined, file, or an object containing the following '
|
|
@@ -69,10 +72,11 @@ module.exports = {
|
|
|
69
72
|
if (isObject && x.bucket && x.date && x.filename && x.filesize && x.path && x.uid) return true
|
|
70
73
|
}
|
|
71
74
|
},
|
|
72
|
-
|
|
75
|
+
isInteger: {
|
|
73
76
|
validateEmptyString: true,
|
|
74
77
|
message: 'Value was not an integer.',
|
|
75
78
|
tryParse: function(x) {
|
|
79
|
+
if (x === '') return null
|
|
76
80
|
if (util.isString(x) && x.match(/^[+-][0-9]+$/)) return x // keep string nums intact
|
|
77
81
|
return isNaN(parseInt(x)) || (!x && x!==0) || x===true? x : parseInt(x)
|
|
78
82
|
},
|
|
@@ -81,10 +85,11 @@ module.exports = {
|
|
|
81
85
|
return typeof x === 'number' && (parseInt(x) === x)
|
|
82
86
|
}
|
|
83
87
|
},
|
|
84
|
-
|
|
88
|
+
isNumber: {
|
|
85
89
|
validateEmptyString: true,
|
|
86
90
|
message: 'Value was not a number.',
|
|
87
91
|
tryParse: function(x) {
|
|
92
|
+
if (x === '') return null
|
|
88
93
|
if (util.isString(x) && x.match(/^[+-][0-9]+$/)) return x // keep string nums intact
|
|
89
94
|
return isNaN(Number(x)) || (!x && x!==0) || x===true? x : Number(x)
|
|
90
95
|
},
|
|
@@ -93,7 +98,7 @@ module.exports = {
|
|
|
93
98
|
return typeof x === 'number'
|
|
94
99
|
}
|
|
95
100
|
},
|
|
96
|
-
|
|
101
|
+
isObject: {
|
|
97
102
|
validateEmptyString: true,
|
|
98
103
|
message: 'Value was not an object.',
|
|
99
104
|
tryParse: function(x) {
|
|
@@ -106,21 +111,25 @@ module.exports = {
|
|
|
106
111
|
return x !== null && typeof x === 'object' && !(x instanceof Array)
|
|
107
112
|
}
|
|
108
113
|
},
|
|
109
|
-
|
|
114
|
+
isString: {
|
|
110
115
|
validateEmptyString: true,
|
|
111
116
|
message: 'Value was not a string.',
|
|
117
|
+
tryParse: function(x) {
|
|
118
|
+
if (typeof x === 'number') return x + ''
|
|
119
|
+
else return x
|
|
120
|
+
},
|
|
112
121
|
fn: function(x) {
|
|
113
122
|
return typeof x === 'string'
|
|
114
123
|
}
|
|
115
124
|
},
|
|
116
|
-
|
|
125
|
+
isAny: {
|
|
117
126
|
validateEmptyString: true,
|
|
118
127
|
message: '',
|
|
119
128
|
fn: function(x) {
|
|
120
129
|
return true
|
|
121
130
|
}
|
|
122
131
|
},
|
|
123
|
-
|
|
132
|
+
isId: {
|
|
124
133
|
validateEmptyString: true,
|
|
125
134
|
message: 'Value was not a valid ObjectId.',
|
|
126
135
|
tryParse: function(x) {
|
|
@@ -135,34 +144,27 @@ module.exports = {
|
|
|
135
144
|
return util.isObject(x) && ObjectId.isValid(x)/*x.get_inc*/? true : false
|
|
136
145
|
}
|
|
137
146
|
},
|
|
138
|
-
|
|
139
|
-
|
|
147
|
+
|
|
148
|
+
/* "Number" rules below ignore undefined and null */
|
|
149
|
+
|
|
150
|
+
max: {
|
|
140
151
|
message: (x, arg) => 'Value was greater than the configured maximum (' + arg + ')',
|
|
141
152
|
fn: function(x, arg) {
|
|
142
153
|
if (typeof x !== 'number') { throw new Error ('Value was not a number.') }
|
|
143
154
|
return x <= arg
|
|
144
155
|
}
|
|
145
156
|
},
|
|
146
|
-
|
|
147
|
-
validateEmptyString: true,
|
|
157
|
+
min: {
|
|
148
158
|
message: (x, arg) => 'Value was less than the configured minimum (' + arg + ')',
|
|
149
159
|
fn: function(x, arg) {
|
|
150
160
|
if (typeof x !== 'number') { throw new Error ('Value was not a number.') }
|
|
151
161
|
return x >= arg
|
|
152
162
|
}
|
|
153
163
|
},
|
|
154
|
-
'isNotEmptyString': {
|
|
155
|
-
validateEmptyString: true,
|
|
156
|
-
message: 'Value was an empty string.',
|
|
157
|
-
fn: function(x) {
|
|
158
|
-
return x !== ''
|
|
159
|
-
}
|
|
160
|
-
},
|
|
161
164
|
|
|
162
|
-
|
|
163
|
-
// (e.g. an empty email field can be saved that isn't required)
|
|
165
|
+
/* "String" rules below ignore undefined, null, and empty strings */
|
|
164
166
|
|
|
165
|
-
|
|
167
|
+
enum: {
|
|
166
168
|
message: (x, arg) => 'Invalid enum value',
|
|
167
169
|
fn: function(x, arg) {
|
|
168
170
|
for (let item of arg) {
|
|
@@ -170,51 +172,54 @@ module.exports = {
|
|
|
170
172
|
}
|
|
171
173
|
}
|
|
172
174
|
},
|
|
173
|
-
|
|
174
|
-
// message: (x, arg) => 'Please agree to the terms and conditions.',
|
|
175
|
-
// fn: function(x, arg) { return !x }
|
|
176
|
-
// },
|
|
177
|
-
'isAfter': {
|
|
175
|
+
isAfter: {
|
|
178
176
|
message: (x, arg) => 'Value was before the configured time (' + arg + ')',
|
|
179
177
|
fn: function(x, arg) { return validator.isAfter(x, arg) }
|
|
180
178
|
},
|
|
181
|
-
|
|
179
|
+
isBefore: {
|
|
182
180
|
message: (x, arg) => 'Value was after the configured time (' + arg + ')',
|
|
183
181
|
fn: function(x, arg) { return validator.isBefore(x, arg) }
|
|
184
182
|
},
|
|
185
|
-
|
|
183
|
+
isCreditCard: {
|
|
186
184
|
message: 'Value was not a valid credit card.',
|
|
187
185
|
fn: function(x, arg) { return validator.isCreditCard(x, arg) }
|
|
188
186
|
},
|
|
189
|
-
|
|
187
|
+
isEmail: {
|
|
190
188
|
message: 'Please enter a valid email address.',
|
|
191
189
|
fn: function(x, arg) { return validator.isEmail(x, arg) }
|
|
192
190
|
},
|
|
193
|
-
|
|
191
|
+
isHexColor: {
|
|
194
192
|
message: 'Value was not a valid hex color.',
|
|
195
193
|
fn: function(x, arg) { return validator.isHexColor(x, arg) }
|
|
196
194
|
},
|
|
197
|
-
|
|
195
|
+
isIn: {
|
|
198
196
|
message: (x, arg) => 'Value was not in the configured whitelist (' + arg.join(', ') + ')',
|
|
199
197
|
fn: function(x, arg) { return validator.isIn(x, arg) }
|
|
200
198
|
},
|
|
201
|
-
|
|
199
|
+
isIP: {
|
|
202
200
|
message: 'Value was not a valid IP address.',
|
|
203
201
|
fn: function(x, arg) { return validator.isIP(x, arg) }
|
|
204
202
|
},
|
|
205
|
-
|
|
203
|
+
isNotEmptyString: {
|
|
204
|
+
validateEmptyString: true,
|
|
205
|
+
message: 'Value was an empty string.',
|
|
206
|
+
fn: function(x) {
|
|
207
|
+
return x !== ''
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
isNotIn: {
|
|
206
211
|
message: (x, arg) => 'Value was in the configured blacklist (' + arg.join(', ') + ')',
|
|
207
212
|
fn: function(x, arg) { return !validator.isIn(x, arg) }
|
|
208
213
|
},
|
|
209
|
-
|
|
214
|
+
isURL: {
|
|
210
215
|
message: 'Value was not a valid URL.',
|
|
211
216
|
fn: function(x, arg) { return validator.isURL(x, arg === true? undefined : arg) }
|
|
212
217
|
},
|
|
213
|
-
|
|
218
|
+
isUUID: {
|
|
214
219
|
message: 'Value was not a valid UUID.',
|
|
215
220
|
fn: function(x, arg) { return validator.isUUID(x) }
|
|
216
221
|
},
|
|
217
|
-
|
|
222
|
+
minLength: {
|
|
218
223
|
message: function(x, arg) {
|
|
219
224
|
if (typeof x === 'string') return 'Value needs to be at least ' + arg + ' characters long.'
|
|
220
225
|
else return 'Value needs to contain a minimum of ' + arg + ' items.'
|
|
@@ -225,7 +230,7 @@ module.exports = {
|
|
|
225
230
|
else return x.length >= arg
|
|
226
231
|
}
|
|
227
232
|
},
|
|
228
|
-
|
|
233
|
+
maxLength: {
|
|
229
234
|
message: function(x, arg) {
|
|
230
235
|
if (typeof x === 'string') return 'Value was longer than the configured maximum length (' + arg + ')'
|
|
231
236
|
else return 'Value cannot contain more than ' + arg + ' items.'
|
|
@@ -236,7 +241,7 @@ module.exports = {
|
|
|
236
241
|
else return x.length <= arg
|
|
237
242
|
}
|
|
238
243
|
},
|
|
239
|
-
|
|
244
|
+
regex: {
|
|
240
245
|
message: (x, arg) => 'Value did not match the configured regular expression (' + arg + ')',
|
|
241
246
|
fn: function(x, arg) {
|
|
242
247
|
if (util.isRegex(arg)) return validator.matches(x, arg)
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "monastery",
|
|
3
3
|
"description": "⛪ A straight forward MongoDB ODM built around Monk",
|
|
4
4
|
"author": "Ricky Boyce",
|
|
5
|
-
"version": "1.
|
|
5
|
+
"version": "1.31.1",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": "github:boycce/monastery",
|
|
8
8
|
"homepage": "https://boycce.github.io/monastery/",
|
|
@@ -23,7 +23,8 @@
|
|
|
23
23
|
"test-one-example": "jest -t images",
|
|
24
24
|
"dev": "npm run lint & DEBUG=-monastery:info jest --watchAll --runInBand --verbose=false",
|
|
25
25
|
"lint": "eslint ./lib ./plugins ./test",
|
|
26
|
-
"docs": "cd docs && bundle exec jekyll serve --livereload --livereload-port 4001"
|
|
26
|
+
"docs": "cd docs && bundle exec jekyll serve --livereload --livereload-port 4001",
|
|
27
|
+
"mong": "nodemon resources/mong.js"
|
|
27
28
|
},
|
|
28
29
|
"dependencies": {
|
|
29
30
|
"aws-sdk": "2.1062.0",
|
|
@@ -39,6 +40,7 @@
|
|
|
39
40
|
"express": "4.17.1",
|
|
40
41
|
"express-fileupload": "1.2.0",
|
|
41
42
|
"jest": "27.4.7",
|
|
43
|
+
"nodemon": "2.0.15",
|
|
42
44
|
"standard-version": "9.3.2",
|
|
43
45
|
"supertest": "4.0.2"
|
|
44
46
|
},
|
package/test/blacklisting.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
module.exports = function(monastery, opendb) {
|
|
2
2
|
|
|
3
|
-
test('
|
|
3
|
+
test('find blacklisting', async () => {
|
|
4
4
|
// Setup
|
|
5
5
|
let db = (await opendb(null)).db
|
|
6
6
|
let bird = db.model('bird', {
|
|
@@ -152,7 +152,7 @@ module.exports = function(monastery, opendb) {
|
|
|
152
152
|
db.close()
|
|
153
153
|
})
|
|
154
154
|
|
|
155
|
-
test('
|
|
155
|
+
test('find blacklisting (default fields)', async () => {
|
|
156
156
|
// Setup
|
|
157
157
|
let db = (await opendb(null)).db
|
|
158
158
|
let user = db.model('user', {
|
|
@@ -223,7 +223,7 @@ module.exports = function(monastery, opendb) {
|
|
|
223
223
|
db.close()
|
|
224
224
|
})
|
|
225
225
|
|
|
226
|
-
test('
|
|
226
|
+
test('find blacklisting (populate)', async () => {
|
|
227
227
|
// Setup
|
|
228
228
|
let db = (await opendb(null)).db
|
|
229
229
|
let bird = db.model('bird', {
|
|
@@ -305,7 +305,7 @@ module.exports = function(monastery, opendb) {
|
|
|
305
305
|
db.close()
|
|
306
306
|
})
|
|
307
307
|
|
|
308
|
-
test('
|
|
308
|
+
test('insert update blacklisting (validate)', async () => {
|
|
309
309
|
// Setup
|
|
310
310
|
let db = (await opendb(null)).db
|
|
311
311
|
let user = db.model('user', {
|
package/test/crud.js
CHANGED
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
module.exports = function(monastery, opendb) {
|
|
4
4
|
|
|
5
|
-
test('
|
|
5
|
+
test('basic operator calls', async () => {
|
|
6
6
|
let db = (await opendb(null)).db
|
|
7
7
|
let user = db.model('user', {
|
|
8
|
-
fields: {
|
|
9
|
-
|
|
8
|
+
fields: {
|
|
9
|
+
name: { type: 'string' },
|
|
10
|
+
},
|
|
10
11
|
})
|
|
11
12
|
|
|
12
13
|
// Insert one
|
|
@@ -79,60 +80,6 @@ module.exports = function(monastery, opendb) {
|
|
|
79
80
|
let findOne3 = await user.findOne(inserted2[0]._id.toString())
|
|
80
81
|
expect(findOne3).toEqual({ _id: inserted2[0]._id, name: 'Martin Luther1' })
|
|
81
82
|
|
|
82
|
-
// Update
|
|
83
|
-
let update = await user.update({
|
|
84
|
-
query: inserted._id,
|
|
85
|
-
data: { name: 'Martin Luther2' }
|
|
86
|
-
})
|
|
87
|
-
expect(update).toEqual({
|
|
88
|
-
name: 'Martin Luther2'
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
// Update (no/empty data object)
|
|
92
|
-
await expect(user.update({ query: inserted._id, data: {}}))
|
|
93
|
-
.rejects.toThrow('No valid data passed to user.update()')
|
|
94
|
-
|
|
95
|
-
await expect(user.update({ query: inserted._id }))
|
|
96
|
-
.rejects.toThrow('No valid data passed to user.update()')
|
|
97
|
-
|
|
98
|
-
// Update (no/empty data object, but has update operators
|
|
99
|
-
await expect(user.update({ query: inserted._id, $set: { name: 'bruce' }}))
|
|
100
|
-
.resolves.toEqual({ name: 'bruce' })
|
|
101
|
-
|
|
102
|
-
await expect(user.update({ query: inserted._id, $pull: { name: 'bruce' }}))
|
|
103
|
-
.rejects.toThrow('Cannot apply $pull to a non-array value') // gets passed no valid data check
|
|
104
|
-
|
|
105
|
-
// Update (data & $set)
|
|
106
|
-
await expect(user.update({ query: inserted._id, data: {}, $set: { name: 'bruce' }}))
|
|
107
|
-
.rejects.toThrow('Please only pass options.$set or options.data to user.update()')
|
|
108
|
-
|
|
109
|
-
// Update (with operators. Make sure beforeValidate isn't called)
|
|
110
|
-
var beforeValidateHookCalled = false
|
|
111
|
-
await user.update({ query: inserted._id, data: { name: 'bruce' }})
|
|
112
|
-
expect(beforeValidateHookCalled).toEqual(true)
|
|
113
|
-
beforeValidateHookCalled = false
|
|
114
|
-
await user.update({ query: inserted._id, $set: { name: 'bruce' }})
|
|
115
|
-
expect(beforeValidateHookCalled).toEqual(false)
|
|
116
|
-
|
|
117
|
-
// Update multiple
|
|
118
|
-
await user.update({
|
|
119
|
-
query: { _id: { $in: [inserted2[0]._id, inserted2[1]._id] }},
|
|
120
|
-
data: { name: 'Martin Luther3' },
|
|
121
|
-
multi: true
|
|
122
|
-
})
|
|
123
|
-
let findUpdated2 = await user.find({
|
|
124
|
-
query: { _id: { $in: [inserted2[0]._id, inserted2[1]._id] }}
|
|
125
|
-
})
|
|
126
|
-
expect(findUpdated2).toEqual([
|
|
127
|
-
{
|
|
128
|
-
_id: expect.any(Object),
|
|
129
|
-
name: 'Martin Luther3'
|
|
130
|
-
}, {
|
|
131
|
-
_id: expect.any(Object),
|
|
132
|
-
name: 'Martin Luther3'
|
|
133
|
-
}
|
|
134
|
-
])
|
|
135
|
-
|
|
136
83
|
// Remove
|
|
137
84
|
let remove = await user.remove({ query: inserted._id })
|
|
138
85
|
expect(remove.result).toEqual({ n: 1, ok: 1 })
|
|
@@ -140,7 +87,7 @@ module.exports = function(monastery, opendb) {
|
|
|
140
87
|
db.close()
|
|
141
88
|
})
|
|
142
89
|
|
|
143
|
-
test('
|
|
90
|
+
test('insert defaults', async () => {
|
|
144
91
|
let db = (await opendb(null, { defaultObjects: true, serverSelectionTimeoutMS: 2000 })).db
|
|
145
92
|
let db2 = (await opendb(null, { useMilliseconds: true, serverSelectionTimeoutMS: 2000 })).db
|
|
146
93
|
let user = db.model('user', { fields: {
|
|
@@ -194,6 +141,65 @@ module.exports = function(monastery, opendb) {
|
|
|
194
141
|
db2.close()
|
|
195
142
|
})
|
|
196
143
|
|
|
144
|
+
test('update basics', async () => {
|
|
145
|
+
let db = (await opendb(null)).db
|
|
146
|
+
let user = db.model('user', {
|
|
147
|
+
fields: {
|
|
148
|
+
name: { type: 'string' },
|
|
149
|
+
},
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
// Insert
|
|
153
|
+
let inserted = await user.insert({ data: { name: 'Martin Luther' }})
|
|
154
|
+
expect(inserted).toEqual({
|
|
155
|
+
_id: expect.any(Object),
|
|
156
|
+
name: 'Martin Luther'
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
// Insert multiple
|
|
160
|
+
let inserted2 = await user.insert({ data: [{ name: 'Martin Luther1' }, { name: 'Martin Luther2' }]})
|
|
161
|
+
expect(inserted2).toEqual([
|
|
162
|
+
{
|
|
163
|
+
_id: expect.any(Object),
|
|
164
|
+
name: 'Martin Luther1'
|
|
165
|
+
}, {
|
|
166
|
+
_id: expect.any(Object),
|
|
167
|
+
name: 'Martin Luther2'
|
|
168
|
+
}
|
|
169
|
+
])
|
|
170
|
+
|
|
171
|
+
// Update
|
|
172
|
+
await expect(user.update({ query: inserted._id, data: { name: 'Martin Luther2' }}))
|
|
173
|
+
.resolves.toEqual({ name: 'Martin Luther2' })
|
|
174
|
+
|
|
175
|
+
// Update (no/empty data object)
|
|
176
|
+
await expect(user.update({ query: inserted._id, data: {}}))
|
|
177
|
+
.rejects.toThrow('No valid data passed to user.update({ data: .. })')
|
|
178
|
+
|
|
179
|
+
await expect(user.update({ query: inserted._id }))
|
|
180
|
+
.rejects.toThrow('Please pass an update operator to user.update(), e.g. data, $unset, etc')
|
|
181
|
+
|
|
182
|
+
// Update multiple
|
|
183
|
+
await user.update({
|
|
184
|
+
query: { _id: { $in: [inserted2[0]._id, inserted2[1]._id] }},
|
|
185
|
+
data: { name: 'Martin Luther3' },
|
|
186
|
+
multi: true
|
|
187
|
+
})
|
|
188
|
+
let findUpdated2 = await user.find({
|
|
189
|
+
query: { _id: { $in: [inserted2[0]._id, inserted2[1]._id] }}
|
|
190
|
+
})
|
|
191
|
+
expect(findUpdated2).toEqual([
|
|
192
|
+
{
|
|
193
|
+
_id: expect.any(Object),
|
|
194
|
+
name: 'Martin Luther3'
|
|
195
|
+
}, {
|
|
196
|
+
_id: expect.any(Object),
|
|
197
|
+
name: 'Martin Luther3'
|
|
198
|
+
}
|
|
199
|
+
])
|
|
200
|
+
db.close()
|
|
201
|
+
})
|
|
202
|
+
|
|
197
203
|
test('update defaults', async () => {
|
|
198
204
|
let db = (await opendb(null, { useMilliseconds: true, serverSelectionTimeoutMS: 2000 })).db
|
|
199
205
|
let user = db.model('user', {
|
|
@@ -235,7 +241,7 @@ module.exports = function(monastery, opendb) {
|
|
|
235
241
|
query: inserted._id,
|
|
236
242
|
data: {},
|
|
237
243
|
timestamps: false
|
|
238
|
-
})).rejects.toThrow('No valid data passed to user.update()')
|
|
244
|
+
})).rejects.toThrow('No valid data passed to user.update({ data: .. })')
|
|
239
245
|
|
|
240
246
|
// UpdatedAt override (wont work)
|
|
241
247
|
let updated4 = await user.update({
|
|
@@ -255,7 +261,51 @@ module.exports = function(monastery, opendb) {
|
|
|
255
261
|
db.close()
|
|
256
262
|
})
|
|
257
263
|
|
|
258
|
-
test('
|
|
264
|
+
test('update operators', async () => {
|
|
265
|
+
let db = (await opendb(null)).db
|
|
266
|
+
let user = db.model('userOperators', {
|
|
267
|
+
fields: {
|
|
268
|
+
name: { type: 'string', minLength: 5 },
|
|
269
|
+
age: { type: 'number' },
|
|
270
|
+
},
|
|
271
|
+
beforeValidate: [(data, next) => { beforeValidateHookCalled = true; next() }]
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
let inserted = await user.insert({
|
|
275
|
+
data: { name: 'Bruce', age: 12 }
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
// No data object, but has another update operators
|
|
279
|
+
await expect(user.update({ query: inserted._id, $set: { name: 'bruce' }}))
|
|
280
|
+
.resolves.toEqual({ name: 'bruce' })
|
|
281
|
+
|
|
282
|
+
// Mixing data and $set, and $set skips validation (minLength)
|
|
283
|
+
await expect(user.update({ query: inserted._id, data: { name: 'bruce2', age: 12 }, $set: { name: 'john' }}))
|
|
284
|
+
.resolves.toEqual({ age: 12, name: 'john' })
|
|
285
|
+
|
|
286
|
+
// Two operators
|
|
287
|
+
await user.update({ query: inserted._id, $set: { name: 'john' }, $unset: { age: 1 }})
|
|
288
|
+
await expect(user.findOne({ query: inserted._id })).resolves.toEqual({
|
|
289
|
+
_id: expect.any(Object),
|
|
290
|
+
name: 'john',
|
|
291
|
+
})
|
|
292
|
+
|
|
293
|
+
// $pull on a non array (also gets passed no valid data check)
|
|
294
|
+
await expect(user.update({ query: inserted._id, $pull: { name: 'bruce' }}))
|
|
295
|
+
.rejects.toThrow('Cannot apply $pull to a non-array value')
|
|
296
|
+
|
|
297
|
+
// Non-data operators don't call beforeValidate
|
|
298
|
+
var beforeValidateHookCalled = false
|
|
299
|
+
await user.update({ query: inserted._id, data: { name: 'bruce' }})
|
|
300
|
+
expect(beforeValidateHookCalled).toEqual(true)
|
|
301
|
+
beforeValidateHookCalled = false
|
|
302
|
+
await user.update({ query: inserted._id, $set: { name: 'bruce' }})
|
|
303
|
+
expect(beforeValidateHookCalled).toEqual(false)
|
|
304
|
+
|
|
305
|
+
db.close()
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
test('insert with id casting', async () => {
|
|
259
309
|
let db = (await opendb(null)).db
|
|
260
310
|
db.model('company', { fields: {
|
|
261
311
|
name: { type: 'string' }
|
|
@@ -279,56 +329,52 @@ module.exports = function(monastery, opendb) {
|
|
|
279
329
|
db.close()
|
|
280
330
|
})
|
|
281
331
|
|
|
282
|
-
test('
|
|
332
|
+
test('find default field population', async () => {
|
|
283
333
|
let db = (await opendb(null)).db
|
|
284
334
|
let user = db.model('user', {
|
|
285
335
|
fields: {
|
|
286
|
-
name: { type: 'string'},
|
|
287
|
-
addresses: [{ city: { type: 'string' }, country: { type: 'string' }}],
|
|
336
|
+
name: { type: 'string', default: 'Martin Luther' },
|
|
337
|
+
addresses: [{ city: { type: 'string' }, country: { type: 'string', default: 'Germany' } }],
|
|
338
|
+
address: { country: { type: 'string', default: 'Germany' }},
|
|
288
339
|
pet: { dog: { model: 'dog' }},
|
|
289
340
|
dogs: [{ model: 'dog' }], // virtual association
|
|
290
341
|
}
|
|
291
342
|
})
|
|
292
343
|
let dog = db.model('dog', {
|
|
293
344
|
fields: {
|
|
294
|
-
name: { type: 'string' },
|
|
345
|
+
name: { type: 'string', default: 'Scruff' },
|
|
295
346
|
user: { model: 'user' }
|
|
296
347
|
}
|
|
297
348
|
})
|
|
298
349
|
|
|
299
|
-
//
|
|
300
|
-
let
|
|
301
|
-
let
|
|
350
|
+
// Default field doesn't override null
|
|
351
|
+
let nulldoc1 = await dog.insert({ data: { name: null }})
|
|
352
|
+
let nullfind1 = await dog.findOne({ query: nulldoc1._id })
|
|
353
|
+
expect(nullfind1).toEqual({ _id: nulldoc1._id, name: null })
|
|
354
|
+
|
|
355
|
+
// Default field doesn't override empty string
|
|
356
|
+
let nulldoc2 = await dog.insert({ data: { name: '' }})
|
|
357
|
+
let nullfind2 = await dog.findOne({ query: nulldoc2._id })
|
|
358
|
+
expect(nullfind2).toEqual({ _id: nulldoc2._id, name: '' })
|
|
359
|
+
|
|
360
|
+
// Default field overrides undefined
|
|
361
|
+
let nulldoc3 = await dog.insert({ data: { name: undefined }})
|
|
362
|
+
let nullfind3 = await dog.findOne({ query: nulldoc3._id })
|
|
363
|
+
expect(nullfind3).toEqual({ _id: nullfind3._id, name: 'Scruff' })
|
|
364
|
+
|
|
365
|
+
// Default field population test
|
|
366
|
+
// Note that addresses.1.country shouldn't be overriden
|
|
367
|
+
// Insert documents (without defaults)
|
|
368
|
+
let inserted = await dog._insert({})
|
|
369
|
+
let inserted2 = await user._insert({
|
|
302
370
|
addresses: [
|
|
303
371
|
{ city: 'Frankfurt' },
|
|
304
372
|
{ city: 'Christchurch', country: 'New Zealand' }
|
|
305
373
|
],
|
|
306
374
|
pet: { dog: inserted._id }
|
|
307
|
-
}})
|
|
308
|
-
await dog.update({
|
|
309
|
-
query: inserted._id,
|
|
310
|
-
data: { user: inserted2._id }
|
|
311
375
|
})
|
|
376
|
+
await dog._update(inserted._id, { $set: { user: inserted2._id }})
|
|
312
377
|
|
|
313
|
-
// Update models
|
|
314
|
-
db.model('user', {
|
|
315
|
-
fields: {
|
|
316
|
-
name: { type: 'string', default: 'Martin Luther' },
|
|
317
|
-
addresses: [{ city: { type: 'string' }, country: { type: 'string', default: 'Germany' } }],
|
|
318
|
-
address: { country: { type: 'string', default: 'Germany' }},
|
|
319
|
-
pet: { dog: { model: 'dog' }},
|
|
320
|
-
dogs: [{ model: 'dog' }], // virtual association
|
|
321
|
-
}
|
|
322
|
-
})
|
|
323
|
-
db.model('dog', {
|
|
324
|
-
fields: {
|
|
325
|
-
name: { type: 'string', default: 'Scruff' },
|
|
326
|
-
user: { model: 'user' }
|
|
327
|
-
}
|
|
328
|
-
})
|
|
329
|
-
|
|
330
|
-
// Default field population test
|
|
331
|
-
// Note that addresses.1.country shouldn't be overriden
|
|
332
378
|
let find1 = await user.findOne({
|
|
333
379
|
query: inserted2._id,
|
|
334
380
|
populate: ['pet.dog', {
|
|
@@ -369,7 +415,7 @@ module.exports = function(monastery, opendb) {
|
|
|
369
415
|
db.close()
|
|
370
416
|
})
|
|
371
417
|
|
|
372
|
-
test('
|
|
418
|
+
test('hooks', async () => {
|
|
373
419
|
let db = (await opendb(null)).db
|
|
374
420
|
let user = db.model('user', {
|
|
375
421
|
fields: {
|
package/test/model.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
module.exports = function(monastery, opendb) {
|
|
2
2
|
|
|
3
|
-
test('
|
|
3
|
+
test('model setup', async () => {
|
|
4
4
|
// Setup
|
|
5
5
|
let db = (await opendb(false)).db
|
|
6
6
|
let user = db.model('user', { fields: {
|
|
@@ -68,7 +68,7 @@ module.exports = function(monastery, opendb) {
|
|
|
68
68
|
))
|
|
69
69
|
})
|
|
70
70
|
|
|
71
|
-
test('
|
|
71
|
+
test('model setup with default fields', async () => {
|
|
72
72
|
// Setup
|
|
73
73
|
let db = (await opendb(false, { defaultObjects: true })).db
|
|
74
74
|
|
|
@@ -90,7 +90,7 @@ module.exports = function(monastery, opendb) {
|
|
|
90
90
|
})
|
|
91
91
|
})
|
|
92
92
|
|
|
93
|
-
test('
|
|
93
|
+
test('model setup with default objects', async () => {
|
|
94
94
|
// Setup
|
|
95
95
|
let db = (await opendb(false, { defaultObjects: true })).db
|
|
96
96
|
let user = db.model('user', { fields: {
|
|
@@ -115,7 +115,7 @@ module.exports = function(monastery, opendb) {
|
|
|
115
115
|
})
|
|
116
116
|
})
|
|
117
117
|
|
|
118
|
-
test('
|
|
118
|
+
test('model indexes', async () => {
|
|
119
119
|
// Setup: Need to test different types of indexes
|
|
120
120
|
let db = (await opendb(null)).db
|
|
121
121
|
// Setup: Drop previously tested collections
|
|
@@ -189,7 +189,7 @@ module.exports = function(monastery, opendb) {
|
|
|
189
189
|
db.close()
|
|
190
190
|
})
|
|
191
191
|
|
|
192
|
-
test('
|
|
192
|
+
test('model subdocument indexes', async () => {
|
|
193
193
|
// Setup: Need to test different types of indexes
|
|
194
194
|
let db = (await opendb(null)).db
|
|
195
195
|
// Setup: Drop previously tested collections
|
|
@@ -234,7 +234,7 @@ module.exports = function(monastery, opendb) {
|
|
|
234
234
|
db.close()
|
|
235
235
|
})
|
|
236
236
|
|
|
237
|
-
test('
|
|
237
|
+
test('model array indexes', async () => {
|
|
238
238
|
// Setup: Need to test different types of indexes
|
|
239
239
|
let db = (await opendb(null)).db
|
|
240
240
|
// Setup: Drop previously tested collections
|
|
@@ -279,7 +279,7 @@ module.exports = function(monastery, opendb) {
|
|
|
279
279
|
db.close()
|
|
280
280
|
})
|
|
281
281
|
|
|
282
|
-
test('
|
|
282
|
+
test('model 2dsphere indexes', async () => {
|
|
283
283
|
// Setup. The tested model needs to be unique as race condition issue arises when the same model
|
|
284
284
|
// with text indexes are setup at the same time
|
|
285
285
|
let db = (await opendb(null)).db
|
|
@@ -339,7 +339,7 @@ module.exports = function(monastery, opendb) {
|
|
|
339
339
|
db.close()
|
|
340
340
|
})
|
|
341
341
|
|
|
342
|
-
test('
|
|
342
|
+
test('model findBL findBLProject', async () => {
|
|
343
343
|
let db = (await opendb(null)).db
|
|
344
344
|
db.model('bird', { fields: {
|
|
345
345
|
name: { type: 'string' }
|
package/test/populate.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
module.exports = function(monastery, opendb) {
|
|
2
2
|
|
|
3
|
-
test('
|
|
3
|
+
test('model populate', async () => {
|
|
4
4
|
// Setup
|
|
5
5
|
let db = (await opendb(null)).db
|
|
6
6
|
let bird = db.model('bird', { fields: {
|
|
@@ -82,7 +82,7 @@ module.exports = function(monastery, opendb) {
|
|
|
82
82
|
db.close()
|
|
83
83
|
})
|
|
84
84
|
|
|
85
|
-
test('
|
|
85
|
+
test('model populate type=any', async () => {
|
|
86
86
|
let db = (await opendb(null)).db
|
|
87
87
|
db.model('company', { fields: {
|
|
88
88
|
address: { type: 'any' }
|
|
@@ -132,7 +132,7 @@ module.exports = function(monastery, opendb) {
|
|
|
132
132
|
db.close()
|
|
133
133
|
})
|
|
134
134
|
|
|
135
|
-
test('
|
|
135
|
+
test('model populate or blacklisting via $lookup', async () => {
|
|
136
136
|
// Setup
|
|
137
137
|
let db = (await opendb(null)).db
|
|
138
138
|
let user = db.model('user', {
|
package/test/util.js
CHANGED
|
@@ -2,7 +2,7 @@ let util = require('../lib/util')
|
|
|
2
2
|
|
|
3
3
|
module.exports = function(monastery, opendb) {
|
|
4
4
|
|
|
5
|
-
test('
|
|
5
|
+
test('utilities formdata', async () => {
|
|
6
6
|
expect(await util.parseFormData({
|
|
7
7
|
'name': 'Martin',
|
|
8
8
|
'pets[]': '',
|
|
@@ -31,7 +31,7 @@ module.exports = function(monastery, opendb) {
|
|
|
31
31
|
.toEqual('Array items in bracket notation need array indexes "users[][\'name\']", e.g. users[0][name]')
|
|
32
32
|
})
|
|
33
33
|
|
|
34
|
-
test('
|
|
34
|
+
test('utilities isId', async () => {
|
|
35
35
|
let db = (await opendb(false)).db
|
|
36
36
|
expect(db.isId('')).toEqual(false)
|
|
37
37
|
expect(db.isId(1234)).toEqual(false)
|
package/test/validate.js
CHANGED
|
@@ -2,7 +2,7 @@ let validate = require('../lib/model-validate')
|
|
|
2
2
|
|
|
3
3
|
module.exports = function(monastery, opendb) {
|
|
4
4
|
|
|
5
|
-
test('
|
|
5
|
+
test('validation basic errors', async () => {
|
|
6
6
|
// Setup
|
|
7
7
|
let db = (await opendb(false)).db
|
|
8
8
|
let user = db.model('user', { fields: {
|
|
@@ -19,7 +19,7 @@ module.exports = function(monastery, opendb) {
|
|
|
19
19
|
detail: 'This field is required.',
|
|
20
20
|
meta: { rule: 'required', model: 'user', field: 'name' }
|
|
21
21
|
})
|
|
22
|
-
await expect(user.validate({ name : '' }
|
|
22
|
+
await expect(user.validate({ name : '' })).rejects.toContainEqual({
|
|
23
23
|
status: '400',
|
|
24
24
|
title: 'name',
|
|
25
25
|
detail: 'This field is required.',
|
|
@@ -34,7 +34,16 @@ module.exports = function(monastery, opendb) {
|
|
|
34
34
|
await expect(user.validate({}, { update: true })).resolves.toEqual({})
|
|
35
35
|
|
|
36
36
|
// Type error (string)
|
|
37
|
-
await expect(user.validate({ name: 1 })).
|
|
37
|
+
await expect(user.validate({ name: 1 })).resolves.toEqual({ name: '1' })
|
|
38
|
+
await expect(user.validate({ name: 1.123 })).resolves.toEqual({ name: '1.123' })
|
|
39
|
+
await expect(user.validate({ name: undefined }, { validateUndefined: false })).resolves.toEqual({})
|
|
40
|
+
await expect(user.validate({ name: null })).rejects.toContainEqual({
|
|
41
|
+
status: '400',
|
|
42
|
+
title: 'name',
|
|
43
|
+
detail: 'This field is required.',
|
|
44
|
+
meta: { rule: 'required', model: 'user', field: 'name' }
|
|
45
|
+
})
|
|
46
|
+
await expect(user.validate({ name: true })).rejects.toContainEqual({
|
|
38
47
|
status: '400',
|
|
39
48
|
title: 'name',
|
|
40
49
|
detail: 'Value was not a string.',
|
|
@@ -42,6 +51,7 @@ module.exports = function(monastery, opendb) {
|
|
|
42
51
|
})
|
|
43
52
|
|
|
44
53
|
// Type error (date)
|
|
54
|
+
await expect(user.validate({ name: 'a', date: null })).resolves.toEqual({ name: 'a', date: null })
|
|
45
55
|
await expect(user.validate({ name: 'a', date: 'fe' })).rejects.toContainEqual({
|
|
46
56
|
status: '400',
|
|
47
57
|
title: 'date',
|
|
@@ -49,6 +59,32 @@ module.exports = function(monastery, opendb) {
|
|
|
49
59
|
meta: { rule: 'isDate', model: 'user', field: 'date' }
|
|
50
60
|
})
|
|
51
61
|
|
|
62
|
+
// Type error (number)
|
|
63
|
+
let usernum = db.model('usernum', { fields: { amount: { type: 'number', required: true }}})
|
|
64
|
+
let usernum2 = db.model('usernum2', { fields: { amount: { type: 'number' }}})
|
|
65
|
+
await expect(usernum.validate({ amount: 0 })).resolves.toEqual({ amount: 0 })
|
|
66
|
+
await expect(usernum.validate({ amount: '0' })).resolves.toEqual({ amount: 0 })
|
|
67
|
+
await expect(usernum2.validate({ amount: '' })).resolves.toEqual({ amount: null })
|
|
68
|
+
await expect(usernum.validate({ amount: undefined }, { validateUndefined: false })).resolves.toEqual({})
|
|
69
|
+
await expect(usernum.validate({ amount: false })).rejects.toEqual([{
|
|
70
|
+
status: '400',
|
|
71
|
+
title: 'amount',
|
|
72
|
+
detail: 'Value was not a number.',
|
|
73
|
+
meta: { rule: 'isNumber', model: 'usernum', field: 'amount' }
|
|
74
|
+
}])
|
|
75
|
+
await expect(usernum.validate({ amount: null })).rejects.toEqual([{
|
|
76
|
+
status: '400',
|
|
77
|
+
title: 'amount',
|
|
78
|
+
detail: 'This field is required.',
|
|
79
|
+
meta: { rule: 'required', model: 'usernum', field: 'amount' },
|
|
80
|
+
}])
|
|
81
|
+
await expect(usernum.validate({ amount: null }, { validateUndefined: false })).rejects.toEqual([{
|
|
82
|
+
status: '400',
|
|
83
|
+
title: 'amount',
|
|
84
|
+
detail: 'This field is required.',
|
|
85
|
+
meta: { rule: 'required', model: 'usernum', field: 'amount' },
|
|
86
|
+
}])
|
|
87
|
+
|
|
52
88
|
// Type error (array)
|
|
53
89
|
await expect(user.validate({ name: 'a', colors: 1 })).rejects.toContainEqual({
|
|
54
90
|
status: '400',
|
|
@@ -82,7 +118,7 @@ module.exports = function(monastery, opendb) {
|
|
|
82
118
|
})
|
|
83
119
|
})
|
|
84
120
|
|
|
85
|
-
test('
|
|
121
|
+
test('validation subdocument errors', async () => {
|
|
86
122
|
// Setup
|
|
87
123
|
let db = (await opendb(false)).db
|
|
88
124
|
let user = db.model('user', { fields: {
|
|
@@ -209,7 +245,7 @@ module.exports = function(monastery, opendb) {
|
|
|
209
245
|
})
|
|
210
246
|
})
|
|
211
247
|
|
|
212
|
-
test('
|
|
248
|
+
test('validation array errors', async () => {
|
|
213
249
|
// Setup
|
|
214
250
|
let db = (await opendb(false)).db
|
|
215
251
|
let user = db.model('user', { fields: {
|
|
@@ -224,7 +260,7 @@ module.exports = function(monastery, opendb) {
|
|
|
224
260
|
|
|
225
261
|
// Type error within an array (string)
|
|
226
262
|
await expect(user.validate({
|
|
227
|
-
animals: { cats: [
|
|
263
|
+
animals: { cats: [true] }
|
|
228
264
|
})).rejects.toContainEqual({
|
|
229
265
|
status: '400',
|
|
230
266
|
title: 'animals.cats.0',
|
|
@@ -239,7 +275,7 @@ module.exports = function(monastery, opendb) {
|
|
|
239
275
|
detail: 'This field is required.',
|
|
240
276
|
meta: { rule: 'required', model: 'user', field: 'color' }
|
|
241
277
|
}
|
|
242
|
-
await expect(user.validate({ animals: { dogs: [{ name: 'sparky', color:
|
|
278
|
+
await expect(user.validate({ animals: { dogs: [{ name: 'sparky', color: false }] }}))
|
|
243
279
|
.rejects.toContainEqual({
|
|
244
280
|
...error,
|
|
245
281
|
detail: 'Value was not a string.',
|
|
@@ -273,7 +309,7 @@ module.exports = function(monastery, opendb) {
|
|
|
273
309
|
.rejects.toContainEqual(error)
|
|
274
310
|
})
|
|
275
311
|
|
|
276
|
-
test('
|
|
312
|
+
test('validation getMostSpecificKeyMatchingPath', async () => {
|
|
277
313
|
let fn = validate._getMostSpecificKeyMatchingPath
|
|
278
314
|
let mock = {
|
|
279
315
|
'cats.name': true,
|
|
@@ -305,7 +341,7 @@ module.exports = function(monastery, opendb) {
|
|
|
305
341
|
expect(fn(mock, 'gulls.1')).toEqual('gulls.$')
|
|
306
342
|
})
|
|
307
343
|
|
|
308
|
-
test('
|
|
344
|
+
test('validation default messages', async () => {
|
|
309
345
|
// Setup
|
|
310
346
|
let db = (await opendb(false)).db
|
|
311
347
|
let user = db.model('user', {
|
|
@@ -357,7 +393,7 @@ module.exports = function(monastery, opendb) {
|
|
|
357
393
|
})
|
|
358
394
|
})
|
|
359
395
|
|
|
360
|
-
test('
|
|
396
|
+
test('validation custom messages', async () => {
|
|
361
397
|
// Setup
|
|
362
398
|
// Todo: Setup testing for array array subdocument field messages
|
|
363
399
|
let db = (await opendb(false)).db
|
|
@@ -399,7 +435,7 @@ module.exports = function(monastery, opendb) {
|
|
|
399
435
|
})
|
|
400
436
|
})
|
|
401
437
|
|
|
402
|
-
test('
|
|
438
|
+
test('validation custom messages for arrays', async () => {
|
|
403
439
|
// Setup
|
|
404
440
|
// Todo: Setup testing for array array subdocument field messages
|
|
405
441
|
let db = (await opendb(false)).db
|
|
@@ -516,7 +552,7 @@ module.exports = function(monastery, opendb) {
|
|
|
516
552
|
})
|
|
517
553
|
})
|
|
518
554
|
|
|
519
|
-
test('
|
|
555
|
+
test('validation custom rules', async () => {
|
|
520
556
|
// Setup
|
|
521
557
|
let db = (await opendb(false)).db
|
|
522
558
|
let user = db.model('user', {
|
|
@@ -602,7 +638,7 @@ module.exports = function(monastery, opendb) {
|
|
|
602
638
|
}])
|
|
603
639
|
})
|
|
604
640
|
|
|
605
|
-
test('
|
|
641
|
+
test('validated data', async () => {
|
|
606
642
|
// Setup
|
|
607
643
|
let db = (await opendb(false)).db
|
|
608
644
|
let fields = {
|
|
@@ -623,16 +659,9 @@ module.exports = function(monastery, opendb) {
|
|
|
623
659
|
// Ignores invalid data
|
|
624
660
|
await expect(user.validate({ badprop: true, schema: {} })).resolves.toEqual({})
|
|
625
661
|
|
|
626
|
-
// Rejects null for non object/array fields
|
|
627
|
-
await expect(user.validate({ name: null })).rejects.toEqual([{
|
|
628
|
-
'detail': 'Value was not a string.',
|
|
629
|
-
'meta': {'detailLong': undefined, 'field': 'name', 'model': 'user', 'rule': 'isString'},
|
|
630
|
-
'status':'400',
|
|
631
|
-
'title': 'name'
|
|
632
|
-
}])
|
|
633
|
-
|
|
634
662
|
// String data
|
|
635
663
|
await expect(user.validate({ name: 'Martin Luther' })).resolves.toEqual({ name: 'Martin Luther' })
|
|
664
|
+
await expect(user.validate({ name: null })).resolves.toEqual({ name: null })
|
|
636
665
|
|
|
637
666
|
// Array data
|
|
638
667
|
await expect(user.validate({ names: ['blue'] })).resolves.toEqual({ names: ['blue'] })
|
|
@@ -652,12 +681,7 @@ module.exports = function(monastery, opendb) {
|
|
|
652
681
|
|
|
653
682
|
// Subdocument property data (null)
|
|
654
683
|
await expect(user.validate({ animals: { dog: null }}))
|
|
655
|
-
.
|
|
656
|
-
'detail': 'Value was not a string.',
|
|
657
|
-
'meta': {'detailLong': undefined, 'field': 'dog', 'model': 'user', 'rule': 'isString'},
|
|
658
|
-
'status': '400',
|
|
659
|
-
'title': 'animals.dog',
|
|
660
|
-
}])
|
|
684
|
+
.resolves.toEqual({ animals: { dog: null }})
|
|
661
685
|
|
|
662
686
|
// Subdocument property data (unknown data)
|
|
663
687
|
await expect(user.validate({ animals: { dog: 'sparky', cat: 'grumpy' } }))
|
|
@@ -672,7 +696,7 @@ module.exports = function(monastery, opendb) {
|
|
|
672
696
|
.resolves.toEqual({ animals: { dogs: [{}] }})
|
|
673
697
|
})
|
|
674
698
|
|
|
675
|
-
test('
|
|
699
|
+
test('schema options', async () => {
|
|
676
700
|
// Setup
|
|
677
701
|
let db = (await opendb(false)).db
|
|
678
702
|
let user = db.model('user', { fields: {
|
|
@@ -713,7 +737,7 @@ module.exports = function(monastery, opendb) {
|
|
|
713
737
|
})
|
|
714
738
|
})
|
|
715
739
|
|
|
716
|
-
test('
|
|
740
|
+
test('schema default rules', async () => {
|
|
717
741
|
// Setup
|
|
718
742
|
let db = (await opendb(false)).db
|
|
719
743
|
let user = db.model('user', { fields: {
|
|
@@ -766,15 +790,10 @@ module.exports = function(monastery, opendb) {
|
|
|
766
790
|
})
|
|
767
791
|
|
|
768
792
|
// Number valid
|
|
769
|
-
await expect(user2.validate({ amount: 0 })).resolves.toEqual({ amount: 0 })
|
|
770
|
-
await expect(
|
|
771
|
-
await expect(user.validate({ amount: undefined })).resolves.toEqual({})
|
|
772
|
-
await expect(user.validate({ amount: null })).
|
|
773
|
-
'detail': 'Value was not a number.',
|
|
774
|
-
'meta': { 'field': 'amount', 'model': 'user', 'rule': 'isNumber'},
|
|
775
|
-
'status': '400',
|
|
776
|
-
'title': 'amount'
|
|
777
|
-
}])
|
|
793
|
+
await expect(user2.validate({ amount: 0 })).resolves.toEqual({ amount: 0 })
|
|
794
|
+
await expect(user2.validate({ amount: '0' })).resolves.toEqual({ amount: 0 })
|
|
795
|
+
await expect(user.validate({ amount: undefined })).resolves.toEqual({})
|
|
796
|
+
await expect(user.validate({ amount: null })).resolves.toEqual({ amount: null })
|
|
778
797
|
|
|
779
798
|
// Number required
|
|
780
799
|
let mock1 = {
|
|
@@ -799,7 +818,7 @@ module.exports = function(monastery, opendb) {
|
|
|
799
818
|
await expect(user.validate({ amount: 'bad' })).rejects.toContainEqual(mock2)
|
|
800
819
|
})
|
|
801
820
|
|
|
802
|
-
test('
|
|
821
|
+
test('schema default objects', async () => {
|
|
803
822
|
let db = (await opendb(null, {
|
|
804
823
|
timestamps: false,
|
|
805
824
|
defaultObjects: true,
|
|
@@ -825,7 +844,7 @@ module.exports = function(monastery, opendb) {
|
|
|
825
844
|
db.close()
|
|
826
845
|
})
|
|
827
846
|
|
|
828
|
-
test('
|
|
847
|
+
test('schema nullObjects', async () => {
|
|
829
848
|
let db = (await opendb(null, {
|
|
830
849
|
timestamps: false,
|
|
831
850
|
nullObjects: true,
|
|
@@ -851,7 +870,7 @@ module.exports = function(monastery, opendb) {
|
|
|
851
870
|
db.close()
|
|
852
871
|
})
|
|
853
872
|
|
|
854
|
-
test('
|
|
873
|
+
test('validation options', async () => {
|
|
855
874
|
let db = (await opendb(false)).db
|
|
856
875
|
let user = db.model('user', { fields: {
|
|
857
876
|
name: { type: 'string', required: true }
|
|
@@ -936,7 +955,7 @@ module.exports = function(monastery, opendb) {
|
|
|
936
955
|
})
|
|
937
956
|
})
|
|
938
957
|
|
|
939
|
-
test('
|
|
958
|
+
test('validation hooks', async () => {
|
|
940
959
|
let db = (await opendb(null)).db
|
|
941
960
|
let user = db.model('user', {
|
|
942
961
|
fields: {
|
package/test/virtuals.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
module.exports = function(monastery, opendb) {
|
|
2
2
|
|
|
3
|
-
test('
|
|
3
|
+
test('virtuals', async () => {
|
|
4
4
|
// Setup
|
|
5
5
|
let db = (await opendb(null)).db
|
|
6
6
|
// Test model setup
|
|
@@ -147,7 +147,7 @@ module.exports = function(monastery, opendb) {
|
|
|
147
147
|
db.close()
|
|
148
148
|
})
|
|
149
149
|
|
|
150
|
-
test('
|
|
150
|
+
test('insert update virtuals (validate)', async () => {
|
|
151
151
|
// Setup
|
|
152
152
|
let db = (await opendb(null)).db
|
|
153
153
|
let user = db.model('user', {
|