mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-01 13:54:35 +03:00
Implement user and related content deletion
Closes #3100 * Introduces `destroyByAuhor`, given a context and an id, it will check if context has permission to delete the user by the id, and then deletes all the content where `author_id` is id, and then deletes the user * Does multiple checks to make sure user exists * Added a fixture `posts:mu` that creates 4 users belonging to 4 roles, 50 posts that have authors evenly distributed, 5 tags and all 50 have one tag attached to it, evenly distributed. Caveats / questions * Started testing
This commit is contained in:
parent
9f21730648
commit
8487dada0b
@ -255,9 +255,22 @@ users = {
|
||||
destroy: function destroy(options) {
|
||||
return canThis(options.context).destroy.user(options.id).then(function () {
|
||||
return users.read(options).then(function (result) {
|
||||
return dataProvider.User.destroy(options).then(function () {
|
||||
return dataProvider.Base.transaction(function (t) {
|
||||
options.transacting = t;
|
||||
dataProvider.Post.destroyByAuthor(options).then(function () {
|
||||
return dataProvider.User.destroy(options);
|
||||
}).then(function () {
|
||||
t.commit();
|
||||
}).catch(function (error) {
|
||||
t.rollback(error);
|
||||
});
|
||||
}).then(function () {
|
||||
return result;
|
||||
}, function (error) {
|
||||
return when.reject(new errors.InternalServerError(error));
|
||||
});
|
||||
}, function (error) {
|
||||
return errors.handleAPIError(error);
|
||||
});
|
||||
}).catch(function (error) {
|
||||
return errors.handleAPIError(error);
|
||||
|
@ -521,6 +521,31 @@ Post = ghostBookshelf.Model.extend({
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* ### destroyByAuthor
|
||||
* @param {[type]} options has context and id. Context is the user doing the destroy, id is the user to destroy
|
||||
*/
|
||||
destroyByAuthor: function (options) {
|
||||
var postCollection = Posts.forge(),
|
||||
authorId = options.id;
|
||||
|
||||
options = this.filterOptions(options, 'destroyByAuthor');
|
||||
if (authorId) {
|
||||
return postCollection.query('where', 'author_id', '=', authorId).fetch(options).then(function (results) {
|
||||
return when.map(results.models, function (post) {
|
||||
return post.related('tags').detach(null, options).then(function () {
|
||||
return post.destroy(options);
|
||||
});
|
||||
});
|
||||
}, function (error) {
|
||||
return when.reject(new errors.InternalServerError(error.message || error));
|
||||
});
|
||||
}
|
||||
return when.reject(new errors.NotFoundError('No user found'));
|
||||
},
|
||||
|
||||
|
||||
permissible: function (postModelOrId, action, context, loadedPermissions, hasUserPermission, hasAppPermission) {
|
||||
var self = this,
|
||||
postModel = postModelOrId,
|
||||
|
@ -745,7 +745,7 @@ User = ghostBookshelf.Model.extend({
|
||||
contextUser = ctxUser;
|
||||
return User.findOne({id: object.id});
|
||||
}).then(function (user) {
|
||||
|
||||
|
||||
var currentRoles = user.toJSON().roles;
|
||||
if (!_.contains(currentRoles, adminRole.id)) {
|
||||
return when.reject(new errors.ValidationError('Only administrators can be assigned the owner role.'));
|
||||
|
@ -12,70 +12,57 @@ var testUtils = require('../../utils'),
|
||||
|
||||
describe('Post Model', function () {
|
||||
// Keep the DB clean
|
||||
before(testUtils.teardown);
|
||||
afterEach(testUtils.teardown);
|
||||
beforeEach(testUtils.setup('owner', 'posts', 'apps'));
|
||||
|
||||
should.exist(PostModel);
|
||||
describe('Single author posts', function () {
|
||||
|
||||
function checkFirstPostData(firstPost) {
|
||||
should.not.exist(firstPost.author_id);
|
||||
firstPost.author.should.be.an.Object;
|
||||
firstPost.fields.should.be.an.Array;
|
||||
firstPost.tags.should.be.an.Array;
|
||||
firstPost.author.name.should.equal(DataGenerator.Content.users[0].name);
|
||||
firstPost.fields[0].key.should.equal(DataGenerator.Content.app_fields[0].key);
|
||||
firstPost.created_at.should.be.an.instanceof(Date);
|
||||
firstPost.created_by.should.be.an.Object;
|
||||
firstPost.updated_by.should.be.an.Object;
|
||||
firstPost.published_by.should.be.an.Object;
|
||||
firstPost.created_by.name.should.equal(DataGenerator.Content.users[0].name);
|
||||
firstPost.updated_by.name.should.equal(DataGenerator.Content.users[0].name);
|
||||
firstPost.published_by.name.should.equal(DataGenerator.Content.users[0].name);
|
||||
firstPost.tags[0].name.should.equal(DataGenerator.Content.tags[0].name);
|
||||
}
|
||||
before(testUtils.teardown);
|
||||
afterEach(testUtils.teardown);
|
||||
beforeEach(testUtils.setup('owner', 'posts', 'apps'));
|
||||
|
||||
it('can findAll', function (done) {
|
||||
PostModel.findAll().then(function (results) {
|
||||
should.exist(results);
|
||||
results.length.should.be.above(1);
|
||||
should.exist(PostModel);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
function checkFirstPostData(firstPost) {
|
||||
should.not.exist(firstPost.author_id);
|
||||
firstPost.author.should.be.an.Object;
|
||||
firstPost.fields.should.be.an.Array;
|
||||
firstPost.tags.should.be.an.Array;
|
||||
firstPost.author.name.should.equal(DataGenerator.Content.users[0].name);
|
||||
firstPost.fields[0].key.should.equal(DataGenerator.Content.app_fields[0].key);
|
||||
firstPost.created_at.should.be.an.instanceof(Date);
|
||||
firstPost.created_by.should.be.an.Object;
|
||||
firstPost.updated_by.should.be.an.Object;
|
||||
firstPost.published_by.should.be.an.Object;
|
||||
firstPost.created_by.name.should.equal(DataGenerator.Content.users[0].name);
|
||||
firstPost.updated_by.name.should.equal(DataGenerator.Content.users[0].name);
|
||||
firstPost.published_by.name.should.equal(DataGenerator.Content.users[0].name);
|
||||
firstPost.tags[0].name.should.equal(DataGenerator.Content.tags[0].name);
|
||||
}
|
||||
|
||||
it('can findAll, returning all related data', function (done) {
|
||||
var firstPost;
|
||||
|
||||
PostModel.findAll({include: ['author_id', 'fields', 'tags', 'created_by', 'updated_by', 'published_by']})
|
||||
.then(function (results) {
|
||||
it('can findAll', function (done) {
|
||||
PostModel.findAll().then(function (results) {
|
||||
should.exist(results);
|
||||
results.length.should.be.above(0);
|
||||
firstPost = results.models[0].toJSON();
|
||||
checkFirstPostData(firstPost);
|
||||
results.length.should.be.above(1);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('can findPage (default)', function (done) {
|
||||
PostModel.findPage().then(function (results) {
|
||||
should.exist(results);
|
||||
it('can findAll, returning all related data', function (done) {
|
||||
var firstPost;
|
||||
|
||||
results.meta.pagination.page.should.equal(1);
|
||||
results.meta.pagination.limit.should.equal(15);
|
||||
results.meta.pagination.pages.should.equal(1);
|
||||
results.posts.length.should.equal(4);
|
||||
PostModel.findAll({include: ['author_id', 'fields', 'tags', 'created_by', 'updated_by', 'published_by']})
|
||||
.then(function (results) {
|
||||
should.exist(results);
|
||||
results.length.should.be.above(0);
|
||||
firstPost = results.models[0].toJSON();
|
||||
checkFirstPostData(firstPost);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can findPage, returning all related data', function (done) {
|
||||
var firstPost;
|
||||
|
||||
PostModel.findPage({include: ['author_id', 'fields', 'tags', 'created_by', 'updated_by', 'published_by']})
|
||||
.then(function (results) {
|
||||
it('can findPage (default)', function (done) {
|
||||
PostModel.findPage().then(function (results) {
|
||||
should.exist(results);
|
||||
|
||||
results.meta.pagination.page.should.equal(1);
|
||||
@ -83,397 +70,444 @@ describe('Post Model', function () {
|
||||
results.meta.pagination.pages.should.equal(1);
|
||||
results.posts.length.should.equal(4);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can findPage, returning all related data', function (done) {
|
||||
var firstPost;
|
||||
|
||||
PostModel.findPage({include: ['author_id', 'fields', 'tags', 'created_by', 'updated_by', 'published_by']})
|
||||
.then(function (results) {
|
||||
should.exist(results);
|
||||
|
||||
results.meta.pagination.page.should.equal(1);
|
||||
results.meta.pagination.limit.should.equal(15);
|
||||
results.meta.pagination.pages.should.equal(1);
|
||||
results.posts.length.should.equal(4);
|
||||
|
||||
firstPost = results.posts[0];
|
||||
|
||||
checkFirstPostData(firstPost);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
|
||||
it('can findOne', function (done) {
|
||||
var firstPost;
|
||||
|
||||
PostModel.findPage().then(function (results) {
|
||||
should.exist(results);
|
||||
should.exist(results.posts);
|
||||
results.posts.length.should.be.above(0);
|
||||
firstPost = results.posts[0];
|
||||
|
||||
checkFirstPostData(firstPost);
|
||||
return PostModel.findOne({slug: firstPost.slug});
|
||||
}).then(function (found) {
|
||||
should.exist(found);
|
||||
found.attributes.title.should.equal(firstPost.title);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('can findOne, returning all related data', function (done) {
|
||||
var firstPost;
|
||||
// TODO: should take author :-/
|
||||
PostModel.findOne({}, {include: ['author_id', 'fields', 'tags', 'created_by', 'updated_by', 'published_by']})
|
||||
.then(function (result) {
|
||||
should.exist(result);
|
||||
firstPost = result.toJSON();
|
||||
|
||||
it('can findOne', function (done) {
|
||||
var firstPost;
|
||||
checkFirstPostData(firstPost);
|
||||
|
||||
PostModel.findPage().then(function (results) {
|
||||
should.exist(results);
|
||||
should.exist(results.posts);
|
||||
results.posts.length.should.be.above(0);
|
||||
firstPost = results.posts[0];
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
return PostModel.findOne({slug: firstPost.slug});
|
||||
}).then(function (found) {
|
||||
should.exist(found);
|
||||
found.attributes.title.should.equal(firstPost.title);
|
||||
it('can edit', function (done) {
|
||||
var firstPost = 1;
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
PostModel.findOne({id: firstPost}).then(function (results) {
|
||||
var post;
|
||||
should.exist(results);
|
||||
post = results.toJSON();
|
||||
post.id.should.equal(firstPost);
|
||||
post.title.should.not.equal('new title');
|
||||
|
||||
it('can findOne, returning all related data', function (done) {
|
||||
var firstPost;
|
||||
// TODO: should take author :-/
|
||||
PostModel.findOne({}, {include: ['author_id', 'fields', 'tags', 'created_by', 'updated_by', 'published_by']})
|
||||
.then(function (result) {
|
||||
should.exist(result);
|
||||
firstPost = result.toJSON();
|
||||
|
||||
checkFirstPostData(firstPost);
|
||||
return PostModel.edit({title: 'new title'}, _.extend(context, {id: firstPost}));
|
||||
}).then(function (edited) {
|
||||
should.exist(edited);
|
||||
edited.attributes.title.should.equal('new title');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can edit', function (done) {
|
||||
var firstPost = 1;
|
||||
|
||||
PostModel.findOne({id: firstPost}).then(function (results) {
|
||||
var post;
|
||||
should.exist(results);
|
||||
post = results.toJSON();
|
||||
post.id.should.equal(firstPost);
|
||||
post.title.should.not.equal('new title');
|
||||
|
||||
return PostModel.edit({title: 'new title'}, _.extend(context, {id: firstPost}));
|
||||
}).then(function (edited) {
|
||||
should.exist(edited);
|
||||
edited.attributes.title.should.equal('new title');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('can add, defaults are all correct', function (done) {
|
||||
var createdPostUpdatedDate,
|
||||
newPost = testUtils.DataGenerator.forModel.posts[2],
|
||||
newPostDB = testUtils.DataGenerator.Content.posts[2];
|
||||
it('can add, defaults are all correct', function (done) {
|
||||
var createdPostUpdatedDate,
|
||||
newPost = testUtils.DataGenerator.forModel.posts[2],
|
||||
newPostDB = testUtils.DataGenerator.Content.posts[2];
|
||||
|
||||
PostModel.add(newPost, context).then(function (createdPost) {
|
||||
return new PostModel({id: createdPost.id}).fetch();
|
||||
}).then(function (createdPost) {
|
||||
should.exist(createdPost);
|
||||
createdPost.has('uuid').should.equal(true);
|
||||
createdPost.get('status').should.equal('draft');
|
||||
createdPost.get('title').should.equal(newPost.title, 'title is correct');
|
||||
createdPost.get('markdown').should.equal(newPost.markdown, 'markdown is correct');
|
||||
createdPost.has('html').should.equal(true);
|
||||
createdPost.get('html').should.equal(newPostDB.html);
|
||||
createdPost.get('slug').should.equal(newPostDB.slug + '-2');
|
||||
(!!createdPost.get('featured')).should.equal(false);
|
||||
(!!createdPost.get('page')).should.equal(false);
|
||||
createdPost.get('language').should.equal('en_US');
|
||||
// testing for nulls
|
||||
(createdPost.get('image') === null).should.equal(true);
|
||||
(createdPost.get('meta_title') === null).should.equal(true);
|
||||
(createdPost.get('meta_description') === null).should.equal(true);
|
||||
PostModel.add(newPost, context).then(function (createdPost) {
|
||||
return new PostModel({id: createdPost.id}).fetch();
|
||||
}).then(function (createdPost) {
|
||||
should.exist(createdPost);
|
||||
createdPost.has('uuid').should.equal(true);
|
||||
createdPost.get('status').should.equal('draft');
|
||||
createdPost.get('title').should.equal(newPost.title, 'title is correct');
|
||||
createdPost.get('markdown').should.equal(newPost.markdown, 'markdown is correct');
|
||||
createdPost.has('html').should.equal(true);
|
||||
createdPost.get('html').should.equal(newPostDB.html);
|
||||
createdPost.get('slug').should.equal(newPostDB.slug + '-2');
|
||||
(!!createdPost.get('featured')).should.equal(false);
|
||||
(!!createdPost.get('page')).should.equal(false);
|
||||
createdPost.get('language').should.equal('en_US');
|
||||
// testing for nulls
|
||||
(createdPost.get('image') === null).should.equal(true);
|
||||
(createdPost.get('meta_title') === null).should.equal(true);
|
||||
(createdPost.get('meta_description') === null).should.equal(true);
|
||||
|
||||
createdPost.get('created_at').should.be.above(new Date(0).getTime());
|
||||
createdPost.get('created_by').should.equal(1);
|
||||
createdPost.get('author_id').should.equal(1);
|
||||
createdPost.has('author').should.equal(false);
|
||||
createdPost.get('created_by').should.equal(createdPost.get('author_id'));
|
||||
createdPost.get('updated_at').should.be.above(new Date(0).getTime());
|
||||
createdPost.get('updated_by').should.equal(1);
|
||||
should.equal(createdPost.get('published_at'), null);
|
||||
should.equal(createdPost.get('published_by'), null);
|
||||
createdPost.get('created_at').should.be.above(new Date(0).getTime());
|
||||
createdPost.get('created_by').should.equal(1);
|
||||
createdPost.get('author_id').should.equal(1);
|
||||
createdPost.has('author').should.equal(false);
|
||||
createdPost.get('created_by').should.equal(createdPost.get('author_id'));
|
||||
createdPost.get('updated_at').should.be.above(new Date(0).getTime());
|
||||
createdPost.get('updated_by').should.equal(1);
|
||||
should.equal(createdPost.get('published_at'), null);
|
||||
should.equal(createdPost.get('published_by'), null);
|
||||
|
||||
createdPostUpdatedDate = createdPost.get('updated_at');
|
||||
createdPostUpdatedDate = createdPost.get('updated_at');
|
||||
|
||||
// Set the status to published to check that `published_at` is set.
|
||||
return createdPost.save({status: 'published'}, context);
|
||||
}).then(function (publishedPost) {
|
||||
publishedPost.get('published_at').should.be.instanceOf(Date);
|
||||
publishedPost.get('published_by').should.equal(1);
|
||||
publishedPost.get('updated_at').should.be.instanceOf(Date);
|
||||
publishedPost.get('updated_by').should.equal(1);
|
||||
publishedPost.get('updated_at').should.not.equal(createdPostUpdatedDate);
|
||||
// Set the status to published to check that `published_at` is set.
|
||||
return createdPost.save({status: 'published'}, context);
|
||||
}).then(function (publishedPost) {
|
||||
publishedPost.get('published_at').should.be.instanceOf(Date);
|
||||
publishedPost.get('published_by').should.equal(1);
|
||||
publishedPost.get('updated_at').should.be.instanceOf(Date);
|
||||
publishedPost.get('updated_by').should.equal(1);
|
||||
publishedPost.get('updated_at').should.not.equal(createdPostUpdatedDate);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
done();
|
||||
}).catch(done);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
it('can add, with previous published_at date', function (done) {
|
||||
var previousPublishedAtDate = new Date(2013, 8, 21, 12);
|
||||
it('can add, with previous published_at date', function (done) {
|
||||
var previousPublishedAtDate = new Date(2013, 8, 21, 12);
|
||||
|
||||
PostModel.add({
|
||||
status: 'published',
|
||||
published_at: previousPublishedAtDate,
|
||||
title: 'published_at test',
|
||||
markdown: 'This is some content'
|
||||
}, context).then(function (newPost) {
|
||||
PostModel.add({
|
||||
status: 'published',
|
||||
published_at: previousPublishedAtDate,
|
||||
title: 'published_at test',
|
||||
markdown: 'This is some content'
|
||||
}, context).then(function (newPost) {
|
||||
|
||||
should.exist(newPost);
|
||||
new Date(newPost.get('published_at')).getTime().should.equal(previousPublishedAtDate.getTime());
|
||||
should.exist(newPost);
|
||||
new Date(newPost.get('published_at')).getTime().should.equal(previousPublishedAtDate.getTime());
|
||||
|
||||
done();
|
||||
done();
|
||||
|
||||
}).catch(done);
|
||||
});
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can trim title', function (done) {
|
||||
var untrimmedCreateTitle = ' test trimmed create title ',
|
||||
untrimmedUpdateTitle = ' test trimmed update title ',
|
||||
newPost = {
|
||||
title: untrimmedCreateTitle,
|
||||
markdown: 'Test Content'
|
||||
it('can trim title', function (done) {
|
||||
var untrimmedCreateTitle = ' test trimmed create title ',
|
||||
untrimmedUpdateTitle = ' test trimmed update title ',
|
||||
newPost = {
|
||||
title: untrimmedCreateTitle,
|
||||
markdown: 'Test Content'
|
||||
};
|
||||
|
||||
PostModel.add(newPost, context).then(function (createdPost) {
|
||||
return new PostModel({ id: createdPost.id }).fetch();
|
||||
}).then(function (createdPost) {
|
||||
should.exist(createdPost);
|
||||
createdPost.get('title').should.equal(untrimmedCreateTitle.trim());
|
||||
|
||||
return createdPost.save({ title: untrimmedUpdateTitle }, context);
|
||||
}).then(function (updatedPost) {
|
||||
updatedPost.get('title').should.equal(untrimmedUpdateTitle.trim());
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can generate a non conflicting slug', function (done) {
|
||||
// Create 12 posts with the same title
|
||||
sequence(_.times(12, function (i) {
|
||||
return function () {
|
||||
return PostModel.add({
|
||||
title: 'Test Title',
|
||||
markdown: 'Test Content ' + (i+1)
|
||||
}, context);
|
||||
};
|
||||
})).then(function (createdPosts) {
|
||||
// Should have created 12 posts
|
||||
createdPosts.length.should.equal(12);
|
||||
|
||||
// Should have unique slugs and contents
|
||||
_(createdPosts).each(function (post, i) {
|
||||
var num = i + 1;
|
||||
|
||||
// First one has normal title
|
||||
if (num === 1) {
|
||||
post.get('slug').should.equal('test-title');
|
||||
return;
|
||||
}
|
||||
|
||||
post.get('slug').should.equal('test-title-' + num);
|
||||
post.get('markdown').should.equal('Test Content ' + num);
|
||||
});
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can generate slugs without duplicate hyphens', function (done) {
|
||||
var newPost = {
|
||||
title: 'apprehensive titles have too many spaces—and m-dashes — – and also n-dashes ',
|
||||
markdown: 'Test Content 1'
|
||||
};
|
||||
|
||||
PostModel.add(newPost, context).then(function (createdPost) {
|
||||
return new PostModel({ id: createdPost.id }).fetch();
|
||||
}).then(function (createdPost) {
|
||||
should.exist(createdPost);
|
||||
createdPost.get('title').should.equal(untrimmedCreateTitle.trim());
|
||||
PostModel.add(newPost, context).then(function (createdPost) {
|
||||
|
||||
return createdPost.save({ title: untrimmedUpdateTitle }, context);
|
||||
}).then(function (updatedPost) {
|
||||
updatedPost.get('title').should.equal(untrimmedUpdateTitle.trim());
|
||||
createdPost.get('slug').should.equal('apprehensive-titles-have-too-many-spaces-and-m-dashes-and-also-n-dashes');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can generate a non conflicting slug', function (done) {
|
||||
// Create 12 posts with the same title
|
||||
sequence(_.times(12, function (i) {
|
||||
return function () {
|
||||
return PostModel.add({
|
||||
title: 'Test Title',
|
||||
markdown: 'Test Content ' + (i+1)
|
||||
}, context);
|
||||
it('can generate a safe slug when a reserved keyword is used', function(done) {
|
||||
var newPost = {
|
||||
title: 'rss',
|
||||
markdown: 'Test Content 1'
|
||||
};
|
||||
})).then(function (createdPosts) {
|
||||
// Should have created 12 posts
|
||||
createdPosts.length.should.equal(12);
|
||||
|
||||
// Should have unique slugs and contents
|
||||
_(createdPosts).each(function (post, i) {
|
||||
var num = i + 1;
|
||||
|
||||
// First one has normal title
|
||||
if (num === 1) {
|
||||
post.get('slug').should.equal('test-title');
|
||||
return;
|
||||
}
|
||||
|
||||
post.get('slug').should.equal('test-title-' + num);
|
||||
post.get('markdown').should.equal('Test Content ' + num);
|
||||
PostModel.add(newPost, context).then(function (createdPost) {
|
||||
createdPost.get('slug').should.not.equal('rss');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
it('can generate slugs without non-ascii characters', function (done) {
|
||||
var newPost = {
|
||||
title: 'भुते धडकी भरवणारा आहेत',
|
||||
markdown: 'Test Content 1'
|
||||
};
|
||||
|
||||
it('can generate slugs without duplicate hyphens', function (done) {
|
||||
var newPost = {
|
||||
title: 'apprehensive titles have too many spaces—and m-dashes — – and also n-dashes ',
|
||||
markdown: 'Test Content 1'
|
||||
};
|
||||
PostModel.add(newPost, context).then(function (createdPost) {
|
||||
createdPost.get('slug').should.equal('bhute-dhddkii-bhrvnnaaraa-aahet');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
PostModel.add(newPost, context).then(function (createdPost) {
|
||||
it('detects duplicate slugs before saving', function (done) {
|
||||
var firstPost = {
|
||||
title: 'First post',
|
||||
markdown: 'First content 1'
|
||||
},
|
||||
secondPost = {
|
||||
title: 'Second post',
|
||||
markdown: 'Second content 1'
|
||||
};
|
||||
|
||||
createdPost.get('slug').should.equal('apprehensive-titles-have-too-many-spaces-and-m-dashes-and-also-n-dashes');
|
||||
// Create the first post
|
||||
PostModel.add(firstPost, context)
|
||||
.then(function (createdFirstPost) {
|
||||
// Store the slug for later
|
||||
firstPost.slug = createdFirstPost.get('slug');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
// Create the second post
|
||||
return PostModel.add(secondPost, context);
|
||||
}).then(function (createdSecondPost) {
|
||||
// Store the slug for comparison later
|
||||
secondPost.slug = createdSecondPost.get('slug');
|
||||
|
||||
it('can generate a safe slug when a reserved keyword is used', function(done) {
|
||||
var newPost = {
|
||||
title: 'rss',
|
||||
markdown: 'Test Content 1'
|
||||
};
|
||||
// Update with a conflicting slug from the first post
|
||||
return createdSecondPost.save({
|
||||
slug: firstPost.slug
|
||||
}, context);
|
||||
}).then(function (updatedSecondPost) {
|
||||
|
||||
PostModel.add(newPost, context).then(function (createdPost) {
|
||||
createdPost.get('slug').should.not.equal('rss');
|
||||
done();
|
||||
// Should have updated from original
|
||||
updatedSecondPost.get('slug').should.not.equal(secondPost.slug);
|
||||
// Should not have a conflicted slug from the first
|
||||
updatedSecondPost.get('slug').should.not.equal(firstPost.slug);
|
||||
|
||||
return PostModel.findOne({
|
||||
id: updatedSecondPost.id,
|
||||
status: 'all'
|
||||
});
|
||||
}).then(function (foundPost) {
|
||||
|
||||
// Should have updated from original
|
||||
foundPost.get('slug').should.not.equal(secondPost.slug);
|
||||
// Should not have a conflicted slug from the first
|
||||
foundPost.get('slug').should.not.equal(firstPost.slug);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can destroy', function (done) {
|
||||
// We're going to try deleting post id 1 which also has tag id 1
|
||||
var firstItemData = {id: 1};
|
||||
|
||||
// Test that we have the post we expect, with exactly one tag
|
||||
PostModel.findOne(firstItemData).then(function (results) {
|
||||
var post;
|
||||
should.exist(results);
|
||||
post = results.toJSON();
|
||||
post.id.should.equal(firstItemData.id);
|
||||
post.tags.should.have.length(2);
|
||||
post.tags[0].should.equal(firstItemData.id);
|
||||
|
||||
// Destroy the post
|
||||
return PostModel.destroy(firstItemData);
|
||||
}).then(function (response) {
|
||||
var deleted = response.toJSON();
|
||||
|
||||
deleted.tags.should.be.empty;
|
||||
should.equal(deleted.author, undefined);
|
||||
|
||||
// Double check we can't find the post again
|
||||
return PostModel.findOne(firstItemData);
|
||||
}).then(function (newResults) {
|
||||
should.equal(newResults, null);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
|
||||
it('can findPage, with various options', function (done) {
|
||||
testUtils.fixtures.insertMorePosts().then(function () {
|
||||
|
||||
return testUtils.fixtures.insertMorePostsTags();
|
||||
}).then(function () {
|
||||
return PostModel.findPage({page: 2});
|
||||
}).then(function (paginationResult) {
|
||||
paginationResult.meta.pagination.page.should.equal(2);
|
||||
paginationResult.meta.pagination.limit.should.equal(15);
|
||||
paginationResult.meta.pagination.pages.should.equal(4);
|
||||
paginationResult.posts.length.should.equal(15);
|
||||
|
||||
return PostModel.findPage({page: 5});
|
||||
}).then(function (paginationResult) {
|
||||
paginationResult.meta.pagination.page.should.equal(5);
|
||||
paginationResult.meta.pagination.limit.should.equal(15);
|
||||
paginationResult.meta.pagination.pages.should.equal(4);
|
||||
paginationResult.posts.length.should.equal(0);
|
||||
|
||||
return PostModel.findPage({limit: 30});
|
||||
}).then(function (paginationResult) {
|
||||
paginationResult.meta.pagination.page.should.equal(1);
|
||||
paginationResult.meta.pagination.limit.should.equal(30);
|
||||
paginationResult.meta.pagination.pages.should.equal(2);
|
||||
paginationResult.posts.length.should.equal(30);
|
||||
|
||||
// Test both boolean formats
|
||||
return PostModel.findPage({limit: 10, staticPages: true});
|
||||
}).then(function (paginationResult) {
|
||||
paginationResult.meta.pagination.page.should.equal(1);
|
||||
paginationResult.meta.pagination.limit.should.equal(10);
|
||||
paginationResult.meta.pagination.pages.should.equal(1);
|
||||
paginationResult.posts.length.should.equal(1);
|
||||
|
||||
// Test both boolean formats
|
||||
return PostModel.findPage({limit: 10, staticPages: '1'});
|
||||
}).then(function (paginationResult) {
|
||||
paginationResult.meta.pagination.page.should.equal(1);
|
||||
paginationResult.meta.pagination.limit.should.equal(10);
|
||||
paginationResult.meta.pagination.pages.should.equal(1);
|
||||
paginationResult.posts.length.should.equal(1);
|
||||
|
||||
return PostModel.findPage({limit: 10, page: 2, status: 'all'});
|
||||
}).then(function (paginationResult) {
|
||||
paginationResult.meta.pagination.pages.should.equal(11);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
it('can findPage for tag, with various options', function (done) {
|
||||
testUtils.fixtures.insertMorePosts().then(function () {
|
||||
|
||||
return testUtils.fixtures.insertMorePostsTags();
|
||||
}).then(function () {
|
||||
// Test tag filter
|
||||
return PostModel.findPage({page: 1, tag: 'bacon'});
|
||||
}).then(function (paginationResult) {
|
||||
paginationResult.meta.pagination.page.should.equal(1);
|
||||
paginationResult.meta.pagination.limit.should.equal(15);
|
||||
paginationResult.meta.pagination.pages.should.equal(1);
|
||||
paginationResult.meta.filters.tags[0].name.should.equal('bacon');
|
||||
paginationResult.meta.filters.tags[0].slug.should.equal('bacon');
|
||||
paginationResult.posts.length.should.equal(2);
|
||||
|
||||
return PostModel.findPage({page: 1, tag: 'kitchen-sink'});
|
||||
}).then(function (paginationResult) {
|
||||
paginationResult.meta.pagination.page.should.equal(1);
|
||||
paginationResult.meta.pagination.limit.should.equal(15);
|
||||
paginationResult.meta.pagination.pages.should.equal(1);
|
||||
paginationResult.meta.filters.tags[0].name.should.equal('kitchen sink');
|
||||
paginationResult.meta.filters.tags[0].slug.should.equal('kitchen-sink');
|
||||
paginationResult.posts.length.should.equal(2);
|
||||
|
||||
return PostModel.findPage({page: 1, tag: 'injection'});
|
||||
}).then(function (paginationResult) {
|
||||
paginationResult.meta.pagination.page.should.equal(1);
|
||||
paginationResult.meta.pagination.limit.should.equal(15);
|
||||
paginationResult.meta.pagination.pages.should.equal(2);
|
||||
paginationResult.meta.filters.tags[0].name.should.equal('injection');
|
||||
paginationResult.meta.filters.tags[0].slug.should.equal('injection');
|
||||
paginationResult.posts.length.should.equal(15);
|
||||
|
||||
return PostModel.findPage({page: 2, tag: 'injection'});
|
||||
}).then(function (paginationResult) {
|
||||
paginationResult.meta.pagination.page.should.equal(2);
|
||||
paginationResult.meta.pagination.limit.should.equal(15);
|
||||
paginationResult.meta.pagination.pages.should.equal(2);
|
||||
paginationResult.meta.filters.tags[0].name.should.equal('injection');
|
||||
paginationResult.meta.filters.tags[0].slug.should.equal('injection');
|
||||
paginationResult.posts.length.should.equal(10);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('can generate slugs without non-ascii characters', function (done) {
|
||||
var newPost = {
|
||||
title: 'भुते धडकी भरवणारा आहेत',
|
||||
markdown: 'Test Content 1'
|
||||
};
|
||||
|
||||
PostModel.add(newPost, context).then(function (createdPost) {
|
||||
createdPost.get('slug').should.equal('bhute-dhddkii-bhrvnnaaraa-aahet');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
describe('Multiauthor Posts', function () {
|
||||
before(testUtils.teardown);
|
||||
afterEach(testUtils.teardown);
|
||||
beforeEach(testUtils.setup('posts:mu'));
|
||||
|
||||
it('detects duplicate slugs before saving', function (done) {
|
||||
var firstPost = {
|
||||
title: 'First post',
|
||||
markdown: 'First content 1'
|
||||
},
|
||||
secondPost = {
|
||||
title: 'Second post',
|
||||
markdown: 'Second content 1'
|
||||
};
|
||||
should.exist(PostModel);
|
||||
|
||||
// Create the first post
|
||||
PostModel.add(firstPost, context)
|
||||
.then(function (createdFirstPost) {
|
||||
// Store the slug for later
|
||||
firstPost.slug = createdFirstPost.get('slug');
|
||||
it('can destroy multiple posts by author', function (done) {
|
||||
|
||||
// Create the second post
|
||||
return PostModel.add(secondPost, context);
|
||||
}).then(function (createdSecondPost) {
|
||||
// Store the slug for comparison later
|
||||
secondPost.slug = createdSecondPost.get('slug');
|
||||
|
||||
// Update with a conflicting slug from the first post
|
||||
return createdSecondPost.save({
|
||||
slug: firstPost.slug
|
||||
}, context);
|
||||
}).then(function (updatedSecondPost) {
|
||||
|
||||
// Should have updated from original
|
||||
updatedSecondPost.get('slug').should.not.equal(secondPost.slug);
|
||||
// Should not have a conflicted slug from the first
|
||||
updatedSecondPost.get('slug').should.not.equal(firstPost.slug);
|
||||
|
||||
return PostModel.findOne({
|
||||
id: updatedSecondPost.id,
|
||||
status: 'all'
|
||||
});
|
||||
}).then(function (foundPost) {
|
||||
|
||||
// Should have updated from original
|
||||
foundPost.get('slug').should.not.equal(secondPost.slug);
|
||||
// Should not have a conflicted slug from the first
|
||||
foundPost.get('slug').should.not.equal(firstPost.slug);
|
||||
// We're going to delete all posts by user 1
|
||||
var authorData = {id: 1};
|
||||
|
||||
PostModel.findAll().then(function (found) {
|
||||
// There are 50 posts to begin with
|
||||
found.length.should.equal(50);
|
||||
return PostModel.destroyByAuthor(authorData);
|
||||
}).then(function (results) {
|
||||
// User 1 has 13 posts in the database
|
||||
results.length.should.equal(13);
|
||||
return PostModel.findAll();
|
||||
}).then(function (found) {
|
||||
// Only 37 should remain
|
||||
found.length.should.equal(37);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can destroy', function (done) {
|
||||
// We're going to try deleting post id 1 which also has tag id 1
|
||||
var firstItemData = {id: 1};
|
||||
|
||||
// Test that we have the post we expect, with exactly one tag
|
||||
PostModel.findOne(firstItemData).then(function (results) {
|
||||
var post;
|
||||
should.exist(results);
|
||||
post = results.toJSON();
|
||||
post.id.should.equal(firstItemData.id);
|
||||
post.tags.should.have.length(2);
|
||||
post.tags[0].should.equal(firstItemData.id);
|
||||
|
||||
// Destroy the post
|
||||
return PostModel.destroy(firstItemData);
|
||||
}).then(function (response) {
|
||||
var deleted = response.toJSON();
|
||||
|
||||
deleted.tags.should.be.empty;
|
||||
should.equal(deleted.author, undefined);
|
||||
|
||||
// Double check we can't find the post again
|
||||
return PostModel.findOne(firstItemData);
|
||||
}).then(function (newResults) {
|
||||
should.equal(newResults, null);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can findPage, with various options', function (done) {
|
||||
testUtils.fixtures.insertMorePosts().then(function () {
|
||||
|
||||
return testUtils.fixtures.insertMorePostsTags();
|
||||
}).then(function () {
|
||||
return PostModel.findPage({page: 2});
|
||||
}).then(function (paginationResult) {
|
||||
paginationResult.meta.pagination.page.should.equal(2);
|
||||
paginationResult.meta.pagination.limit.should.equal(15);
|
||||
paginationResult.meta.pagination.pages.should.equal(4);
|
||||
paginationResult.posts.length.should.equal(15);
|
||||
|
||||
return PostModel.findPage({page: 5});
|
||||
}).then(function (paginationResult) {
|
||||
paginationResult.meta.pagination.page.should.equal(5);
|
||||
paginationResult.meta.pagination.limit.should.equal(15);
|
||||
paginationResult.meta.pagination.pages.should.equal(4);
|
||||
paginationResult.posts.length.should.equal(0);
|
||||
|
||||
return PostModel.findPage({limit: 30});
|
||||
}).then(function (paginationResult) {
|
||||
paginationResult.meta.pagination.page.should.equal(1);
|
||||
paginationResult.meta.pagination.limit.should.equal(30);
|
||||
paginationResult.meta.pagination.pages.should.equal(2);
|
||||
paginationResult.posts.length.should.equal(30);
|
||||
|
||||
// Test both boolean formats
|
||||
return PostModel.findPage({limit: 10, staticPages: true});
|
||||
}).then(function (paginationResult) {
|
||||
paginationResult.meta.pagination.page.should.equal(1);
|
||||
paginationResult.meta.pagination.limit.should.equal(10);
|
||||
paginationResult.meta.pagination.pages.should.equal(1);
|
||||
paginationResult.posts.length.should.equal(1);
|
||||
|
||||
// Test both boolean formats
|
||||
return PostModel.findPage({limit: 10, staticPages: '1'});
|
||||
}).then(function (paginationResult) {
|
||||
paginationResult.meta.pagination.page.should.equal(1);
|
||||
paginationResult.meta.pagination.limit.should.equal(10);
|
||||
paginationResult.meta.pagination.pages.should.equal(1);
|
||||
paginationResult.posts.length.should.equal(1);
|
||||
|
||||
return PostModel.findPage({limit: 10, page: 2, status: 'all'});
|
||||
}).then(function (paginationResult) {
|
||||
paginationResult.meta.pagination.pages.should.equal(11);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
it('can findPage for tag, with various options', function (done) {
|
||||
testUtils.fixtures.insertMorePosts().then(function () {
|
||||
|
||||
return testUtils.fixtures.insertMorePostsTags();
|
||||
}).then(function () {
|
||||
// Test tag filter
|
||||
return PostModel.findPage({page: 1, tag: 'bacon'});
|
||||
}).then(function (paginationResult) {
|
||||
paginationResult.meta.pagination.page.should.equal(1);
|
||||
paginationResult.meta.pagination.limit.should.equal(15);
|
||||
paginationResult.meta.pagination.pages.should.equal(1);
|
||||
paginationResult.meta.filters.tags[0].name.should.equal('bacon');
|
||||
paginationResult.meta.filters.tags[0].slug.should.equal('bacon');
|
||||
paginationResult.posts.length.should.equal(2);
|
||||
|
||||
return PostModel.findPage({page: 1, tag: 'kitchen-sink'});
|
||||
}).then(function (paginationResult) {
|
||||
paginationResult.meta.pagination.page.should.equal(1);
|
||||
paginationResult.meta.pagination.limit.should.equal(15);
|
||||
paginationResult.meta.pagination.pages.should.equal(1);
|
||||
paginationResult.meta.filters.tags[0].name.should.equal('kitchen sink');
|
||||
paginationResult.meta.filters.tags[0].slug.should.equal('kitchen-sink');
|
||||
paginationResult.posts.length.should.equal(2);
|
||||
|
||||
return PostModel.findPage({page: 1, tag: 'injection'});
|
||||
}).then(function (paginationResult) {
|
||||
paginationResult.meta.pagination.page.should.equal(1);
|
||||
paginationResult.meta.pagination.limit.should.equal(15);
|
||||
paginationResult.meta.pagination.pages.should.equal(2);
|
||||
paginationResult.meta.filters.tags[0].name.should.equal('injection');
|
||||
paginationResult.meta.filters.tags[0].slug.should.equal('injection');
|
||||
paginationResult.posts.length.should.equal(15);
|
||||
|
||||
return PostModel.findPage({page: 2, tag: 'injection'});
|
||||
}).then(function (paginationResult) {
|
||||
paginationResult.meta.pagination.page.should.equal(2);
|
||||
paginationResult.meta.pagination.limit.should.equal(15);
|
||||
paginationResult.meta.pagination.pages.should.equal(2);
|
||||
paginationResult.meta.filters.tags[0].name.should.equal('injection');
|
||||
paginationResult.meta.filters.tags[0].slug.should.equal('injection');
|
||||
paginationResult.posts.length.should.equal(10);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
// disabling sanitization until we can implement a better version
|
||||
|
File diff suppressed because one or more lines are too long
@ -31,12 +31,71 @@ var when = require('when'),
|
||||
fixtures = {
|
||||
insertPosts: function insertPosts() {
|
||||
var knex = config.database.knex;
|
||||
// ToDo: Get rid of pyramid of doom
|
||||
return when(knex('posts').insert(DataGenerator.forKnex.posts).then(function () {
|
||||
return knex('tags').insert(DataGenerator.forKnex.tags).then(function () {
|
||||
return knex('posts_tags').insert(DataGenerator.forKnex.posts_tags);
|
||||
});
|
||||
}));
|
||||
return when(knex('posts').insert(DataGenerator.forKnex.posts)).then(function () {
|
||||
return knex('tags').insert(DataGenerator.forKnex.tags);
|
||||
}).then(function () {
|
||||
return knex('posts_tags').insert(DataGenerator.forKnex.posts_tags);
|
||||
});
|
||||
},
|
||||
|
||||
insertMultiAuthorPosts: function insertMultiAuthorPosts(max) {
|
||||
var knex = config.database.knex,
|
||||
tags,
|
||||
author,
|
||||
authors,
|
||||
i, j, k = postsInserted,
|
||||
posts = [];
|
||||
|
||||
max = max || 50;
|
||||
// insert users of different roles
|
||||
return when(fixtures.createUsersWithRoles()).then(function (results) {
|
||||
// create the tags
|
||||
return knex('tags').insert(DataGenerator.forKnex.tags);
|
||||
}).then(function (results) {
|
||||
return knex('users').select('id');
|
||||
}).then(function (results) {
|
||||
authors = _.pluck(results, 'id');
|
||||
|
||||
// Let's insert posts with random authors
|
||||
for (i = 0; i < max; i += 1) {
|
||||
author = authors[i % authors.length];
|
||||
posts.push(DataGenerator.forKnex.createGenericPost(k++, null, null, author));
|
||||
}
|
||||
|
||||
// Keep track so we can run this function again safely
|
||||
postsInserted = k;
|
||||
|
||||
return sequence(_.times(posts.length, function (index) {
|
||||
return function () {
|
||||
return knex('posts').insert(posts[index]);
|
||||
};
|
||||
}));
|
||||
}).then(function () {
|
||||
return when.all([
|
||||
// PostgreSQL can return results in any order
|
||||
knex('posts').orderBy('id', 'asc').select('id'),
|
||||
knex('tags').select('id')
|
||||
]);
|
||||
}).then(function (results) {
|
||||
var posts = _.pluck(results[0], 'id'),
|
||||
tags = _.pluck(results[1], 'id'),
|
||||
promises = [],
|
||||
i;
|
||||
|
||||
if (max > posts.length) {
|
||||
throw new Error('Trying to add more posts_tags than the number of posts.');
|
||||
}
|
||||
|
||||
for (i = 0; i < max; i += 1) {
|
||||
promises.push(DataGenerator.forKnex.createPostsTags(posts[i], tags[i % tags.length]));
|
||||
}
|
||||
|
||||
return sequence(_.times(promises.length, function (index) {
|
||||
return function () {
|
||||
return knex('posts_tags').insert(promises[index]);
|
||||
};
|
||||
}));
|
||||
});
|
||||
},
|
||||
|
||||
insertMorePosts: function insertMorePosts(max) {
|
||||
@ -263,14 +322,16 @@ toDoList = {
|
||||
'permission': function insertPermission() { return fixtures.insertOne('permissions', 'createPermission'); },
|
||||
'role': function insertRole() { return fixtures.insertOne('roles', 'createRole'); },
|
||||
'roles': function insertRoles() { return fixtures.insertRoles(); },
|
||||
'tag': function insertRole() { return fixtures.insertOne('tags', 'createTag'); },
|
||||
'tag': function insertTag() { return fixtures.insertOne('tags', 'createTag'); },
|
||||
|
||||
'posts': function insertPosts() { return fixtures.insertPosts(); },
|
||||
'posts:mu': function insertMultiAuthorPosts() { return fixtures.insertMultiAuthorPosts(); },
|
||||
'apps': function insertApps() { return fixtures.insertApps(); },
|
||||
'settings': function populate() {
|
||||
return settings.populateDefaults().then(function () { return SettingsAPI.updateSettingsCache(); });
|
||||
},
|
||||
'users:roles': function createUsersWithRoles() { return fixtures.createUsersWithRoles(); },
|
||||
'users': function createExtraUsers() { return fixtures.createExtraUsers(); },
|
||||
'users': function createExtraUsers() { return fixtures.createExtraUsers(); },
|
||||
'owner': function insertOwnerUser() { return fixtures.insertOwnerUser(); },
|
||||
'owner:pre': function initOwnerUser() { return fixtures.initOwnerUser(); },
|
||||
'owner:post': function overrideOwnerUser() { return fixtures.overrideOwnerUser(); },
|
||||
|
Loading…
Reference in New Issue
Block a user