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.
@@ -0,0 +1,445 @@
1
+ const { ObjectId } = require('mongodb')
2
+ const monastery = require('../lib/index.js')
3
+ const util = require('../lib/util.js')
4
+
5
+ // Setup/destroy
6
+ let db, userCol, indexCol, indexDropCol
7
+ beforeAll(async () => {
8
+ db = monastery('127.0.0.1/monastery')
9
+ userCol = db.get('users-' + Date.now())
10
+ indexCol = db.get('index-' + Date.now())
11
+ indexDropCol = db.get('index-' + Date.now())
12
+ })
13
+ afterAll(async () => {
14
+ await userCol.drop()
15
+ await indexCol.drop()
16
+ await indexDropCol.drop()
17
+ db.close()
18
+ })
19
+
20
+ // test('createIndex > should accept a field string', async () => {
21
+ // await indexCol.createIndex('name.first')
22
+ // const indexes = await indexCol.indexInformation()
23
+ // expect(indexes['name.first_1']).toBeDefined()
24
+ // })
25
+
26
+ // test('createIndex > should accept space-delimited compound indexes', async () => {
27
+ // await indexCol.createIndex('name last')
28
+ // const indexes = await indexCol.indexInformation()
29
+ // expect(indexes.name_1_last_1).toBeDefined()
30
+ // })
31
+
32
+ // test('createIndex > should accept array compound indexes', async () => {
33
+ // await indexCol.createIndex(['nombre', 'apellido'])
34
+ // const indexes = await indexCol.indexInformation()
35
+ // expect(indexes.nombre_1_apellido_1).toBeDefined()
36
+ // })
37
+
38
+ test('createIndex > should accept an object argument', async () => {
39
+ await indexCol.createIndex({ location: '2dsphere' })
40
+ const indexes = await indexCol.indexInformation()
41
+ expect(indexes.location_2dsphere).toBeDefined()
42
+ })
43
+
44
+ test('createIndex > should accept object compound indexes', async () => {
45
+ await indexCol.createIndex({ up: 1, down: -1 })
46
+ const indexes = await indexCol.indexInformation()
47
+ expect(indexes['up_1_down_-1']).toBeDefined()
48
+ })
49
+
50
+ test('createIndex > should accept options', async () => {
51
+ await indexCol.createIndex({ woot: 1 }, { unique: true })
52
+ const indexes = await indexCol.indexInformation()
53
+ expect(indexes.woot_1).toBeDefined()
54
+ })
55
+
56
+ // test('dropIndex > should accept a field string', async () => {
57
+ // await indexCol.createIndex('name2.first')
58
+ // let indexes = await indexCol.indexInformation()
59
+ // expect(indexes['name2.first_1']).toBeDefined()
60
+
61
+ // await indexCol.dropIndex('name2.first')
62
+ // indexes = await indexCol.indexInformation()
63
+ // expect(indexes['name2.first_1']).toBeUndefined()
64
+ // })
65
+
66
+ // test('dropIndex > should accept space-delimited compound indexes', async () => {
67
+ // await indexCol.createIndex('name2 last')
68
+ // let indexes = await indexCol.indexInformation()
69
+ // expect(indexes.name2_1_last_1).toBeDefined()
70
+
71
+ // await indexCol.dropIndex('name2 last')
72
+ // indexes = await indexCol.indexInformation()
73
+ // expect(indexes.name2_1_last_1).toBeUndefined()
74
+ // })
75
+
76
+ // test('dropIndex > should accept array compound indexes', async () => {
77
+ // await indexCol.createIndex(['nombre2', 'apellido'])
78
+ // let indexes = await indexCol.indexInformation()
79
+ // expect(indexes.nombre2_1_apellido_1).toBeDefined()
80
+
81
+ // await indexCol.dropIndex(['nombre2', 'apellido'])
82
+ // indexes = await indexCol.indexInformation()
83
+ // expect(indexes.nombre2_1_apellido_1).toBeUndefined()
84
+ // })
85
+
86
+ test('dropIndex > should accept object compound indexes', async () => {
87
+ await indexCol.createIndex({ up2: 1, down: -1 })
88
+ let indexes = await indexCol.indexInformation()
89
+ expect(indexes['up2_1_down_-1']).toBeDefined()
90
+
91
+ await indexCol.dropIndex({ up2: 1, down: -1 })
92
+ indexes = await indexCol.indexInformation()
93
+ expect(indexes['up2_1_down_']).toBeUndefined()
94
+ })
95
+
96
+ test('dropIndexes > should drop all indexes', async () => {
97
+ await indexDropCol.createIndex({ up2: 1, down: -1 })
98
+ let indexes = await indexDropCol.indexInformation()
99
+ expect(indexes['up2_1_down_-1']).toBeDefined()
100
+
101
+ await indexDropCol.dropIndexes()
102
+ indexes = await indexDropCol.indexInformation()
103
+ expect(indexes['up2_1_down_']).toBeUndefined()
104
+ })
105
+
106
+
107
+ test('insert > should force callback in next tick', async () => {
108
+ await userCol.insert({ woot: 'a' })
109
+ expect(true).toBeTruthy() // No assertion is made here, just ensuring the test passes
110
+ })
111
+
112
+ test('insert > should give you an object with the _id', async () => {
113
+ const obj = await userCol.insert({ woot: 'b' })
114
+ expect(typeof obj._id).toBe('object')
115
+ expect(obj._id.toHexString).toBeDefined()
116
+ })
117
+
118
+ test('insert > should insert the whole document not just _id', async () => {
119
+ const echo = util.id('5f079e17fbab7b0017b12a3d')
120
+ const res1 = await userCol.insert({ woot: 'b', echo: echo, sub: { woot: 'c' } })
121
+ expect(res1).toEqual({ _id: expect.any(Object), woot: 'b', echo: echo, sub: { woot: 'c' }})
122
+ })
123
+
124
+ test('insert > should return an array if an array was inserted', async () => {
125
+ const docs = await userCol.insert([{ woot: 'c' }, { woot: 'd' }])
126
+ expect(Array.isArray(docs)).toBeTruthy()
127
+ expect(docs.length).toBe(2)
128
+ })
129
+
130
+ test('insert > should not fail when inserting an empty array', async () => {
131
+ const docs = await userCol.insert([])
132
+ expect(Array.isArray(docs)).toBeTruthy()
133
+ expect(docs.length).toBe(0)
134
+ })
135
+
136
+
137
+ test('findOne > should return null if no document', async () => {
138
+ const doc = await userCol.findOne({ nonExistingField: true })
139
+ expect(doc).toBeNull()
140
+ })
141
+
142
+ test('findOne > findOne(undefined) should work', async () => {
143
+ await userCol.insert({ a: 'b', c: 'd', e: 'f' })
144
+ await userCol.findOne()
145
+ expect(true).toBeTruthy() // No assertion is made here, just ensuring the test passes
146
+ })
147
+
148
+ test('findOne > should only provide selected fields', async () => {
149
+ const insertedDoc = await userCol.insert({ a: 'b', c: 'd', e: 'f' })
150
+ const doc = await userCol.findOne(insertedDoc._id, { fields: { a: 1, e: 1 }})
151
+ expect(doc.a).toBe('b')
152
+ expect(doc.e).toBe('f')
153
+ expect(doc.c).toBeUndefined()
154
+ })
155
+
156
+ test('find > should project only specified fields using projection options', async () => {
157
+ await userCol.insert([{ a: 1, b: 1 }, { a: 2, b: 2 }])
158
+ const docs = await userCol.find({}, { projection: { a: 1 }, sort: { _id: -1 } })
159
+ expect(docs[0].a).toBe(2)
160
+ expect(docs[0].b).toBeUndefined()
161
+ expect(docs[1].a).toBe(1)
162
+ expect(docs[1].b).toBeUndefined()
163
+ })
164
+
165
+ test('find > should find with nested query', async () => {
166
+ await userCol.insert([{ nested: { a: 1 } }, { nested: { a: 2 } }])
167
+ const docs = await userCol.find({ 'nested.a': 1 })
168
+ expect(docs.length).toBe(1)
169
+ expect(docs[0].nested.a).toBe(1)
170
+ })
171
+
172
+ test('find > should find with nested array query', async () => {
173
+ await userCol.insert([{ nestedArray: [{ a: 1 }] }, { nestedArray: [{ a: 2 }] }])
174
+ const docs = await userCol.find({ 'nestedArray.a': 1 })
175
+ expect(docs.length).toBe(1)
176
+ expect(docs[0].nestedArray[0].a).toBe(1)
177
+ })
178
+
179
+ test('find > should sort', async () => {
180
+ await userCol.insert([{ sort: true, a: 1, b: 2 }, { sort: true, a: 1, b: 1 }])
181
+ const docs = await userCol.find({ sort: true }, { sort: '-a b' })
182
+ expect(docs[0].b).toBe(1)
183
+ expect(docs[1].b).toBe(2)
184
+ })
185
+
186
+ test('find > should return the raw cursor', async () => {
187
+ const query = { stream: 3 }
188
+ await userCol.insert([{ stream: 3 }, { stream: 3 }, { stream: 3 }, { stream: 3 }])
189
+ const cursor = await userCol.find(query, { rawCursor: true })
190
+ expect(cursor.close).toBeTruthy()
191
+ expect(cursor.next).toBeTruthy()
192
+ cursor.close()
193
+ })
194
+
195
+ test('find > should work with streaming option', async () => {
196
+ const query = { stream: 2 }
197
+ let found = 0
198
+ await userCol.insert([{ stream: 2 }, { stream: 2 }, { stream: 2 }, { stream: 2 }])
199
+ await userCol.find(query, {
200
+ stream: (doc) => {
201
+ expect(doc.stream).toBe(2)
202
+ found++
203
+ },
204
+ })
205
+ expect(found).toBe(4)
206
+ })
207
+
208
+ test('find > should allow stream cursor destroy', async () => {
209
+ const query = { cursor: { $exists: true } }
210
+ let found = 0
211
+ await userCol.insert([{ cursor: true }, { cursor: true }, { cursor: true }, { cursor: true }])
212
+ await userCol.find(query, {
213
+ stream: (doc, { close }) => {
214
+ expect(doc.cursor).not.toBeNull()
215
+ found++
216
+ if (found === 2) close()
217
+ },
218
+ })
219
+ await new Promise((resolve) => setTimeout(resolve, 100))
220
+ expect(found).toBe(2)
221
+ })
222
+
223
+ test('find > should allow stream cursor destroy even when paused', async () => {
224
+ const query = { cursor: { $exists: true } }
225
+ let found = 0
226
+ await userCol.insert([{ cursor: true }, { cursor: true }, { cursor: true }, { cursor: true }])
227
+ await userCol.find(query, {
228
+ stream: (doc, { close, pause, resume }) => {
229
+ pause()
230
+ expect(doc.cursor).not.toBeNull()
231
+ found++
232
+ if (found === 2) return close()
233
+ resume()
234
+ },
235
+ })
236
+ await new Promise((resolve) => setTimeout(resolve, 100))
237
+ expect(found).toBe(2)
238
+ })
239
+
240
+ test('find > stream pause and continue', async () => {
241
+ const query = { stream: 4 }
242
+ await userCol.insert([{ stream: 4 }, { stream: 4 }, { stream: 4 }, { stream: 4 }])
243
+ const start = Date.now()
244
+ let index = 0
245
+ await userCol.find(query, {
246
+ stream: (doc, { pause, resume }) => {
247
+ pause()
248
+ const duration = Date.now() - start + 1 // 1ms case when it runs in the same tick
249
+ expect(duration).toBeGreaterThan(index * 100)
250
+ setTimeout(() => {
251
+ index++
252
+ resume()
253
+ }, 100)
254
+ },
255
+ })
256
+ expect(index).toBe(4)
257
+ const duration = Date.now() - start
258
+ expect(duration).toBeGreaterThan(400)
259
+ })
260
+
261
+
262
+ test('count > should count', async () => {
263
+ let count = await userCol.count({ a: 'counting' })
264
+ expect(count).toBe(0)
265
+
266
+ await userCol.insert({ a: 'counting' })
267
+ count = await userCol.count({ a: 'counting' })
268
+ expect(count).toBe(1)
269
+ })
270
+
271
+ test('count > should not ignore options', async () => {
272
+ let count = await userCol.count({ b: 'counting' })
273
+ expect(count).toBe(0)
274
+
275
+ await userCol.insert([{ b: 'counting' }, { b: 'counting' }, { b: 'counting' }, { b: 'counting' }])
276
+ count = await userCol.count({ b: 'counting' }, { limit: 2 })
277
+ expect(count).toBe(2)
278
+ })
279
+
280
+ test('count > should count with no arguments', async () => {
281
+ let count = await userCol.count({ c: 'counting' })
282
+ expect(count).toBe(0)
283
+
284
+ await userCol.insert({ c: 'counting' })
285
+ count = await userCol.count()
286
+ expect(count).toBe(41)
287
+ })
288
+
289
+ test('count > should estimate count', async () => {
290
+ const count = await userCol.count({}, { estimate: true })
291
+ expect(count).toBe(41)
292
+ })
293
+
294
+ test('count > should estimate count with options', async () => {
295
+ const count = await userCol.count({}, { estimate: true, maxTimeMS: 10000 })
296
+ expect(count).toBe(41)
297
+ })
298
+
299
+ test('distinct', async () => {
300
+ await userCol.insert([{ distinct: 'a' }, { distinct: 'a' }, { distinct: 'b' }])
301
+ const docs = await userCol.distinct('distinct')
302
+ expect(docs).toEqual(['a', 'b'])
303
+ })
304
+
305
+ test('distinct with options', async () => {
306
+ await userCol.insert([{ distinct2: 'a' }, { distinct2: 'a' }, { distinct2: 'b' }])
307
+ const docs = await userCol.distinct('distinct2', {})
308
+ expect(docs).toEqual(['a', 'b'])
309
+ })
310
+
311
+
312
+ test('update > should update', async () => {
313
+ const doc = await userCol.insert({ d: 'e' })
314
+ await userCol.update({ _id: doc._id }, { $set: { d: 'f' } })
315
+ const updatedDoc = await userCol.findOne(doc._id)
316
+ expect(updatedDoc.d).toBe('f')
317
+ })
318
+
319
+ test('update > should update with 0', async () => {
320
+ const doc = await userCol.insert({ d: 'e' })
321
+ await userCol.update({ _id: doc._id }, { $set: { d: 0 } })
322
+ const updatedDoc = await userCol.findOne(doc._id)
323
+ expect(updatedDoc.d).toBe(0)
324
+ })
325
+
326
+ test('update > should update with an objectid', async () => {
327
+ const doc = await userCol.insert({ d: 'e' })
328
+ await userCol.update(doc._id, { $set: { d: 'f' } })
329
+ const updatedDoc = await userCol.findOne(doc._id)
330
+ expect(updatedDoc.d).toBe('f')
331
+ })
332
+
333
+ test('update > should update with an objectid (string)', async () => {
334
+ const doc = await userCol.insert({ d: 'e' })
335
+ await userCol.update(doc._id.toString(), { $set: { d: 'f' } })
336
+ const updatedDoc = await userCol.findOne(doc._id)
337
+ expect(updatedDoc.d).toBe('f')
338
+ })
339
+
340
+ test('update > returned updated object', async () => {
341
+ await userCol.insert({ woot: 'b' })
342
+ const res = await userCol.update({ woot: 'nonyetadded' }, { $set: { woot: 'c' }}, {})
343
+ expect(res).toEqual({
344
+ acknowledged: true,
345
+ matchedCount: 0,
346
+ modifiedCount: 0,
347
+ upsertedCount: 0,
348
+ upsertedId: null,
349
+ })
350
+ const res2 = await userCol.update({ woot: 'nonyetadded' }, { $set: { woot: 'c' }}, { upsert: true })
351
+ expect(res2).toEqual({
352
+ acknowledged: true,
353
+ matchedCount: 0,
354
+ modifiedCount: 0,
355
+ upsertedCount: 1,
356
+ upsertedId: expect.any(ObjectId), // asymmetric helper
357
+ })
358
+ })
359
+
360
+ test('update > mutli', async () => {
361
+ await userCol.insert([{ f: true }, { f: true }, { g: true }, { g: true }])
362
+ const res = await userCol.update({ f: true }, { $set: { f: 'g' } })
363
+ expect(res.matchedCount === 1).toBeTruthy()
364
+ const res2 = await userCol.update({ g: true }, { $set: { g: 'i' } }, { multi: true })
365
+ expect(res2.matchedCount == 2 && res2.modifiedCount == 2).toBeTruthy()
366
+ })
367
+
368
+ test('remove > should remove a document', async () => {
369
+ await userCol.insert({ name: 'Tobi' })
370
+ await userCol.remove({ name: 'Tobi' })
371
+ const result = await userCol.find({ name: 'Tobi' })
372
+ expect(result).toEqual([])
373
+ })
374
+
375
+
376
+ test('findOneAndDelete > should remove a document and return it', async () => {
377
+ await userCol.insert({ name: 'Bob' })
378
+ const deletedDoc = await userCol.findOneAndDelete({ name: 'Bob' })
379
+ expect(deletedDoc.name).toBe('Bob')
380
+ const result = await userCol.find({ name: 'Bob' })
381
+ expect(result).toEqual([])
382
+ })
383
+
384
+ test('findOneAndDelete > should return null if found nothing', async () => {
385
+ const doc = await userCol.findOneAndDelete({ name: 'Bob3' })
386
+ expect(doc).toBeNull()
387
+ })
388
+
389
+ test('findOneAndUpdate > should update a document and return it', async () => {
390
+ await userCol.insert({ name: 'Jack' })
391
+ const updatedDoc = await userCol.findOneAndUpdate({ name: 'Jack' }, { $set: { name: 'Jack4' } })
392
+ expect(updatedDoc.name).toBe('Jack4')
393
+ })
394
+
395
+ test('findOneAndUpdate > should return null if found nothing', async () => {
396
+ const doc = await userCol.findOneAndUpdate({ name: 'Jack5' }, { $set: { name: 'Jack6' } })
397
+ expect(doc).toBeNull()
398
+ })
399
+
400
+ test('findOneAndUpdate > should return an error if no atomic operations are specified', async () => {
401
+ const err = await userCol.findOneAndUpdate({ name: 'Jack5' }, { name: 'Jack6' }).catch(err => err)
402
+ expect(err.message).toBe('Update document requires atomic operators')
403
+ })
404
+
405
+ test('aggregate > should fail properly', async () => {
406
+ await expect(userCol.aggregate(null)).rejects.toThrow()
407
+ })
408
+
409
+ test('aggregate > should work in normal case', async () => {
410
+ const res = await userCol.aggregate([{ $group: { _id: null, maxWoot: { $max: '$woot' } } }])
411
+ expect(Array.isArray(res)).toBeTruthy()
412
+ expect(res.length).toBe(1)
413
+ })
414
+
415
+ test('aggregate > should work with option', async () => {
416
+ const res = await userCol.aggregate([{ $group: { _id: null, maxWoot: { $max: '$woot' } } }], { explain: true })
417
+ expect(Array.isArray(res)).toBeTruthy()
418
+ expect(res.length).toBe(1)
419
+ })
420
+
421
+
422
+ test('bulkWrite', async () => {
423
+ const r = await userCol.bulkWrite([{ insertOne: { document: { bulkWrite: 1 } } }])
424
+ expect(r.nInserted).toBe(1)
425
+ })
426
+
427
+ test('drop > should not throw when dropping an empty db', async () => {
428
+ const result = await db.get('dropDB-' + Date.now()).drop().catch(() => false)
429
+ expect(result).toBeTruthy()
430
+ })
431
+
432
+ test('caching collections', () => {
433
+ const collectionName = 'cached-' + Date.now()
434
+ expect(db.get(collectionName)).toBe(db.get(collectionName))
435
+ })
436
+
437
+ test('not caching collections', () => {
438
+ const collectionName = 'cached-' + Date.now()
439
+ expect(db.get(collectionName, { cache: false })).not.toBe(db.get(collectionName, { cache: false }))
440
+ })
441
+
442
+ test('stats', async () => {
443
+ const res = await userCol.stats()
444
+ expect(res).toBeTruthy()
445
+ })