monastery 1.33.0 → 1.36.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.
@@ -3,88 +3,63 @@ let rules = require('./rules')
3
3
 
4
4
  module.exports = {
5
5
 
6
- validate: function(data, opts, cb) {
6
+ validate: async function(data, opts, cb) {
7
7
  /**
8
8
  * Validates a model
9
- * @param {instance} model
10
9
  * @param {object} data
11
10
  * @param {object} <opts>
12
- * @param {boolean(false)} update - are we validating for insert or update?
13
- * @param {array|string|false} blacklist - augment schema blacklist, `false` will remove all blacklisting
14
- * @param {array|string} projection - only return these fields, ignores blacklist
15
- * @param {array|string|false} validateUndefined - validates all 'required' undefined fields, true by
16
- * default, but false on update
17
- * @param {array|string|true} skipValidation - skip validation on these fields
18
- * @param {boolean} timestamps - whether `createdAt` and `updatedAt` are inserted, or `updatedAt` is
11
+ * @param {array|string|false} <opts.blacklist> - augment insertBL/updateBL, `false` will remove blacklisting
12
+ * @param {array|string} <opts.project> - return only these fields, ignores blacklisting
13
+ * @param {array|string|true} <opts.skipValidation> - skip validation on these fields
14
+ * @param {boolean} <opts.timestamps> - whether `createdAt` and `updatedAt` are inserted, or `updatedAt` is
19
15
  * updated, depending on the `options.update` value
16
+ * @param {boolean(false)} <opts.update> - are we validating for insert or update? todo: change to `type`
17
+ * @param {array|string|false} <opts.validateUndefined> - validates all 'required' undefined fields, true by
18
+ * default, but false on update
20
19
  * @param {function} <cb> - instead of returning a promise
21
- * @this model
22
-
23
20
  * @return promise(errors[] || pruned data{})
21
+ * @this model
24
22
  */
25
23
 
26
24
  // Optional cb and opts
27
- if (util.isFunction(opts)) { cb = opts; opts = undefined }
25
+ if (util.isFunction(opts)) {
26
+ cb = opts; opts = undefined
27
+ }
28
28
  if (cb && !util.isFunction(cb)) {
29
29
  throw new Error(`The callback passed to ${this.name}.validate() is not a function`)
30
30
  }
31
- data = util.deepCopy(data)
32
- opts = opts || {}
33
- opts.insert = !opts.update
34
- opts.action = opts.update? 'update' : 'insert'
35
- opts.skipValidation = opts.skipValidation === true? true : util.toArray(opts.skipValidation||[])
31
+ try {
32
+ data = util.deepCopy(data)
33
+ opts = opts || {}
34
+ opts.update = opts.update || opts.findOneAndUpdate
35
+ opts.insert = !opts.update
36
+ opts.skipValidation = opts.skipValidation === true ? true : util.toArray(opts.skipValidation||[])
36
37
 
37
- // Blacklist
38
- if (opts.blacklist) {
39
- let whitelist = []
40
- let blacklist = [ ...this[`${opts.action}BL`] ]
41
- if (typeof opts.blacklist === 'string') {
42
- opts.blacklist = opts.blacklist.trim().split(/\s+/)
43
- }
44
- // Auguemnt the schema blacklist
45
- for (let _path of opts.blacklist) {
46
- let path = _path.replace(/^-/, '')
47
- if (_path.match(/^-/)) whitelist.push(path)
48
- else blacklist.push(path)
49
- }
50
- // Remove whitelisted/negated fields
51
- blacklist = blacklist.filter(o => !whitelist.includes(o))
52
- // Remove any deep blacklisted fields that have a whitelisted parent specified.
53
- // E.g remove ['deep.deep2.deep3'] if ['deep'] exists in the whitelist
54
- for (let i=blacklist.length; i--;) {
55
- let split = blacklist[i].split('.')
56
- for (let j=split.length; j--;) {
57
- if (split.length > 1) split.pop()
58
- else continue
59
- if (whitelist.includes(split.join())) {
60
- blacklist.splice(i, 1)
61
- break
62
- }
63
- }
64
- }
65
- opts.blacklist = blacklist
66
- } else {
67
- opts.blacklist = [ ...this[`${opts.action}BL`] ]
68
- }
38
+ // Get projection
39
+ if (opts.project) opts.projection = this._getProjectionFromProject(opts.project)
40
+ else opts.projection = this._getProjectionFromBlacklist(opts.update ? 'update' : 'insert', opts.blacklist)
41
+
42
+ // Hook: beforeValidate
43
+ await util.runSeries(this.beforeValidate.map(f => f.bind(opts, data)))
69
44
 
70
- // Run before hook, then recurse through the model's fields
71
- return util.runSeries(this.beforeValidate.map(f => f.bind(opts, data))).then(() => {
72
- return util.toArray(data).map(item => {
45
+ // Recurse and validate fields
46
+ let response = util.toArray(data).map(item => {
73
47
  let validated = this._validateFields(item, this.fields, item, opts, '')
74
48
  if (validated[0].length) throw validated[0]
75
49
  else return validated[1]
76
50
  })
77
51
 
78
- // Success/error
79
- }).then(data2 => {
80
- let response = util.isArray(data)? data2 : data2[0]
52
+ // Single document?
53
+ response = util.isArray(data)? response : response[0]
54
+
55
+ // Success/error
81
56
  if (cb) cb(null, response)
82
57
  else return Promise.resolve(response)
83
58
 
84
- }).catch(errs => {
85
- if (cb) cb(errs)
86
- else throw errs
87
- })
59
+ } catch (e) {
60
+ if (cb) cb(e)
61
+ else throw e
62
+ }
88
63
  },
89
64
 
90
65
  _getMostSpecificKeyMatchingPath: function(object, path) {
@@ -140,7 +115,7 @@ module.exports = {
140
115
  let value = util.isArray(fields)? data : (data||{})[fieldName]
141
116
  let indexOrFieldName = util.isArray(fields)? i : fieldName
142
117
  let path2 = `${path}.${indexOrFieldName}`.replace(/^\./, '')
143
- let path3 = path2.replace(/(^|\.)[0-9]+(\.|$)/, '$2') // no numerical keys, e.g. pets.1.name
118
+ let path3 = path2.replace(/(^|\.)[0-9]+(\.|$)/, '$2') // no numerical keys, e.g. pets.1.name = pets.name
144
119
  let isType = 'is' + util.ucFirst(schema.type)
145
120
  let isTypeRule = this.rules[isType] || rules[isType]
146
121
 
@@ -157,7 +132,7 @@ module.exports = {
157
132
  }
158
133
 
159
134
  // Ignore blacklisted
160
- if (opts.blacklist.indexOf(path3) >= 0 && !schema.defaultOverride) return
135
+ if (this._pathBlacklisted(path3, opts.projection) && !schema.defaultOverride) return
161
136
  // Ignore insert only
162
137
  if (opts.update && schema.insertOnly) return
163
138
  // Ignore virtual fields
@@ -218,8 +193,8 @@ module.exports = {
218
193
  * @param {object} field - field schema
219
194
  * @param {string} path - full field path
220
195
  * @param {object} opts - original validate() options
221
- * @this model
222
196
  * @return {array} errors
197
+ * @this model
223
198
  */
224
199
  let errors = []
225
200
  if (opts.skipValidation === true) return []
package/lib/model.js CHANGED
@@ -9,8 +9,8 @@ let Model = module.exports = function(name, opts, manager) {
9
9
  * @param {string} name
10
10
  * @param {object} opts - see mongodb colleciton documentation
11
11
  * @param {boolean} opts.waitForIndexes
12
- * @this model
13
12
  * @return Promise(model) | this
13
+ * @this model
14
14
  */
15
15
  if (!(this instanceof Model)) {
16
16
  return new Model(name, opts, this)
@@ -72,7 +72,7 @@ let Model = module.exports = function(name, opts, manager) {
72
72
  this._setupFields(this.fields = Object.assign({}, this._timestampFields, this.fields))
73
73
  this.fieldsFlattened = this._getFieldsFlattened(this.fields, '') // test output?
74
74
 
75
- // Extend model with monk collection actions
75
+ // Extend model with monk collection queries
76
76
  this._collection = manager.get? manager.get(name, { castIds: false }) : null
77
77
  if (!this._collection) {
78
78
  this.info('There is no mongodb connection, a lot of the monk/monastery methods will be unavailable')
@@ -83,8 +83,7 @@ let Model = module.exports = function(name, opts, manager) {
83
83
  }
84
84
 
85
85
  // Add model to manager
86
- if (typeof this.manager[name] === 'undefined'
87
- || typeof this.manager.model[name] !== 'undefined') {
86
+ if (typeof this.manager[name] === 'undefined' || typeof this.manager.model[name] !== 'undefined') {
88
87
  this.manager[name] = this
89
88
  } else {
90
89
  this.warn(`Your model name '${name}' is conflicting, you are only able to
@@ -0,0 +1,73 @@
1
+ let MongoClient = require('mongodb').MongoClient
2
+
3
+ module.exports.open = function(uri, opts, fn) {
4
+ /*
5
+ * Monkey patch to remove db event listener warnings
6
+ * @todo remove when monk is removed
7
+ * @see https://www.mongodb.com/community/forums/t/node-44612-deprecationwarning-listening-to-events-on-
8
+ the-db-class-has-been-deprecated-and-will-be-removed-in-the-next-major-version/15849/4
9
+ */
10
+ var STATE = {
11
+ CLOSED: 'closed',
12
+ OPENING: 'opening',
13
+ OPEN: 'open'
14
+ }
15
+ MongoClient.connect(uri, opts, function (err, client) {
16
+ // this = Manager
17
+ if (err) {
18
+ this._state = STATE.CLOSED
19
+ this.emit('error-opening', err)
20
+ } else {
21
+ this._state = STATE.OPEN
22
+
23
+ this._client = client
24
+ this._db = client.db()
25
+
26
+ // set up events
27
+ var self = this
28
+ ;['authenticated', 'close', 'error', 'parseError', 'timeout'].forEach(function (eventName) {
29
+ self._client.on(eventName, function (e) {
30
+ self.emit(eventName, e)
31
+ })
32
+ })
33
+
34
+ this.emit('open', this._db)
35
+ }
36
+ if (fn) {
37
+ fn(err, this)
38
+ }
39
+ }.bind(this))
40
+ }
41
+
42
+ module.exports.findOneAndUpdate = function(query, update, opts, fn) {
43
+ /*
44
+ * Monkey patch to use returnDocument
45
+ * @todo remove when monk is removed
46
+ * @see https://github.com/Automattic/monk/blob/master/lib/collection.js#L265
47
+ */
48
+ // this = model
49
+ if (typeof opts === 'function') {
50
+ fn = opts
51
+ opts = {}
52
+ }
53
+ return this._dispatch(function findOneAndUpdate(args) {
54
+ var method = 'findOneAndUpdate'
55
+ if (typeof (args.options || {}).returnDocument === 'undefined') {
56
+ args.options.returnDocument = 'after'
57
+ }
58
+ if (args.options.replaceOne | args.options.replace) {
59
+ method = 'findOneAndReplace'
60
+ }
61
+ return args.col[method](args.query, args.update, args.options)
62
+ .then(function (doc) {
63
+ if (doc && typeof doc.value !== 'undefined') {
64
+ return doc.value
65
+ }
66
+ if (doc.ok && doc.lastErrorObject && doc.lastErrorObject.n === 0) {
67
+ return null
68
+ }
69
+ return doc
70
+ })
71
+ })({options: opts, query: query, update: update, callback: fn}, 'findOneAndUpdate')
72
+ }
73
+
package/lib/util.js CHANGED
@@ -140,14 +140,13 @@ module.exports = {
140
140
  return typeof value === 'undefined'
141
141
  },
142
142
 
143
- omit: function(obj, keys) {
144
- let target = {}
145
- for (let i in obj) {
146
- if (keys.indexOf(i) >= 0) continue
147
- if (!Object.prototype.hasOwnProperty.call(obj, i)) continue
148
- target[i] = obj[i]
143
+ omit: function(obj, fields) {
144
+ const shallowCopy = Object.assign({}, obj)
145
+ for (let i=0; i<fields.length; i+=1) {
146
+ const key = fields[i]
147
+ delete shallowCopy[key]
149
148
  }
150
- return target
149
+ return shallowCopy
151
150
  },
152
151
 
153
152
  parseData: function(obj) {
@@ -228,21 +227,24 @@ module.exports = {
228
227
  }
229
228
  },
230
229
 
231
- pluck: function(obj, keys) {
232
- let target = {}
233
- for (let prop in obj) {
234
- let match
235
- if (!Object.prototype.hasOwnProperty.call(obj, prop)) continue
236
- for (let key of keys) {
237
- if (this.isString(key) && key == prop) match = true
238
- else if (this.isRegex(key) && prop.match(key)) match = true
230
+ pick: function(obj, keys) {
231
+ // Similiar to underscore.pick
232
+ // @param {string[] | regex[]} keys
233
+ if (!this.isObject(obj) && !this.isFunction(obj)) return {}
234
+ keys = this.toArray(keys)
235
+ let res = {}
236
+ for (let key of keys) {
237
+ if (this.isString(key) && obj.hasOwnProperty(key)) res[key] = obj[key]
238
+ if (this.isRegex(key)) {
239
+ for (let key2 in obj) {
240
+ if (obj.hasOwnProperty(key2) && key2.match(key)) res[key2] = obj[key2]
241
+ }
239
242
  }
240
- if (match) target[prop] = obj[prop]
241
243
  }
242
- return target
244
+ return res
243
245
  },
244
246
 
245
- removeUndefined: (variable) => {
247
+ removeUndefined: function(variable) {
246
248
  // takes an array or object
247
249
  if (Array.isArray(variable)) {
248
250
  for (let i=variable.length; i--;) {
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.33.0",
5
+ "version": "1.36.0",
6
6
  "license": "MIT",
7
7
  "repository": "github:boycce/monastery",
8
8
  "homepage": "https://boycce.github.io/monastery/",
@@ -1,6 +1,6 @@
1
1
  module.exports = function(monastery, opendb) {
2
2
 
3
- test('find blacklisting', async () => {
3
+ test('find blacklisting basic', async () => {
4
4
  // Setup
5
5
  let db = (await opendb(null)).db
6
6
  let bird = db.model('bird', {
@@ -93,7 +93,7 @@ module.exports = function(monastery, opendb) {
93
93
  }
94
94
  }})
95
95
 
96
- // Test initial blacklist
96
+ // initial blacklist
97
97
  let find1 = await user.findOne({
98
98
  query: user1._id
99
99
  })
@@ -107,18 +107,30 @@ module.exports = function(monastery, opendb) {
107
107
  deepModel: { myBird: bird1._id }
108
108
  })
109
109
 
110
- // Test augmented blacklist
110
+ // augmented blacklist
111
111
  let find2 = await user.findOne({
112
112
  query: user1._id,
113
113
  blacklist: ['pet', 'pet', 'deep', 'deepModel', '-dog', '-animals.cat']
114
114
  })
115
- expect(find2).toEqual({
115
+ let customBlacklist
116
+ expect(find2).toEqual((customBlacklist = {
116
117
  _id: user1._id,
117
118
  dog: 'Bruce',
118
119
  list: [44, 54],
119
120
  pets: [{ name: 'Pluto' }, { name: 'Milo' }],
120
121
  animals: { dog: 'Max', cat: 'Ginger' }
122
+ }))
123
+
124
+ // blacklist string
125
+ let find3 = await user.findOne({
126
+ query: user1._id,
127
+ blacklist: 'pet pet deep deepModel -dog -animals.cat'
121
128
  })
129
+ expect(find3).toEqual(customBlacklist)
130
+
131
+ // blacklist removal
132
+ let find4 = await user.findOne({ query: user1._id, blacklist: false })
133
+ expect(find4).toEqual(user1)
122
134
 
123
135
  db.close()
124
136
  })
@@ -219,6 +231,11 @@ module.exports = function(monastery, opendb) {
219
231
  _id: user1._id,
220
232
  bird5: { ...bird1Base, name: 'ponyo', height: 40 },
221
233
  })
234
+ // blacklist removal
235
+ expect(await user.findOne({ query: user1._id, blacklist: false, populate: ['bird1'] })).toEqual({
236
+ ...user1,
237
+ bird1: { ...bird1Base, height: 40, name: 'ponyo', wing: { size: 1, sizes: { one: 1, two: 1 }} },
238
+ })
222
239
 
223
240
  db.close()
224
241
  })
@@ -243,13 +260,13 @@ module.exports = function(monastery, opendb) {
243
260
  },
244
261
  })
245
262
  // default
246
- expect(db.user._getBlacklistProjection('find')).toEqual({
263
+ expect(db.user._getProjectionFromBlacklist('find')).toEqual({
247
264
  'bird1.wing': 0,
248
265
  'bird1.age': 0,
249
266
  'password': 0,
250
267
  })
251
268
  // blacklist /w invalid field (which goes through)
252
- expect(db.user._getBlacklistProjection('find', ['name', 'invalidfield'])).toEqual({
269
+ expect(db.user._getProjectionFromBlacklist('find', ['name', 'invalidfield'])).toEqual({
253
270
  'bird1.wing': 0,
254
271
  'bird1.age': 0,
255
272
  'invalidfield': 0,
@@ -257,38 +274,38 @@ module.exports = function(monastery, opendb) {
257
274
  'password': 0,
258
275
  })
259
276
  // whitelist
260
- expect(db.user._getBlacklistProjection('find', ['-password', '-bird1.age'])).toEqual({
277
+ expect(db.user._getProjectionFromBlacklist('find', ['-password', '-bird1.age'])).toEqual({
261
278
  'bird1.wing': 0,
262
279
  })
263
280
  // whitelist parent
264
- expect(db.user._getBlacklistProjection('find', ['-bird1'])).toEqual({
281
+ expect(db.user._getProjectionFromBlacklist('find', ['-bird1'])).toEqual({
265
282
  'password': 0,
266
283
  })
267
284
  // whitelist parent, then blacklist child
268
- expect(db.user._getBlacklistProjection('find', ['-bird1', 'bird1.name'])).toEqual({
285
+ expect(db.user._getProjectionFromBlacklist('find', ['-bird1', 'bird1.name'])).toEqual({
269
286
  'password': 0,
270
287
  'bird1.name': 0,
271
288
  })
272
289
  // the model's blacklists are applied after deep model's
273
290
  db.user.findBL = ['-bird1.age']
274
- expect(db.user._getBlacklistProjection('find')).toEqual({
291
+ expect(db.user._getProjectionFromBlacklist('find')).toEqual({
275
292
  'bird1.wing': 0,
276
293
  })
277
294
  // custom blacklists are applied after the model's, which are after deep model's
278
295
  db.user.findBL = ['-bird1.age']
279
- expect(db.user._getBlacklistProjection('find', ['bird1'])).toEqual({
296
+ expect(db.user._getProjectionFromBlacklist('find', ['bird1'])).toEqual({
280
297
  'bird1': 0,
281
298
  })
282
299
  // blacklisted parent with a blacklisted child
283
- expect(db.user._getBlacklistProjection('find', ['bird1', 'bird1.wing'])).toEqual({
300
+ expect(db.user._getProjectionFromBlacklist('find', ['bird1', 'bird1.wing'])).toEqual({
284
301
  'bird1': 0,
285
302
  })
286
303
  // A mess of things
287
- expect(db.user._getBlacklistProjection('find', ['-bird1', 'bird1.wing', '-bird1.wing','bird1.wing.size'])).toEqual({
304
+ expect(db.user._getProjectionFromBlacklist('find', ['-bird1', 'bird1.wing', '-bird1.wing','bird1.wing.size'])).toEqual({
288
305
  'bird1.wing.size': 0,
289
306
  })
290
307
  // blacklisted parent with a whitelisted child (expect blacklist expansion in future version?)
291
- // expect(db.user._getBlacklistProjection('find', ['bird1', '-bird1.wing'])).toEqual({
308
+ // expect(db.user._getProjectionFromBlacklist('find', ['bird1', '-bird1.wing'])).toEqual({
292
309
  // 'bird1.age': 0,
293
310
  // 'bird1.name': 0,
294
311
  // })
@@ -296,7 +313,7 @@ module.exports = function(monastery, opendb) {
296
313
  db.close()
297
314
  })
298
315
 
299
- test('find project', async () => {
316
+ test('find project basic', async () => {
300
317
  // Test mongodb native project option
301
318
  // Setup
302
319
  let db = (await opendb(null)).db
@@ -370,14 +387,14 @@ module.exports = function(monastery, opendb) {
370
387
  color: { type: 'string', default: 'red' },
371
388
  }
372
389
  },
373
- findBL: ['age']
390
+ findBL: ['age'],
374
391
  })
375
392
  let user = db.model('user', {
376
393
  fields: {
377
394
  dog: { type: 'string' },
378
395
  bird: { model: 'bird' },
379
396
  bird2: { model: 'bird' },
380
- bird3: { model: 'bird' }
397
+ bird3: { model: 'bird' },
381
398
  },
382
399
  findBL: [
383
400
  // allll these should be ignored.....?/////
@@ -390,38 +407,47 @@ module.exports = function(monastery, opendb) {
390
407
  name: 'ponyo',
391
408
  age: 3,
392
409
  height: 40,
393
- sub: {}
410
+ sub: {},
394
411
  }})
395
412
  let user1 = await user.insert({ data: {
396
413
  dog: 'Bruce',
397
414
  bird: bird1._id,
398
415
  bird2: bird1._id,
399
- bird3: bird1._id
416
+ bird3: bird1._id,
400
417
  }})
401
418
 
402
- // Test project
419
+ // project
403
420
  let find1 = await user.findOne({
404
421
  query: user1._id,
405
422
  populate: ['bird', 'bird2'],
406
- project: ['bird.age', 'bird2']
423
+ project: ['bird.age', 'bird2'],
407
424
  })
408
425
  expect(find1).toEqual({
409
426
  _id: user1._id,
410
427
  bird: { age: 3 },
411
- bird2: { _id: bird1._id, age: 3, name: 'ponyo', height: 40, color: 'red', sub: { color: 'red' }}
428
+ bird2: { _id: bird1._id, age: 3, name: 'ponyo', height: 40, color: 'red', sub: { color: 'red' }},
412
429
  })
413
430
 
414
- // Test project (different project details)
431
+ // project (different project details)
415
432
  let find2 = await user.findOne({
416
433
  query: user1._id,
417
434
  populate: ['bird', 'bird2'],
418
- project: ['bird', 'bird2.height']
435
+ project: ['bird', 'bird2.height'],
419
436
  })
420
- expect(find2).toEqual({
437
+ let customProject
438
+ expect(find2).toEqual((customProject={
421
439
  _id: user1._id,
422
440
  bird: { _id: bird1._id, age: 3, name: 'ponyo', height: 40, color: 'red', sub: { color: 'red' }},
423
441
  bird2: { height: 40 },
442
+ }))
443
+
444
+ // project string
445
+ let find3 = await user.findOne({
446
+ query: user1._id,
447
+ populate: ['bird', 'bird2'],
448
+ project: 'bird bird2.height',
424
449
  })
450
+ expect(find3).toEqual(customProject)
425
451
 
426
452
  db.close()
427
453
  })
@@ -506,7 +532,8 @@ module.exports = function(monastery, opendb) {
506
532
  '-deep' // blacklist a parent
507
533
  ],
508
534
  })
509
- expect(user2).toEqual({
535
+ let customBlacklist
536
+ expect(user2).toEqual((customBlacklist = {
510
537
  list: [44, 54],
511
538
  dog: 'Bruce',
512
539
  pet: 'Freddy',
@@ -522,6 +549,36 @@ module.exports = function(monastery, opendb) {
522
549
  }
523
550
  }
524
551
  }
552
+ }))
553
+
554
+ // Blacklist string
555
+ let user3 = await user.validate(doc1, {
556
+ blacklist: '-dog -animals.dog pets.name -hiddenList -deep'
557
+ })
558
+ expect(user3).toEqual(customBlacklist)
559
+
560
+ // Blacklist removal
561
+ let user4 = await user.validate(doc1, { blacklist: false })
562
+ expect(user4).toEqual(doc1)
563
+
564
+ // Project whitelist
565
+ let user5 = await user.validate(doc1, {
566
+ project: [
567
+ 'dog',
568
+ 'pets.name',
569
+ 'deep'
570
+ ],
571
+ })
572
+ expect(user5).toEqual({
573
+ dog: 'Bruce',
574
+ pets: [ {name: 'Pluto'}, {name: 'Milo'} ],
575
+ deep: {
576
+ deep2: {
577
+ deep3: {
578
+ deep4: 'hideme'
579
+ }
580
+ }
581
+ }
525
582
  })
526
583
  db.close()
527
584
  })
package/test/crud.js CHANGED
@@ -423,14 +423,72 @@ module.exports = function(monastery, opendb) {
423
423
  db.close()
424
424
  })
425
425
 
426
- test('remove basics', async () => {
426
+ test('findOneAndUpdate basics', async () => {
427
+ // todo: test all findOneAndUpdate options
428
+
427
429
  let db = (await opendb(null)).db
430
+ let dog = db.model('dog', {
431
+ fields: {
432
+ name: { type: 'string', default: 'Scruff' },
433
+ }
434
+ })
428
435
  let user = db.model('user', {
436
+ fields: {
437
+ name: { type: 'string', default: 'Martin' },
438
+ dog: { model: 'dog' },
439
+ }
440
+ })
441
+
442
+ // Returns omitted field after update, i.e. dog
443
+ let dog1 = await dog.insert({ data: {} })
444
+ let user1 = await user.insert({ data: { dog: dog1._id }})
445
+ expect(await user.findOneAndUpdate({ query: { _id: user1._id }, data: { name: 'Martin2' }})).toEqual({
446
+ _id: user1._id,
447
+ name: 'Martin2',
448
+ dog: dog1._id,
449
+ })
450
+
451
+ // Returns omitted field requiring population after update, i.e. dog
452
+ expect(await user.findOneAndUpdate({
453
+ query: { _id: user1._id },
454
+ data: { name: 'Martin2' },
455
+ populate: ['dog'],
456
+ })).toEqual({
457
+ _id: user1._id,
458
+ name: 'Martin2',
459
+ dog: {
460
+ _id: dog1._id,
461
+ name: 'Scruff'
462
+ },
463
+ })
464
+
465
+ // Error finding document to update
466
+ expect(await user.findOneAndUpdate({
467
+ query: { _id: db.id() },
468
+ data: { name: 'Martin2' },
469
+ })).toEqual(null)
470
+
471
+ // Error finding document to update (populate)
472
+ expect(await user.findOneAndUpdate({
473
+ query: { _id: db.id() },
474
+ data: { name: 'Martin2' },
475
+ populate: ['dog'],
476
+ })).toEqual(null)
477
+
478
+ db.close()
479
+ })
480
+
481
+ test('remove basics', async () => {
482
+ let db = (await opendb(null)).db
483
+ let user = db.model('userRemove', {
429
484
  fields: {
430
485
  name: { type: 'string' },
431
486
  },
432
487
  })
433
488
 
489
+ // Clear (incase of failed a test)
490
+ user._remove({}, { multi: true })
491
+
434
492
  // Insert multiple
435
493
  let inserted2 = await user.insert({ data: [{ name: 'Martin' }, { name: 'Martin' }, { name: 'Martin' }]})
436
494
  expect(inserted2).toEqual([
package/test/monk.js CHANGED
@@ -1,6 +1,6 @@
1
1
  module.exports = function(monastery, opendb) {
2
2
 
3
- test('Monk confilicts', async () => {
3
+ test('Monk conflicts', async () => {
4
4
  // Setup
5
5
  let db = (await opendb(false)).db
6
6
  let monkdb = require('monk')(':badconnection', () => {})
package/test/util.js CHANGED
@@ -39,4 +39,11 @@ module.exports = function(monastery, opendb) {
39
39
  expect(db.isId('5ff50fe955da2c00170de734')).toEqual(true)
40
40
  })
41
41
 
42
+ test('utilities arrayWithSchema', async () => {
43
+ let db = (await opendb(false)).db
44
+ let res = db.arrayWithSchema([{ name: { type: 'string' }}], { minLength: 1 })
45
+ expect(res).toContainEqual({ name: { type: 'string' }})
46
+ expect(res.schema).toEqual({ minLength: 1 })
47
+ })
48
+
42
49
  }