UNPKG

19.9 kBJavaScriptView Raw
1var t = require('../test-lib/test.js');
2var assert = require('assert');
3var _ = require('@sailshq/lodash');
4var async = require('async');
5var apos;
6
7describe('Docs', function() {
8
9 this.timeout(t.timeout);
10
11 after(function(done) {
12 return t.destroy(apos, done);
13 });
14
15 // EXISTENCE
16
17 it('should be a property of the apos object', function(done) {
18 apos = require('../index.js')({
19 root: module,
20 shortName: 'test',
21
22 modules: {
23 'apostrophe-express': {
24 secret: 'xxx',
25 port: 7900
26 },
27 'test-people': {
28 extend: 'apostrophe-doc-type-manager',
29 name: 'test-person',
30 addFields: [
31 {
32 name: '_friend',
33 type: 'joinByOne',
34 withType: 'test-person',
35 idField: 'friendId',
36 label: 'Friend'
37 }
38 ]
39 }
40 },
41 afterInit: function(callback) {
42 assert(apos.docs);
43 apos.argv._ = [];
44 return callback(null);
45 },
46 afterListen: function(err) {
47 assert(!err);
48 done();
49 }
50 });
51 });
52
53 it('should have a db property', function() {
54 assert(apos.docs.db);
55 });
56
57 /// ///
58 // SETUP
59 /// ///
60
61 it('should make sure all of the expected indexes are configured', function(done) {
62 var expectedIndexes = ['type', 'slug', 'titleSortified', 'tags', 'published'];
63 var actualIndexes = [];
64
65 apos.docs.db.indexInformation(function(err, info) {
66 assert(!err);
67
68 // Extract the actual index info we care about
69 _.each(info, function(index) {
70 actualIndexes.push(index[0][0]);
71 });
72
73 // Now make sure everything in expectedIndexes is in actualIndexes
74 _.each(expectedIndexes, function(index) {
75 assert(_.contains(actualIndexes, index));
76 });
77
78 // Lastly, make sure there is a text index present
79 assert(info.highSearchText_text_lowSearchText_text_title_text_searchBoost_text[0][1] === 'text');
80 done();
81 });
82 });
83
84 it('should make sure there is no test data hanging around from last time', function(done) {
85 // Attempt to purge the entire aposDocs collection
86 apos.docs.db.remove({}, function(err) {
87 assert(!err);
88 // Make sure it went away
89 apos.docs.db.findWithProjection({ slug: 'larry' }).toArray(function(err, docs) {
90 assert(!err);
91 assert(docs.length === 0);
92 done();
93 });
94 });
95 });
96
97 it('should be able to use db to insert documents', function(done) {
98 var testItems = [
99 {
100 _id: 'lori',
101 slug: 'lori',
102 published: true,
103 type: 'test-person',
104 firstName: 'Lori',
105 lastName: 'Pizzaroni',
106 age: 32,
107 alive: true
108 },
109 {
110 _id: 'larry',
111 slug: 'larry',
112 published: true,
113 type: 'test-person',
114 firstName: 'Larry',
115 lastName: 'Cherber',
116 age: 28,
117 alive: true
118 },
119 {
120 _id: 'carl',
121 slug: 'carl',
122 published: true,
123 type: 'test-person',
124 firstName: 'Carl',
125 lastName: 'Sagan',
126 age: 62,
127 alive: false,
128 friendId: 'larry'
129 }
130 ];
131
132 apos.docs.db.insert(testItems, function(err) {
133 assert(!err);
134 done();
135 });
136 });
137
138 it('should be able to carry out schema joins', function(done) {
139
140 var manager = apos.docs.getManager('test-person');
141
142 assert(manager);
143 assert(manager.find);
144 assert(manager.schema);
145
146 var cursor = manager.find(apos.tasks.getAnonReq(), { slug: 'carl' });
147 assert(cursor);
148 cursor.toObject(function(err, person) {
149 assert(!err);
150 assert(person);
151 assert(person.slug === 'carl');
152 assert(person._friend);
153 assert(person._friend.slug === 'larry');
154 done();
155 });
156 });
157
158 /// ///
159 // UNIQUENESS
160 /// ///
161
162 it('should fail if you try to insert a document with the same unique key twice', function(done) {
163 apos.docs.db.insert([
164 {
165 type: 'test-person',
166 published: false,
167 age: 70,
168 slug: 'peter'
169 },
170 {
171 type: 'test-person',
172 published: false,
173 age: 70,
174 slug: 'peter'
175 }
176 ], function(err) {
177 assert(err);
178 done();
179 });
180 });
181
182 /// ///
183 // FINDING
184 /// ///
185
186 it('should have a find method on docs that returns a cursor', function() {
187 var cursor = apos.docs.find(apos.tasks.getAnonReq());
188 assert(cursor);
189 });
190
191 it('should be able to find all PUBLISHED test documents and output them as an array', function(done) {
192 var cursor = apos.docs.find(apos.tasks.getAnonReq(), { type: 'test-person' });
193
194 cursor.toArray(function(err, docs) {
195 assert(!err);
196 // There should be only 3 results.
197 assert(docs.length === 3);
198 // They should all have a type of test-person
199 assert(docs[0].type === 'test-person');
200 done();
201 });
202 });
203
204 it('same thing, but with promises', function(done) {
205 apos.docs.find(apos.tasks.getAnonReq(), { type: 'test-person' }).toArray().then(function(docs) {
206 // There should be only 3 results.
207 assert(docs.length === 3);
208 // They should all have a type of test-person
209 assert(docs[0].type === 'test-person');
210 done();
211 }).catch(function(err) {
212 assert(!err);
213 });
214 });
215
216 /// ///
217 // PROJECTIONS
218 /// ///
219
220 it('should be able to specify which fields to get by passing a projection object', function(done) {
221 var cursor = apos.docs.find(apos.tasks.getAnonReq(), { type: 'test-person' }, { age: 1 });
222 cursor.toArray(function(err, docs) {
223
224 assert(!err);
225 // There SHOULD be an age
226 assert(docs[0].age);
227
228 // There SHOULD NOT be a firstName
229 assert(!docs[0].firstName);
230 done();
231 });
232 });
233
234 /// ///
235 // PUBLISHED vs UNPUBLISHED
236 /// ///
237
238 it('should be that non-admins DO NOT get unpublished docs by default', function(done) {
239 var cursor = apos.docs.find(apos.tasks.getAnonReq(), { type: 'test-person' });
240 cursor.toArray(function(err, docs) {
241 assert(!err);
242 _.each(docs, function(doc) {
243 // There SHOULD NOT be a firstName
244 assert(doc.published);
245 });
246
247 done();
248 });
249 });
250
251 it('should be that non-admins do not get unpublished docs, even if they ask for them', function(done) {
252 var cursor = apos.docs.find(apos.tasks.getAnonReq(), { type: 'test-person' }).published(false);
253 cursor.toArray(function(err, docs) {
254 assert(!err);
255 assert(docs.length === 0);
256 done();
257 });
258 });
259
260 it('should be that admins can get unpublished docs if they ask for them', function(done) {
261 var cursor = apos.docs.find(apos.tasks.getReq(), { type: 'test-person' }).published(false);
262 cursor.toArray(function(err, docs) {
263 assert(!err);
264 assert(!docs[0].published);
265 done();
266 });
267 });
268
269 it('should be that admins can get a mixture of unpublished docs and published docs if they ask', function(done) {
270 var cursor = apos.docs.find(apos.tasks.getReq(), { type: 'test-person' }).published(null);
271 cursor.toArray(function(err, docs) {
272 assert(!err);
273 assert(docs.length === 4);
274 done();
275 });
276 });
277
278 /// ///
279 // SORTING
280 /// ///
281
282 it('should be able to sort', function(done) {
283 var cursor = apos.docs.find(apos.tasks.getAnonReq(), { type: 'test-person' }).sort({ age: 1 });
284 cursor.toArray(function(err, docs) {
285 assert(!err);
286 assert(docs[0].slug === 'larry');
287 done();
288 });
289 });
290
291 it('should be able to sort by multiple keys', function(done) {
292 var cursor = apos.docs.find(apos.tasks.getAnonReq(), { type: 'test-person' }).sort({ firstName: 1, age: 1 });
293 cursor.toArray(function(err, docs) {
294 assert(!err);
295 assert(docs[0].slug === 'carl');
296 assert(docs[1].slug === 'larry');
297 done();
298 });
299 });
300
301 /// ///
302 // INSERTING
303 /// ///
304
305 it('should have an "insert" method that returns a new database object', function(done) {
306 var object = {
307 slug: 'one',
308 published: true,
309 type: 'test-person',
310 firstName: 'Lori',
311 lastName: 'Ferber',
312 age: 15,
313 alive: true
314 };
315
316 apos.docs.insert(apos.tasks.getReq(), object, function(err, object) {
317 assert(!err);
318 assert(object);
319 assert(object._id);
320 done();
321 });
322 });
323
324 it('should be able to insert a new object into the docs collection in the database', function(done) {
325 var cursor = apos.docs.find(apos.tasks.getReq(), { type: 'test-person', slug: 'one' });
326 cursor.toArray(function(err, docs) {
327 assert(!err);
328 assert(docs[0].slug === 'one');
329 done();
330 });
331 });
332
333 it('should append the slug property with a numeral if inserting an object whose slug already exists in the database', function(done) {
334 var object = {
335 slug: 'one',
336 published: true,
337 type: 'test-person',
338 firstName: 'Lilith',
339 lastName: 'Iyapo',
340 age: 29,
341 alive: true
342 };
343
344 apos.docs.insert(apos.tasks.getReq(), object, function(err, object) {
345 assert(!err);
346 assert(object);
347 assert(object.slug.match(/^one\d+$/));
348 done();
349 });
350 });
351
352 it('should not allow you to call the insert method if you are not an admin', function(done) {
353 var object = {
354 slug: 'not-for-you',
355 published: false,
356 type: 'test-person',
357 firstName: 'Darry',
358 lastName: 'Derrber',
359 age: 5,
360 alive: true
361 };
362
363 apos.docs.insert(apos.tasks.getAnonReq(), object, function(err, object) {
364 // did it return an error?
365 assert(err);
366 done();
367 });
368 });
369
370 /// ///
371 // UPDATING
372 /// ///
373
374 it('should have an "update" method on docs that updates an existing database object based on the "_id" porperty', function(done) {
375 apos.docs.find(apos.tasks.getReq(), { slug: 'one' }).toArray(function(err, docs) {
376 assert(!err);
377 // we should have a document
378 assert(docs);
379 // there should be only one document in our results
380 assert(docs.length === 1);
381
382 // grab the object
383 var object = docs[0];
384 // we want update the alive property
385 object.alive = false;
386
387 apos.docs.update(apos.tasks.getReq(), object, function(err, object) {
388 assert(!err);
389 assert(object);
390 // has the property been updated?
391 assert(object.alive === false);
392 done();
393 });
394 });
395 });
396
397 it('should append an updated slug with a numeral if the updated slug already exists', function(done) {
398
399 var cursor = apos.docs.find(apos.tasks.getReq(), { type: 'test-person', slug: 'one' });
400 cursor.toObject(function(err, doc) {
401 assert(!err);
402 assert(doc);
403
404 doc.slug = 'peter';
405
406 apos.docs.update(apos.tasks.getReq(), doc, function(err, doc) {
407 assert(!err);
408 assert(doc);
409 // has the updated slug been appended?
410 assert(doc.slug.match(/^peter\d+$/));
411 done();
412 });
413 });
414 });
415
416 it('same thing, but with promises', function(done) {
417
418 // We need to insert another, we used 'one' up
419 var object = {
420 slug: 'two',
421 published: true,
422 type: 'test-person',
423 firstName: 'Twofy',
424 lastName: 'Twofer',
425 age: 15,
426 alive: true
427 };
428
429 apos.docs.insert(apos.tasks.getReq(), object)
430 .then(function(doc) {
431 var cursor;
432 assert(doc);
433 assert(doc._id);
434 cursor = apos.docs.find(apos.tasks.getReq(), { type: 'test-person', slug: 'two' });
435 return cursor.toObject();
436 })
437 .then(function(doc) {
438 assert(doc);
439 doc.slug = 'peter';
440 return apos.docs.update(apos.tasks.getReq(), doc);
441 })
442 .then(function(doc) {
443 assert(doc);
444 // has the updated slug been appended?
445 assert(doc.slug.match(/^peter\d+$/));
446 done();
447 })
448 .catch(function(err) {
449 assert(!err);
450 });
451 });
452
453 it('should be able to fetch all unique firstNames with toDistinct', function() {
454 return apos.docs.find(apos.tasks.getReq(), { type: 'test-person' }).toDistinct('firstName')
455 .then(function(firstNames) {
456 assert(Array.isArray(firstNames));
457 assert(firstNames.length === 5);
458 assert(_.contains(firstNames, 'Larry'));
459 });
460 });
461
462 it('should be able to fetch all unique firstNames and their counts with toDistinct and distinctCounts', function() {
463 var cursor = apos.docs.find(apos.tasks.getReq(), { type: 'test-person' }).distinctCounts(true);
464 return cursor.toDistinct('firstName')
465 .then(function(firstNames) {
466 assert(Array.isArray(firstNames));
467 assert(firstNames.length === 5);
468 assert(_.contains(firstNames, 'Larry'));
469 var counts = cursor.get('distinctCounts');
470 assert(counts['Larry'] === 1);
471 assert(counts['Lori'] === 2);
472 });
473 });
474
475 it('should not allow you to call the update method if you are not an admin', function(done) {
476 var cursor = apos.docs.find(apos.tasks.getAnonReq(), { type: 'test-person', slug: 'lori' });
477 cursor.toObject(function(err, doc) {
478 assert(!err);
479 assert(doc);
480
481 doc.slug = 'laurie';
482
483 apos.docs.update(apos.tasks.getAnonReq(), doc, function(err, doc) {
484 // did it return an error?
485 assert(err);
486 done();
487 });
488 });
489 });
490
491 /// ///
492 // TRASH
493 /// ///
494
495 it('should have a "trash" method on docs', function(done) {
496 apos.docs.trash(apos.tasks.getReq(), { slug: 'carl' }, function(err) {
497 assert(!err);
498 done();
499 });
500 });
501
502 it('should not be able to find the trashed object', function(done) {
503 apos.docs.find(apos.tasks.getReq(), { slug: 'carl' }).toObject(function(err, doc) {
504 assert(!err);
505 // we should not have a document
506 assert(!doc);
507 done();
508 });
509 });
510
511 it('should not allow you to call the trash method if you are not an admin', function(done) {
512 apos.docs.trash(apos.tasks.getAnonReq(), { slug: 'lori' }, function(err) {
513 assert(err);
514 done();
515 });
516 });
517
518 it('should be able to find the trashed object when using the "trash" method on find()', function(done) {
519 apos.docs.find(apos.tasks.getReq(), { slug: 'carl' }).trash(true).toObject(function(err, doc) {
520 assert(!err);
521 // we should have a document
522 assert(doc);
523 done();
524 });
525 });
526
527 /// ///
528 // RESCUE
529 /// ///
530
531 it('should have a "rescue" method on docs that removes the "trash" property from an object', function(done) {
532 apos.docs.rescue(apos.tasks.getReq(), { slug: 'carl' }, function(err) {
533 assert(!err);
534 apos.docs.find(apos.tasks.getReq(), { slug: 'carl' }).toObject(function(err, doc) {
535 assert(!err);
536 // we should have a document
537 assert(doc);
538 done();
539 });
540 });
541 });
542
543 it('should not allow you to call the rescue method if you are not an admin', function(done) {
544 apos.docs.rescue(apos.tasks.getAnonReq(), { slug: 'carl' }, function(err) {
545 // was there an error?
546 assert(err);
547 done();
548 });
549 });
550
551 /// ///
552 // EMPTY TRASH
553 /// ///
554
555 it('should have an "deleteFromTrash" method on docs that removes specified objects from the database which have a "trash" property', function(done) {
556
557 return async.series({
558 trashCarl: function(callback) {
559 return apos.docs.trash(apos.tasks.getReq(), { slug: 'carl' }, function(err) {
560 assert(!err);
561 return callback(null);
562 });
563 },
564 deleteFromTrash: function(callback) {
565 return apos.docs.deleteFromTrash(apos.tasks.getReq(), {}, function(err) {
566 assert(!err);
567 return callback(null);
568 });
569 },
570 find: function(callback) {
571 return apos.docs.find(apos.tasks.getReq(), { slug: 'carl' }).trash(true).toObject(function(err, doc) {
572 assert(!err);
573 // we should not have a document
574 assert(!doc);
575 return callback(null);
576 });
577 }
578 }, done);
579 });
580
581 it('should not allow you to call the deleteFromTrash method if you are not an admin', function(done) {
582 return async.series({
583 trashLarry: function(callback) {
584 return apos.docs.trash(apos.tasks.getReq(), { slug: 'larry' }, function(err) {
585 assert(!err);
586 return callback(null);
587 });
588 },
589 deleteFromTrash: function(callback) {
590 apos.docs.deleteFromTrash(apos.tasks.getAnonReq(), {}, function(err) {
591 assert(!err);
592 return callback(null);
593 });
594 },
595 find: function(callback) {
596 return apos.docs.find(apos.tasks.getReq(), { slug: 'larry' }).trash(true).toObject(function(err, doc) {
597 assert(!err);
598 // we should have a document
599 assert(doc);
600 callback(null);
601 });
602 }
603 }, done);
604 });
605
606 it('should throw an exception on find() if you fail to pass req as the first argument', function() {
607 var exception;
608 try {
609 apos.docs.find({ slug: 'larry' });
610 } catch (e) {
611 exception = e;
612 }
613 assert(exception);
614 });
615
616 it('should respect explicitOrder()', function(done) {
617
618 var testItems = [];
619 var i;
620 for (i = 0; (i < 100); i++) {
621 testItems.push({
622 _id: 'i' + i,
623 slug: 'i' + i,
624 published: true,
625 type: 'test',
626 title: 'title: ' + i
627 });
628 }
629
630 return apos.docs.db.insert(testItems, function(err) {
631 assert(!err);
632 return apos.docs.find(apos.tasks.getAnonReq(), {}).explicitOrder([ 'i7', 'i3', 'i27', 'i9' ]).toArray(function(err, docs) {
633 assert(!err);
634 assert(docs[0]._id === 'i7');
635 assert(docs[1]._id === 'i3');
636 assert(docs[2]._id === 'i27');
637 assert(docs[3]._id === 'i9');
638 assert(!docs[4]);
639 return done();
640 });
641 });
642
643 });
644
645 it('should respect explicitOrder with skip and limit', function(done) {
646
647 // Relies on test data of previous test
648 return apos.docs.find(apos.tasks.getAnonReq(), {}).explicitOrder([ 'i7', 'i3', 'i27', 'i9' ]).skip(2).limit(2).toArray(function(err, docs) {
649 assert(!err);
650 assert(docs[0]._id === 'i27');
651 assert(docs[1]._id === 'i9');
652 assert(!docs[2]);
653 return done();
654 });
655
656 });
657
658 it('should be able to lock a document', function(done) {
659 var req = apos.tasks.getReq();
660 apos.docs.lock(req, 'i27', 'abc', function(err) {
661 assert(!err);
662 done();
663 });
664 });
665
666 it('should not be able to lock a document with a different contextId', function(done) {
667 var req = apos.tasks.getReq();
668 apos.docs.lock(req, 'i27', 'def', function(err) {
669 assert(err);
670 assert(err === 'locked');
671 done();
672 });
673 });
674
675 it('should be able to unlock a document', function(done) {
676 var req = apos.tasks.getReq();
677 apos.docs.unlock(req, 'i27', 'abc', function(err) {
678 assert(!err);
679 done();
680 });
681 });
682
683 it('should be able to re-lock an unlocked document', function(done) {
684 var req = apos.tasks.getReq();
685 apos.docs.lock(req, 'i27', 'def', function(err) {
686 assert(!err);
687 done();
688 });
689 });
690
691 it('should be able to lock a locked document with force: true', function(done) {
692 var req = apos.tasks.getReq();
693 apos.docs.lock(req, 'i27', 'abc', { force: true }, function(err) {
694 assert(!err);
695 done();
696 });
697 });
698
699 it('should be able to unlock all documents locked with the same contextId', function(done) {
700 var req = apos.tasks.getReq();
701 apos.docs.lock(req, 'i26', 'abc', function(err) {
702 assert(!err);
703 apos.docs.lock(req, 'i25', 'abc', function(err) {
704 assert(!err);
705 apos.docs.unlockAll(req, 'abc', function(err) {
706 assert(!err);
707 apos.docs.lock(req, 'i26', 'def', function(err) {
708 assert(!err);
709 done();
710 });
711 });
712 });
713 });
714 });
715
716});