apostrophe
Version:
The Apostrophe Content Management System.
717 lines (625 loc) • 19.9 kB
JavaScript
var t = require('../test-lib/test.js');
var assert = require('assert');
var _ = require('@sailshq/lodash');
var async = require('async');
var apos;
describe('Docs', function() {
this.timeout(t.timeout);
after(function(done) {
return t.destroy(apos, done);
});
// EXISTENCE
it('should be a property of the apos object', function(done) {
apos = require('../index.js')({
root: module,
shortName: 'test',
modules: {
'apostrophe-express': {
secret: 'xxx',
port: 7900
},
'test-people': {
extend: 'apostrophe-doc-type-manager',
name: 'test-person',
addFields: [
{
name: '_friend',
type: 'joinByOne',
withType: 'test-person',
idField: 'friendId',
label: 'Friend'
}
]
}
},
afterInit: function(callback) {
assert(apos.docs);
apos.argv._ = [];
return callback(null);
},
afterListen: function(err) {
assert(!err);
done();
}
});
});
it('should have a db property', function() {
assert(apos.docs.db);
});
/// ///
// SETUP
/// ///
it('should make sure all of the expected indexes are configured', function(done) {
var expectedIndexes = ['type', 'slug', 'titleSortified', 'tags', 'published'];
var actualIndexes = [];
apos.docs.db.indexInformation(function(err, info) {
assert(!err);
// Extract the actual index info we care about
_.each(info, function(index) {
actualIndexes.push(index[0][0]);
});
// Now make sure everything in expectedIndexes is in actualIndexes
_.each(expectedIndexes, function(index) {
assert(_.contains(actualIndexes, index));
});
// Lastly, make sure there is a text index present
assert(info.highSearchText_text_lowSearchText_text_title_text_searchBoost_text[0][1] === 'text');
done();
});
});
it('should make sure there is no test data hanging around from last time', function(done) {
// Attempt to purge the entire aposDocs collection
apos.docs.db.remove({}, function(err) {
assert(!err);
// Make sure it went away
apos.docs.db.findWithProjection({ slug: 'larry' }).toArray(function(err, docs) {
assert(!err);
assert(docs.length === 0);
done();
});
});
});
it('should be able to use db to insert documents', function(done) {
var testItems = [
{
_id: 'lori',
slug: 'lori',
published: true,
type: 'test-person',
firstName: 'Lori',
lastName: 'Pizzaroni',
age: 32,
alive: true
},
{
_id: 'larry',
slug: 'larry',
published: true,
type: 'test-person',
firstName: 'Larry',
lastName: 'Cherber',
age: 28,
alive: true
},
{
_id: 'carl',
slug: 'carl',
published: true,
type: 'test-person',
firstName: 'Carl',
lastName: 'Sagan',
age: 62,
alive: false,
friendId: 'larry'
}
];
apos.docs.db.insert(testItems, function(err) {
assert(!err);
done();
});
});
it('should be able to carry out schema joins', function(done) {
var manager = apos.docs.getManager('test-person');
assert(manager);
assert(manager.find);
assert(manager.schema);
var cursor = manager.find(apos.tasks.getAnonReq(), { slug: 'carl' });
assert(cursor);
cursor.toObject(function(err, person) {
assert(!err);
assert(person);
assert(person.slug === 'carl');
assert(person._friend);
assert(person._friend.slug === 'larry');
done();
});
});
/// ///
// UNIQUENESS
/// ///
it('should fail if you try to insert a document with the same unique key twice', function(done) {
apos.docs.db.insert([
{
type: 'test-person',
published: false,
age: 70,
slug: 'peter'
},
{
type: 'test-person',
published: false,
age: 70,
slug: 'peter'
}
], function(err) {
assert(err);
done();
});
});
/// ///
// FINDING
/// ///
it('should have a find method on docs that returns a cursor', function() {
var cursor = apos.docs.find(apos.tasks.getAnonReq());
assert(cursor);
});
it('should be able to find all PUBLISHED test documents and output them as an array', function(done) {
var cursor = apos.docs.find(apos.tasks.getAnonReq(), { type: 'test-person' });
cursor.toArray(function(err, docs) {
assert(!err);
// There should be only 3 results.
assert(docs.length === 3);
// They should all have a type of test-person
assert(docs[0].type === 'test-person');
done();
});
});
it('same thing, but with promises', function(done) {
apos.docs.find(apos.tasks.getAnonReq(), { type: 'test-person' }).toArray().then(function(docs) {
// There should be only 3 results.
assert(docs.length === 3);
// They should all have a type of test-person
assert(docs[0].type === 'test-person');
done();
}).catch(function(err) {
assert(!err);
});
});
/// ///
// PROJECTIONS
/// ///
it('should be able to specify which fields to get by passing a projection object', function(done) {
var cursor = apos.docs.find(apos.tasks.getAnonReq(), { type: 'test-person' }, { age: 1 });
cursor.toArray(function(err, docs) {
assert(!err);
// There SHOULD be an age
assert(docs[0].age);
// There SHOULD NOT be a firstName
assert(!docs[0].firstName);
done();
});
});
/// ///
// PUBLISHED vs UNPUBLISHED
/// ///
it('should be that non-admins DO NOT get unpublished docs by default', function(done) {
var cursor = apos.docs.find(apos.tasks.getAnonReq(), { type: 'test-person' });
cursor.toArray(function(err, docs) {
assert(!err);
_.each(docs, function(doc) {
// There SHOULD NOT be a firstName
assert(doc.published);
});
done();
});
});
it('should be that non-admins do not get unpublished docs, even if they ask for them', function(done) {
var cursor = apos.docs.find(apos.tasks.getAnonReq(), { type: 'test-person' }).published(false);
cursor.toArray(function(err, docs) {
assert(!err);
assert(docs.length === 0);
done();
});
});
it('should be that admins can get unpublished docs if they ask for them', function(done) {
var cursor = apos.docs.find(apos.tasks.getReq(), { type: 'test-person' }).published(false);
cursor.toArray(function(err, docs) {
assert(!err);
assert(!docs[0].published);
done();
});
});
it('should be that admins can get a mixture of unpublished docs and published docs if they ask', function(done) {
var cursor = apos.docs.find(apos.tasks.getReq(), { type: 'test-person' }).published(null);
cursor.toArray(function(err, docs) {
assert(!err);
assert(docs.length === 4);
done();
});
});
/// ///
// SORTING
/// ///
it('should be able to sort', function(done) {
var cursor = apos.docs.find(apos.tasks.getAnonReq(), { type: 'test-person' }).sort({ age: 1 });
cursor.toArray(function(err, docs) {
assert(!err);
assert(docs[0].slug === 'larry');
done();
});
});
it('should be able to sort by multiple keys', function(done) {
var cursor = apos.docs.find(apos.tasks.getAnonReq(), { type: 'test-person' }).sort({ firstName: 1, age: 1 });
cursor.toArray(function(err, docs) {
assert(!err);
assert(docs[0].slug === 'carl');
assert(docs[1].slug === 'larry');
done();
});
});
/// ///
// INSERTING
/// ///
it('should have an "insert" method that returns a new database object', function(done) {
var object = {
slug: 'one',
published: true,
type: 'test-person',
firstName: 'Lori',
lastName: 'Ferber',
age: 15,
alive: true
};
apos.docs.insert(apos.tasks.getReq(), object, function(err, object) {
assert(!err);
assert(object);
assert(object._id);
done();
});
});
it('should be able to insert a new object into the docs collection in the database', function(done) {
var cursor = apos.docs.find(apos.tasks.getReq(), { type: 'test-person', slug: 'one' });
cursor.toArray(function(err, docs) {
assert(!err);
assert(docs[0].slug === 'one');
done();
});
});
it('should append the slug property with a numeral if inserting an object whose slug already exists in the database', function(done) {
var object = {
slug: 'one',
published: true,
type: 'test-person',
firstName: 'Harry',
lastName: 'Gerber',
age: 29,
alive: true
};
apos.docs.insert(apos.tasks.getReq(), object, function(err, object) {
assert(!err);
assert(object);
assert(object.slug.match(/^one\d+$/));
done();
});
});
it('should not allow you to call the insert method if you are not an admin', function(done) {
var object = {
slug: 'not-for-you',
published: false,
type: 'test-person',
firstName: 'Darry',
lastName: 'Derrber',
age: 5,
alive: true
};
apos.docs.insert(apos.tasks.getAnonReq(), object, function(err, object) {
// did it return an error?
assert(err);
done();
});
});
/// ///
// UPDATING
/// ///
it('should have an "update" method on docs that updates an existing database object based on the "_id" porperty', function(done) {
apos.docs.find(apos.tasks.getReq(), { slug: 'one' }).toArray(function(err, docs) {
assert(!err);
// we should have a document
assert(docs);
// there should be only one document in our results
assert(docs.length === 1);
// grab the object
var object = docs[0];
// we want update the alive property
object.alive = false;
apos.docs.update(apos.tasks.getReq(), object, function(err, object) {
assert(!err);
assert(object);
// has the property been updated?
assert(object.alive === false);
done();
});
});
});
it('should append an updated slug with a numeral if the updated slug already exists', function(done) {
var cursor = apos.docs.find(apos.tasks.getReq(), { type: 'test-person', slug: 'one' });
cursor.toObject(function(err, doc) {
assert(!err);
assert(doc);
doc.slug = 'peter';
apos.docs.update(apos.tasks.getReq(), doc, function(err, doc) {
assert(!err);
assert(doc);
// has the updated slug been appended?
assert(doc.slug.match(/^peter\d+$/));
done();
});
});
});
it('same thing, but with promises', function(done) {
// We need to insert another, we used 'one' up
var object = {
slug: 'two',
published: true,
type: 'test-person',
firstName: 'Twofy',
lastName: 'Twofer',
age: 15,
alive: true
};
apos.docs.insert(apos.tasks.getReq(), object)
.then(function(doc) {
var cursor;
assert(doc);
assert(doc._id);
cursor = apos.docs.find(apos.tasks.getReq(), { type: 'test-person', slug: 'two' });
return cursor.toObject();
})
.then(function(doc) {
assert(doc);
doc.slug = 'peter';
return apos.docs.update(apos.tasks.getReq(), doc);
})
.then(function(doc) {
assert(doc);
// has the updated slug been appended?
assert(doc.slug.match(/^peter\d+$/));
done();
})
.catch(function(err) {
assert(!err);
});
});
it('should be able to fetch all unique firstNames with toDistinct', function() {
return apos.docs.find(apos.tasks.getReq(), { type: 'test-person' }).toDistinct('firstName')
.then(function(firstNames) {
assert(Array.isArray(firstNames));
assert(firstNames.length === 5);
assert(_.contains(firstNames, 'Larry'));
});
});
it('should be able to fetch all unique firstNames and their counts with toDistinct and distinctCounts', function() {
var cursor = apos.docs.find(apos.tasks.getReq(), { type: 'test-person' }).distinctCounts(true);
return cursor.toDistinct('firstName')
.then(function(firstNames) {
assert(Array.isArray(firstNames));
assert(firstNames.length === 5);
assert(_.contains(firstNames, 'Larry'));
var counts = cursor.get('distinctCounts');
assert(counts['Larry'] === 1);
assert(counts['Lori'] === 2);
});
});
it('should not allow you to call the update method if you are not an admin', function(done) {
var cursor = apos.docs.find(apos.tasks.getAnonReq(), { type: 'test-person', slug: 'lori' });
cursor.toObject(function(err, doc) {
assert(!err);
assert(doc);
doc.slug = 'laurie';
apos.docs.update(apos.tasks.getAnonReq(), doc, function(err, doc) {
// did it return an error?
assert(err);
done();
});
});
});
/// ///
// TRASH
/// ///
it('should have a "trash" method on docs', function(done) {
apos.docs.trash(apos.tasks.getReq(), { slug: 'carl' }, function(err) {
assert(!err);
done();
});
});
it('should not be able to find the trashed object', function(done) {
apos.docs.find(apos.tasks.getReq(), { slug: 'carl' }).toObject(function(err, doc) {
assert(!err);
// we should not have a document
assert(!doc);
done();
});
});
it('should not allow you to call the trash method if you are not an admin', function(done) {
apos.docs.trash(apos.tasks.getAnonReq(), { slug: 'lori' }, function(err) {
assert(err);
done();
});
});
it('should be able to find the trashed object when using the "trash" method on find()', function(done) {
apos.docs.find(apos.tasks.getReq(), { slug: 'carl' }).trash(true).toObject(function(err, doc) {
assert(!err);
// we should have a document
assert(doc);
done();
});
});
/// ///
// RESCUE
/// ///
it('should have a "rescue" method on docs that removes the "trash" property from an object', function(done) {
apos.docs.rescue(apos.tasks.getReq(), { slug: 'carl' }, function(err) {
assert(!err);
apos.docs.find(apos.tasks.getReq(), { slug: 'carl' }).toObject(function(err, doc) {
assert(!err);
// we should have a document
assert(doc);
done();
});
});
});
it('should not allow you to call the rescue method if you are not an admin', function(done) {
apos.docs.rescue(apos.tasks.getAnonReq(), { slug: 'carl' }, function(err) {
// was there an error?
assert(err);
done();
});
});
/// ///
// EMPTY TRASH
/// ///
it('should have an "deleteFromTrash" method on docs that removes specified objects from the database which have a "trash" property', function(done) {
return async.series({
trashCarl: function(callback) {
return apos.docs.trash(apos.tasks.getReq(), { slug: 'carl' }, function(err) {
assert(!err);
return callback(null);
});
},
deleteFromTrash: function(callback) {
return apos.docs.deleteFromTrash(apos.tasks.getReq(), {}, function(err) {
assert(!err);
return callback(null);
});
},
find: function(callback) {
return apos.docs.find(apos.tasks.getReq(), { slug: 'carl' }).trash(true).toObject(function(err, doc) {
assert(!err);
// we should not have a document
assert(!doc);
return callback(null);
});
}
}, done);
});
it('should not allow you to call the deleteFromTrash method if you are not an admin', function(done) {
return async.series({
trashLarry: function(callback) {
return apos.docs.trash(apos.tasks.getReq(), { slug: 'larry' }, function(err) {
assert(!err);
return callback(null);
});
},
deleteFromTrash: function(callback) {
apos.docs.deleteFromTrash(apos.tasks.getAnonReq(), {}, function(err) {
assert(!err);
return callback(null);
});
},
find: function(callback) {
return apos.docs.find(apos.tasks.getReq(), { slug: 'larry' }).trash(true).toObject(function(err, doc) {
assert(!err);
// we should have a document
assert(doc);
callback(null);
});
}
}, done);
});
it('should throw an exception on find() if you fail to pass req as the first argument', function() {
var exception;
try {
apos.docs.find({ slug: 'larry' });
} catch (e) {
exception = e;
}
assert(exception);
});
it('should respect explicitOrder()', function(done) {
var testItems = [];
var i;
for (i = 0; (i < 100); i++) {
testItems.push({
_id: 'i' + i,
slug: 'i' + i,
published: true,
type: 'test',
title: 'title: ' + i
});
}
return apos.docs.db.insert(testItems, function(err) {
assert(!err);
return apos.docs.find(apos.tasks.getAnonReq(), {}).explicitOrder([ 'i7', 'i3', 'i27', 'i9' ]).toArray(function(err, docs) {
assert(!err);
assert(docs[0]._id === 'i7');
assert(docs[1]._id === 'i3');
assert(docs[2]._id === 'i27');
assert(docs[3]._id === 'i9');
assert(!docs[4]);
return done();
});
});
});
it('should respect explicitOrder with skip and limit', function(done) {
// Relies on test data of previous test
return apos.docs.find(apos.tasks.getAnonReq(), {}).explicitOrder([ 'i7', 'i3', 'i27', 'i9' ]).skip(2).limit(2).toArray(function(err, docs) {
assert(!err);
assert(docs[0]._id === 'i27');
assert(docs[1]._id === 'i9');
assert(!docs[2]);
return done();
});
});
it('should be able to lock a document', function(done) {
var req = apos.tasks.getReq();
apos.docs.lock(req, 'i27', 'abc', function(err) {
assert(!err);
done();
});
});
it('should not be able to lock a document with a different contextId', function(done) {
var req = apos.tasks.getReq();
apos.docs.lock(req, 'i27', 'def', function(err) {
assert(err);
assert(err === 'locked');
done();
});
});
it('should be able to unlock a document', function(done) {
var req = apos.tasks.getReq();
apos.docs.unlock(req, 'i27', 'abc', function(err) {
assert(!err);
done();
});
});
it('should be able to re-lock an unlocked document', function(done) {
var req = apos.tasks.getReq();
apos.docs.lock(req, 'i27', 'def', function(err) {
assert(!err);
done();
});
});
it('should be able to lock a locked document with force: true', function(done) {
var req = apos.tasks.getReq();
apos.docs.lock(req, 'i27', 'abc', { force: true }, function(err) {
assert(!err);
done();
});
});
it('should be able to unlock all documents locked with the same contextId', function(done) {
var req = apos.tasks.getReq();
apos.docs.lock(req, 'i26', 'abc', function(err) {
assert(!err);
apos.docs.lock(req, 'i25', 'abc', function(err) {
assert(!err);
apos.docs.unlockAll(req, 'abc', function(err) {
assert(!err);
apos.docs.lock(req, 'i26', 'def', function(err) {
assert(!err);
done();
});
});
});
});
});
});