mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-29 07:09:48 +03:00
00cf043e15
no issue - reported in the forum: https://forum.ghost.org/t/publishing-with-a-single-post-request-to-posts/1648 - the defaults are defined in two places 1. on the schema level (defaults for the database) 2. on the ORM (model layer) - the defaults on the db layer are set correctly when inserting a new resource - but if we don't apply all defaults on the model layer, it will happen that model events are emitted without the correct defaults - see comment in code base - it's caused by the fact that knex only returns the inserted resource id (probably caused by the fact knex has to support x databases) - components/modules are listening on model events and expect: 1. a complete set of attributes 2. a complete set of defaults 3. sanitized values e.g. bool, date - this commit fixes: 1. added missing defaults for user & post model 2. sanitize booleans (0|1 => false|true) 3. added tests to ensure this works as expected 4. clarfies the usage of `defaults` Regarding https://forum.ghost.org/t/publishing-with-a-single-post-request-to-posts/1648: - the post event was emitted with the following values {page: undefined, featured: undefined} - the urlservice receives this event and won't match the resource against collection filters correctly - NOTE: the post data in the db were correct
1945 lines
83 KiB
JavaScript
1945 lines
83 KiB
JavaScript
/* eslint no-invalid-this:0 */
|
|
|
|
const should = require('should'),
|
|
sinon = require('sinon'),
|
|
_ = require('lodash'),
|
|
testUtils = require('../../utils'),
|
|
knex = require('../../../server/data/db').knex,
|
|
urlService = require('../../../server/services/url'),
|
|
schema = require('../../../server/data/schema'),
|
|
models = require('../../../server/models'),
|
|
common = require('../../../server/lib/common'),
|
|
security = require('../../../server/lib/security'),
|
|
sandbox = sinon.sandbox.create();
|
|
|
|
describe('Unit: models/post', function () {
|
|
before(function () {
|
|
models.init();
|
|
});
|
|
|
|
before(testUtils.teardown);
|
|
before(testUtils.setup('users:roles', 'posts'));
|
|
|
|
beforeEach(function () {
|
|
sandbox.stub(security.password, 'hash').resolves('$2a$10$we16f8rpbrFZ34xWj0/ZC.LTPUux8ler7bcdTs5qIleN6srRHhilG');
|
|
sandbox.stub(urlService, 'getUrlByResourceId');
|
|
});
|
|
|
|
afterEach(function () {
|
|
sandbox.restore();
|
|
});
|
|
|
|
after(function () {
|
|
sandbox.restore();
|
|
});
|
|
|
|
describe('add', function () {
|
|
describe('ensure full set of data for model events', function () {
|
|
it('default', function () {
|
|
const events = {
|
|
post: []
|
|
};
|
|
|
|
sandbox.stub(models.Post.prototype, 'emitChange').callsFake(function (event) {
|
|
events.post.push({event: event, data: this.toJSON()});
|
|
});
|
|
|
|
return models.Post.add({
|
|
title: 'My beautiful title.',
|
|
tags: [{
|
|
name: 'my-tag'
|
|
}]
|
|
}, testUtils.context.editor)
|
|
.then((post) => {
|
|
post.get('title').should.eql('My beautiful title.');
|
|
post = post.toJSON();
|
|
|
|
_.each(_.keys(_.omit(schema.tables.posts, ['mobiledoc', 'amp', 'plaintext'])), (key) => {
|
|
should.exist(post.hasOwnProperty(key));
|
|
|
|
if (['page', 'status', 'visibility', 'featured'].indexOf(key) !== -1) {
|
|
events.post[0].data[key].should.eql(schema.tables.posts[key].defaultTo);
|
|
}
|
|
});
|
|
|
|
should.not.exist(post.authors);
|
|
should.not.exist(post.primary_author);
|
|
should.not.exist(post.tags);
|
|
should.not.exist(post.primary_tag);
|
|
|
|
events.post[0].event.should.eql('added');
|
|
|
|
_.each(_.keys(_.omit(schema.tables.posts, ['mobiledoc', 'amp', 'plaintext'])), (key) => {
|
|
should.exist(events.post[0].data.hasOwnProperty(key));
|
|
|
|
if (['page', 'status', 'visibility', 'featured'].indexOf(key) !== -1) {
|
|
events.post[0].data[key].should.eql(schema.tables.posts[key].defaultTo);
|
|
}
|
|
});
|
|
|
|
should.exist(events.post[0].data.authors);
|
|
should.exist(events.post[0].data.primary_author);
|
|
should.exist(events.post[0].data.tags);
|
|
should.exist(events.post[0].data.primary_tag);
|
|
});
|
|
});
|
|
|
|
it('with page:1', function () {
|
|
const events = {
|
|
post: []
|
|
};
|
|
|
|
sandbox.stub(models.Post.prototype, 'emitChange').callsFake(function (event) {
|
|
events.post.push({event: event, data: this.toJSON()});
|
|
});
|
|
|
|
return models.Post.add({
|
|
title: 'My beautiful title.',
|
|
page: 1
|
|
}, testUtils.context.editor)
|
|
.then((post) => {
|
|
post.get('title').should.eql('My beautiful title.');
|
|
post = post.toJSON();
|
|
|
|
// transformed 1 to true
|
|
post.page.should.eql(true);
|
|
events.post[0].data.page.should.eql(true);
|
|
});
|
|
});
|
|
|
|
it('use `withRelated=tags`', function () {
|
|
const events = {
|
|
post: []
|
|
};
|
|
|
|
sandbox.stub(models.Post.prototype, 'emitChange').callsFake(function (event) {
|
|
events.post.push({event: event, data: this.toJSON()});
|
|
});
|
|
|
|
return models.Post.add({
|
|
title: 'My beautiful title.',
|
|
tags: [{
|
|
name: 'my-tag'
|
|
}]
|
|
}, _.merge({
|
|
withRelated: ['tags']
|
|
}, testUtils.context.editor))
|
|
.then((post) => {
|
|
post.get('title').should.eql('My beautiful title.');
|
|
post = post.toJSON();
|
|
|
|
should.not.exist(post.authors);
|
|
should.not.exist(post.primary_author);
|
|
should.exist(post.tags);
|
|
should.exist(post.primary_tag);
|
|
|
|
events.post[0].event.should.eql('added');
|
|
should.exist(events.post[0].data.authors);
|
|
should.exist(events.post[0].data.primary_author);
|
|
should.exist(events.post[0].data.tags);
|
|
should.exist(events.post[0].data.primary_tag);
|
|
});
|
|
});
|
|
|
|
it('use `withRelated=tags,authors`', function () {
|
|
const events = {
|
|
post: []
|
|
};
|
|
|
|
sandbox.stub(models.Post.prototype, 'emitChange').callsFake(function (event) {
|
|
events.post.push({event: event, data: this.toJSON()});
|
|
});
|
|
|
|
return models.Post.add({
|
|
title: 'My beautiful title.',
|
|
tags: [{
|
|
name: 'my-tag'
|
|
}]
|
|
}, _.merge({
|
|
withRelated: ['tags', 'authors']
|
|
}, testUtils.context.editor))
|
|
.then((post) => {
|
|
post.get('title').should.eql('My beautiful title.');
|
|
post = post.toJSON();
|
|
|
|
should.exist(post.authors);
|
|
should.exist(post.primary_author);
|
|
should.exist(post.tags);
|
|
should.exist(post.primary_tag);
|
|
|
|
events.post[0].event.should.eql('added');
|
|
|
|
should.exist(events.post[0].data.authors);
|
|
should.exist(events.post[0].data.primary_author);
|
|
should.exist(events.post[0].data.tags);
|
|
should.exist(events.post[0].data.primary_tag);
|
|
});
|
|
});
|
|
|
|
it('use `columns=title`', function () {
|
|
const events = {
|
|
post: []
|
|
};
|
|
|
|
sandbox.stub(models.Post.prototype, 'emitChange').callsFake(function (event) {
|
|
events.post.push({event: event, data: this.toJSON()});
|
|
});
|
|
|
|
return models.Post.add({
|
|
title: 'My beautiful title.',
|
|
tags: [{
|
|
name: 'my-tag'
|
|
}]
|
|
}, _.merge({
|
|
columns: ['title']
|
|
}, testUtils.context.editor))
|
|
.then((post) => {
|
|
post.get('title').should.eql('My beautiful title.');
|
|
post = post.toJSON();
|
|
|
|
_.each(_.keys(_.omit(schema.tables.posts, ['title', 'id'])), (key) => {
|
|
should.not.exist(post[key]);
|
|
});
|
|
|
|
should.exist(post.id);
|
|
should.exist(post.title);
|
|
|
|
should.not.exist(post.authors);
|
|
should.not.exist(post.primary_author);
|
|
should.not.exist(post.tags);
|
|
should.not.exist(post.primary_tag);
|
|
|
|
events.post[0].event.should.eql('added');
|
|
|
|
_.each(_.keys(_.omit(schema.tables.posts, ['mobiledoc', 'amp', 'plaintext'])), (key) => {
|
|
should.exist(events.post[0].data.hasOwnProperty(key));
|
|
});
|
|
|
|
should.exist(events.post[0].data.authors);
|
|
should.exist(events.post[0].data.primary_author);
|
|
should.exist(events.post[0].data.tags);
|
|
should.exist(events.post[0].data.primary_tag);
|
|
});
|
|
});
|
|
|
|
it('use `formats=mobiledoc`', function () {
|
|
const events = {
|
|
post: []
|
|
};
|
|
|
|
sandbox.stub(models.Post.prototype, 'emitChange').callsFake(function (event) {
|
|
events.post.push({event: event, data: this.toJSON()});
|
|
});
|
|
|
|
return models.Post.add({
|
|
title: 'My beautiful title.',
|
|
tags: [{
|
|
name: 'my-tag'
|
|
}]
|
|
}, _.merge({
|
|
formats: ['mobiledoc']
|
|
}, testUtils.context.editor))
|
|
.then((post) => {
|
|
post.get('title').should.eql('My beautiful title.');
|
|
post = post.toJSON();
|
|
|
|
_.each(_.keys(_.omit(schema.tables.posts, ['html', 'amp', 'plaintext'])), (key) => {
|
|
should.exist(post.hasOwnProperty(key));
|
|
});
|
|
|
|
should.not.exist(post.authors);
|
|
should.not.exist(post.primary_author);
|
|
should.not.exist(post.tags);
|
|
should.not.exist(post.primary_tag);
|
|
|
|
events.post[0].event.should.eql('added');
|
|
|
|
_.each(_.keys(_.omit(schema.tables.posts, ['mobiledoc', 'amp', 'plaintext'])), (key) => {
|
|
should.exist(events.post[0].data.hasOwnProperty(key));
|
|
});
|
|
|
|
should.exist(events.post[0].data.authors);
|
|
should.exist(events.post[0].data.primary_author);
|
|
should.exist(events.post[0].data.tags);
|
|
should.exist(events.post[0].data.primary_tag);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('edit', function () {
|
|
it('update post, relation has not changed', function () {
|
|
const events = {
|
|
post: [],
|
|
tag: []
|
|
};
|
|
|
|
sandbox.stub(models.Post.prototype, 'emitChange').callsFake(function (event) {
|
|
events.post.push(event);
|
|
});
|
|
|
|
sandbox.stub(models.Tag.prototype, 'emitChange').callsFake(function (event) {
|
|
events.tag.push(event);
|
|
});
|
|
|
|
return models.Post.findOne({
|
|
id: testUtils.DataGenerator.forKnex.posts[3].id,
|
|
status: 'draft'
|
|
}, {withRelated: ['tags']})
|
|
.then((post) => {
|
|
// post will be updated, tags relation not
|
|
return models.Post.edit({
|
|
title: 'change',
|
|
tags: post.related('tags').attributes
|
|
}, _.merge({id: testUtils.DataGenerator.forKnex.posts[3].id}, testUtils.context.editor));
|
|
})
|
|
.then((post) => {
|
|
post.updated('title').should.eql(testUtils.DataGenerator.forKnex.posts[3].title);
|
|
post.get('title').should.eql('change');
|
|
|
|
events.post.should.eql(['edited']);
|
|
events.tag.should.eql([]);
|
|
});
|
|
});
|
|
|
|
it('update post, relation has changed', function () {
|
|
const events = {
|
|
post: [],
|
|
tag: []
|
|
};
|
|
|
|
sandbox.stub(models.Post.prototype, 'emitChange').callsFake(function (event) {
|
|
events.post.push(event);
|
|
});
|
|
|
|
sandbox.stub(models.Tag.prototype, 'emitChange').callsFake(function (event) {
|
|
events.tag.push(event);
|
|
});
|
|
|
|
return models.Post.findOne({
|
|
id: testUtils.DataGenerator.forKnex.posts[3].id,
|
|
status: 'draft'
|
|
}, {withRelated: ['tags']})
|
|
.then((post) => {
|
|
// post will be updated, tags relation not
|
|
return models.Post.edit({
|
|
title: 'change',
|
|
tags: [{id: post.related('tags').toJSON()[0].id, slug: 'after'}]
|
|
}, _.merge({id: testUtils.DataGenerator.forKnex.posts[3].id}, testUtils.context.editor));
|
|
})
|
|
.then((post) => {
|
|
post.updated('title').should.eql('change');
|
|
post.get('title').should.eql('change');
|
|
|
|
events.post.should.eql(['edited']);
|
|
events.tag.should.eql(['edited']);
|
|
});
|
|
});
|
|
|
|
it('resets given empty value to null', function () {
|
|
return models.Post.findOne({slug: 'html-ipsum'})
|
|
.then(function (post) {
|
|
post.get('slug').should.eql('html-ipsum');
|
|
post.get('feature_image').should.eql('https://example.com/super_photo.jpg');
|
|
post.set('feature_image', '');
|
|
post.set('custom_excerpt', '');
|
|
return post.save();
|
|
})
|
|
.then(function (post) {
|
|
should(post.get('feature_image')).be.null();
|
|
post.get('custom_excerpt').should.eql('');
|
|
});
|
|
});
|
|
|
|
describe('ensure full set of data for model events', function () {
|
|
it('default', function () {
|
|
const events = {
|
|
post: []
|
|
};
|
|
|
|
sandbox.stub(models.Post.prototype, 'emitChange').callsFake(function (event) {
|
|
events.post.push({event: event, data: this.toJSON()});
|
|
});
|
|
|
|
return models.Post.edit({
|
|
title: 'My beautiful title.'
|
|
}, _.merge({id: testUtils.DataGenerator.forKnex.posts[3].id}, testUtils.context.editor))
|
|
.then((post) => {
|
|
post.get('title').should.eql('My beautiful title.');
|
|
post = post.toJSON();
|
|
|
|
_.each(_.keys(_.omit(schema.tables.posts, ['mobiledoc', 'amp', 'plaintext'])), (key) => {
|
|
should.exist(post.hasOwnProperty(key));
|
|
});
|
|
|
|
should.not.exist(post.authors);
|
|
should.not.exist(post.primary_author);
|
|
should.not.exist(post.tags);
|
|
should.not.exist(post.primary_tag);
|
|
|
|
events.post[0].event.should.eql('edited');
|
|
|
|
_.each(_.keys(_.omit(schema.tables.posts, ['mobiledoc', 'amp', 'plaintext'])), (key) => {
|
|
should.exist(events.post[0].data.hasOwnProperty(key));
|
|
});
|
|
|
|
should.exist(events.post[0].data.authors);
|
|
should.exist(events.post[0].data.primary_author);
|
|
should.exist(events.post[0].data.tags);
|
|
should.exist(events.post[0].data.primary_tag);
|
|
});
|
|
});
|
|
|
|
it('use `withRelated=tags`', function () {
|
|
const events = {
|
|
post: []
|
|
};
|
|
|
|
sandbox.stub(models.Post.prototype, 'emitChange').callsFake(function (event) {
|
|
events.post.push({event: event, data: this.toJSON()});
|
|
});
|
|
|
|
return models.Post.edit({
|
|
title: 'My beautiful title.'
|
|
}, _.merge({
|
|
id: testUtils.DataGenerator.forKnex.posts[3].id,
|
|
withRelated: ['tags']
|
|
}, testUtils.context.editor))
|
|
.then((post) => {
|
|
post.get('title').should.eql('My beautiful title.');
|
|
post = post.toJSON();
|
|
|
|
_.each(_.keys(_.omit(schema.tables.posts, ['mobiledoc', 'amp', 'plaintext'])), (key) => {
|
|
should.exist(post.hasOwnProperty(key));
|
|
});
|
|
|
|
should.not.exist(post.authors);
|
|
should.not.exist(post.primary_author);
|
|
should.exist(post.tags);
|
|
should.exist(post.primary_tag);
|
|
|
|
events.post[0].event.should.eql('edited');
|
|
|
|
_.each(_.keys(_.omit(schema.tables.posts, ['mobiledoc', 'amp', 'plaintext'])), (key) => {
|
|
should.exist(events.post[0].data.hasOwnProperty(key));
|
|
});
|
|
|
|
should.exist(events.post[0].data.authors);
|
|
should.exist(events.post[0].data.primary_author);
|
|
should.exist(events.post[0].data.tags);
|
|
should.exist(events.post[0].data.primary_tag);
|
|
});
|
|
});
|
|
|
|
it('use `withRelated=tags,authors`', function () {
|
|
const events = {
|
|
post: []
|
|
};
|
|
|
|
sandbox.stub(models.Post.prototype, 'emitChange').callsFake(function (event) {
|
|
events.post.push({event: event, data: this.toJSON()});
|
|
});
|
|
|
|
return models.Post.edit({
|
|
title: 'My beautiful title.'
|
|
}, _.merge({
|
|
id: testUtils.DataGenerator.forKnex.posts[3].id,
|
|
withRelated: ['tags', 'authors']
|
|
}, testUtils.context.editor))
|
|
.then((post) => {
|
|
post.get('title').should.eql('My beautiful title.');
|
|
post = post.toJSON();
|
|
|
|
_.each(_.keys(_.omit(schema.tables.posts, ['mobiledoc', 'amp', 'plaintext'])), (key) => {
|
|
should.exist(post.hasOwnProperty(key));
|
|
});
|
|
|
|
should.exist(post.authors);
|
|
should.exist(post.primary_author);
|
|
should.exist(post.tags);
|
|
should.exist(post.primary_tag);
|
|
|
|
events.post[0].event.should.eql('edited');
|
|
|
|
_.each(_.keys(_.omit(schema.tables.posts, ['mobiledoc', 'amp', 'plaintext'])), (key) => {
|
|
should.exist(events.post[0].data.hasOwnProperty(key));
|
|
});
|
|
|
|
should.exist(events.post[0].data.authors);
|
|
should.exist(events.post[0].data.primary_author);
|
|
should.exist(events.post[0].data.tags);
|
|
should.exist(events.post[0].data.primary_tag);
|
|
});
|
|
});
|
|
|
|
it('use `columns=title`', function () {
|
|
const events = {
|
|
post: []
|
|
};
|
|
|
|
sandbox.stub(models.Post.prototype, 'emitChange').callsFake(function (event) {
|
|
events.post.push({event: event, data: this.toJSON()});
|
|
});
|
|
|
|
return models.Post.edit({
|
|
title: 'My beautiful title.'
|
|
}, _.merge({
|
|
id: testUtils.DataGenerator.forKnex.posts[3].id,
|
|
columns: ['title']
|
|
}, testUtils.context.editor))
|
|
.then((post) => {
|
|
post.get('title').should.eql('My beautiful title.');
|
|
post = post.toJSON();
|
|
|
|
_.each(_.keys(_.omit(schema.tables.posts, ['title', 'id'])), (key) => {
|
|
should.not.exist(post[key]);
|
|
});
|
|
|
|
should.exist(post.id);
|
|
should.exist(post.title);
|
|
|
|
should.not.exist(post.authors);
|
|
should.not.exist(post.primary_author);
|
|
should.not.exist(post.tags);
|
|
should.not.exist(post.primary_tag);
|
|
|
|
events.post[0].event.should.eql('edited');
|
|
|
|
_.each(_.keys(_.omit(schema.tables.posts, ['mobiledoc', 'amp', 'plaintext'])), (key) => {
|
|
should.exist(events.post[0].data.hasOwnProperty(key));
|
|
});
|
|
|
|
should.exist(events.post[0].data.authors);
|
|
should.exist(events.post[0].data.primary_author);
|
|
should.exist(events.post[0].data.tags);
|
|
should.exist(events.post[0].data.primary_tag);
|
|
});
|
|
});
|
|
|
|
it('use `formats=mobiledoc`', function () {
|
|
const events = {
|
|
post: []
|
|
};
|
|
|
|
sandbox.stub(models.Post.prototype, 'emitChange').callsFake(function (event) {
|
|
events.post.push({event: event, data: this.toJSON()});
|
|
});
|
|
|
|
return models.Post.edit({
|
|
title: 'My beautiful title.'
|
|
}, _.merge({
|
|
id: testUtils.DataGenerator.forKnex.posts[3].id,
|
|
formats: ['mobiledoc']
|
|
}, testUtils.context.editor))
|
|
.then((post) => {
|
|
post.get('title').should.eql('My beautiful title.');
|
|
post = post.toJSON();
|
|
|
|
_.each(_.keys(_.omit(schema.tables.posts, ['html', 'amp', 'plaintext'])), (key) => {
|
|
should.exist(post.hasOwnProperty(key));
|
|
});
|
|
|
|
should.not.exist(post.authors);
|
|
should.not.exist(post.primary_author);
|
|
should.not.exist(post.tags);
|
|
should.not.exist(post.primary_tag);
|
|
|
|
events.post[0].event.should.eql('edited');
|
|
|
|
_.each(_.keys(_.omit(schema.tables.posts, ['mobiledoc', 'amp', 'plaintext'])), (key) => {
|
|
should.exist(events.post[0].data.hasOwnProperty(key));
|
|
});
|
|
|
|
should.exist(events.post[0].data.authors);
|
|
should.exist(events.post[0].data.primary_author);
|
|
should.exist(events.post[0].data.tags);
|
|
should.exist(events.post[0].data.primary_tag);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Relations', function () {
|
|
describe('author/authors', function () {
|
|
describe('add', function () {
|
|
it('with invalid post.author_id', function () {
|
|
const post = testUtils.DataGenerator.forKnex.createPost();
|
|
post.author_id = '12345';
|
|
|
|
return models.Post.add(post, {
|
|
context: {user: testUtils.DataGenerator.forKnex.users[2].id},
|
|
withRelated: ['author', 'authors']
|
|
}).then(function () {
|
|
'Expected error'.should.eql(false);
|
|
}).catch(function (err) {
|
|
(err instanceof common.errors.ValidationError).should.eql(true);
|
|
});
|
|
});
|
|
|
|
it('with invalid post.authors[0].id', function () {
|
|
const post = testUtils.DataGenerator.forKnex.createPost();
|
|
delete post.author_id;
|
|
delete post.author;
|
|
|
|
post.authors = [{
|
|
id: '12345'
|
|
}];
|
|
|
|
return models.Post.add(post, {
|
|
context: {user: testUtils.DataGenerator.forKnex.users[2].id},
|
|
withRelated: ['author', 'authors']
|
|
}).then(function () {
|
|
'Expected error'.should.eql(false);
|
|
}).catch(function (err) {
|
|
(err instanceof common.errors.ValidationError).should.eql(true);
|
|
});
|
|
});
|
|
|
|
// NOTE: this can be supported as soon as we remove the deprecation for post.author_id
|
|
it('[unsupported] insert post.authors[0]', function () {
|
|
const post = testUtils.DataGenerator.forKnex.createPost();
|
|
delete post.author_id;
|
|
delete post.author;
|
|
|
|
post.authors = [{
|
|
name: 'Gregor'
|
|
}];
|
|
|
|
return models.Post.add(post, {
|
|
context: {user: testUtils.DataGenerator.forKnex.users[2].id},
|
|
withRelated: ['author', 'authors']
|
|
}).then(function () {
|
|
'Expected error'.should.eql(false);
|
|
}).catch(function (err) {
|
|
(err[0] instanceof common.errors.ValidationError).should.eql(true);
|
|
});
|
|
});
|
|
|
|
it('with invalid post.author_id', function () {
|
|
const post = testUtils.DataGenerator.forKnex.createPost();
|
|
post.author_id = '12345';
|
|
|
|
return models.Post.add(post, {
|
|
context: {user: testUtils.DataGenerator.forKnex.users[2].id},
|
|
withRelated: ['author', 'authors']
|
|
}).then(function () {
|
|
'Expected error'.should.eql(false);
|
|
}).catch(function (err) {
|
|
(err instanceof common.errors.ValidationError).should.eql(true);
|
|
});
|
|
});
|
|
|
|
it('without author_id/author/authors', function () {
|
|
const post = testUtils.DataGenerator.forKnex.createPost();
|
|
delete post.author_id;
|
|
delete post.author;
|
|
delete post.authors;
|
|
|
|
return models.Post.add(post, {
|
|
context: {user: testUtils.DataGenerator.forKnex.users[2].id},
|
|
withRelated: ['author', 'authors']
|
|
}).then(function (post) {
|
|
post = post.toJSON();
|
|
post.author.id.should.eql(testUtils.DataGenerator.forKnex.users[2].id);
|
|
post.authors.length.should.eql(1);
|
|
post.authors[0].id.should.eql(testUtils.DataGenerator.forKnex.users[2].id);
|
|
});
|
|
});
|
|
|
|
it('without author/authors', function () {
|
|
const post = testUtils.DataGenerator.forKnex.createPost();
|
|
post.author_id.should.eql(testUtils.DataGenerator.forKnex.users[0].id);
|
|
|
|
return models.Post.add(post, {withRelated: ['author', 'authors']})
|
|
.then(function (post) {
|
|
post = post.toJSON();
|
|
post.author.id.should.eql(testUtils.DataGenerator.forKnex.users[0].id);
|
|
post.authors.length.should.eql(1);
|
|
post.authors[0].id.should.eql(testUtils.DataGenerator.forKnex.users[0].id);
|
|
});
|
|
});
|
|
|
|
it('without author/authors', function () {
|
|
const post = testUtils.DataGenerator.forKnex.createPost();
|
|
post.author_id.should.eql(testUtils.DataGenerator.forKnex.users[0].id);
|
|
|
|
return models.Post.add(post, {withRelated: ['author']})
|
|
.then(function (post) {
|
|
post = post.toJSON();
|
|
post.author.id.should.eql(testUtils.DataGenerator.forKnex.users[0].id);
|
|
should.not.exist(post.authors);
|
|
});
|
|
});
|
|
|
|
it('with author, with author_id', function () {
|
|
const post = testUtils.DataGenerator.forKnex.createPost();
|
|
post.author_id.should.eql(testUtils.DataGenerator.forKnex.users[0].id);
|
|
post.author = {
|
|
id: testUtils.DataGenerator.forKnex.users[1].id
|
|
};
|
|
|
|
return models.Post.add(post, {withRelated: ['author']})
|
|
.then(function (post) {
|
|
post = post.toJSON();
|
|
post.author.id.should.eql(testUtils.DataGenerator.forKnex.users[0].id);
|
|
should.not.exist(post.authors);
|
|
});
|
|
});
|
|
|
|
it('[unsupported] with author, without author_id', function () {
|
|
const post = testUtils.DataGenerator.forKnex.createPost();
|
|
delete post.author_id;
|
|
post.author = {
|
|
id: testUtils.DataGenerator.forKnex.users[2].id
|
|
};
|
|
|
|
return models.Post.add(post, {
|
|
withRelated: ['author'],
|
|
context: {user: testUtils.DataGenerator.forKnex.users[0].id}
|
|
}).then(function (post) {
|
|
post = post.toJSON();
|
|
|
|
// no update happened, because `post.author` is ignored
|
|
post.author.id.should.eql(testUtils.DataGenerator.forKnex.users[0].id);
|
|
should.not.exist(post.authors);
|
|
});
|
|
});
|
|
|
|
it('[not allowed] with empty authors ([]), without author_id', function () {
|
|
const post = testUtils.DataGenerator.forKnex.createPost();
|
|
delete post.author_id;
|
|
post.authors = [];
|
|
|
|
return models.Post.add(post, {withRelated: ['author', 'authors']})
|
|
.then(function () {
|
|
'Expected error'.should.eql(false);
|
|
})
|
|
.catch(function (err) {
|
|
(err instanceof common.errors.ValidationError).should.eql(true);
|
|
});
|
|
});
|
|
|
|
it('[not allowed] with empty authors ([]), with author_id', function () {
|
|
const post = testUtils.DataGenerator.forKnex.createPost();
|
|
post.author_id.should.eql(testUtils.DataGenerator.forKnex.users[0].id);
|
|
post.authors = [];
|
|
|
|
return models.Post.add(post, {withRelated: ['author', 'authors']})
|
|
.then(function () {
|
|
'Expected error'.should.eql(false);
|
|
})
|
|
.catch(function (err) {
|
|
(err instanceof common.errors.ValidationError).should.eql(true);
|
|
});
|
|
});
|
|
|
|
it('with authors, with author_id', function () {
|
|
const post = testUtils.DataGenerator.forKnex.createPost();
|
|
post.author_id.should.eql(testUtils.DataGenerator.forKnex.users[0].id);
|
|
post.authors = [{
|
|
id: testUtils.DataGenerator.forKnex.users[0].id
|
|
}];
|
|
|
|
return models.Post.add(post, {withRelated: ['author', 'authors']})
|
|
.then(function (post) {
|
|
post = post.toJSON();
|
|
post.author.id.should.eql(testUtils.DataGenerator.forKnex.users[0].id);
|
|
post.authors.length.should.eql(1);
|
|
post.authors[0].id.should.eql(testUtils.DataGenerator.forKnex.users[0].id);
|
|
});
|
|
});
|
|
|
|
it('with authors, without author_id', function () {
|
|
const post = testUtils.DataGenerator.forKnex.createPost();
|
|
delete post.author_id;
|
|
post.authors = [{
|
|
id: testUtils.DataGenerator.forKnex.users[0].id
|
|
}];
|
|
|
|
return models.Post.add(post, {
|
|
context: {user: testUtils.DataGenerator.forKnex.users[0].id},
|
|
withRelated: ['author', 'authors']
|
|
}).then(function (post) {
|
|
post = post.toJSON();
|
|
post.author.id.should.eql(testUtils.DataGenerator.forKnex.users[0].id);
|
|
post.authors.length.should.eql(1);
|
|
post.authors[0].id.should.eql(testUtils.DataGenerator.forKnex.users[0].id);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('findOne', function () {
|
|
it('withRelated: []', function () {
|
|
return models.Post.findOne({
|
|
id: testUtils.DataGenerator.forKnex.posts[3].id,
|
|
status: 'draft'
|
|
}, {withRelated: []})
|
|
.then(function (post) {
|
|
post = post.toJSON();
|
|
post.author.should.eql(testUtils.DataGenerator.forKnex.users[0].id);
|
|
should.not.exist(post.authors);
|
|
should.not.exist(post.author_id);
|
|
});
|
|
});
|
|
|
|
it('withRelated: [author]', function () {
|
|
return models.Post.findOne({
|
|
id: testUtils.DataGenerator.forKnex.posts[3].id,
|
|
status: 'draft'
|
|
}, {withRelated: ['author']})
|
|
.then(function (post) {
|
|
post = post.toJSON();
|
|
post.author.id.should.eql(testUtils.DataGenerator.forKnex.users[0].id);
|
|
should.not.exist(post.authors);
|
|
});
|
|
});
|
|
|
|
it('withRelated: [authors]', function () {
|
|
return models.Post.findOne({
|
|
id: testUtils.DataGenerator.forKnex.posts[3].id,
|
|
status: 'draft'
|
|
}, {withRelated: ['authors']})
|
|
.then(function (post) {
|
|
post = post.toJSON();
|
|
post.author.should.eql(testUtils.DataGenerator.forKnex.users[0].id);
|
|
post.authors.length.should.eql(2);
|
|
post.authors[0].id.should.eql(testUtils.DataGenerator.forKnex.users[0].id);
|
|
post.authors[1].id.should.eql(testUtils.DataGenerator.forKnex.users[2].id);
|
|
});
|
|
});
|
|
|
|
it('withRelated: [authors, author]', function () {
|
|
return models.Post.findOne({
|
|
id: testUtils.DataGenerator.forKnex.posts[3].id,
|
|
status: 'draft'
|
|
}, {withRelated: ['authors', 'author']})
|
|
.then(function (post) {
|
|
post = post.toJSON();
|
|
post.author.id.should.eql(testUtils.DataGenerator.forKnex.users[0].id);
|
|
post.authors.length.should.eql(2);
|
|
post.authors[0].id.should.eql(testUtils.DataGenerator.forKnex.users[0].id);
|
|
post.authors[1].id.should.eql(testUtils.DataGenerator.forKnex.users[2].id);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('edit', function () {
|
|
beforeEach(testUtils.teardown);
|
|
beforeEach(testUtils.setup('users:roles', 'posts'));
|
|
|
|
beforeEach(function () {
|
|
// posts[3] has the following author_id
|
|
testUtils.DataGenerator.forKnex.posts[3].author_id.should.eql(testUtils.DataGenerator.forKnex.users[0].id);
|
|
|
|
// posts[3] has two authors relations
|
|
testUtils.DataGenerator.forKnex.posts_authors[3].post_id.should.eql(testUtils.DataGenerator.forKnex.posts[3].id);
|
|
testUtils.DataGenerator.forKnex.posts_authors[3].author_id.should.eql(testUtils.DataGenerator.forKnex.users[0].id);
|
|
testUtils.DataGenerator.forKnex.posts_authors[4].post_id.should.eql(testUtils.DataGenerator.forKnex.posts[3].id);
|
|
testUtils.DataGenerator.forKnex.posts_authors[4].author_id.should.eql(testUtils.DataGenerator.forKnex.users[2].id);
|
|
});
|
|
|
|
it('[not allowed] post.authors = []', function () {
|
|
const data = {
|
|
authors: []
|
|
};
|
|
|
|
return models.Post.edit(data, {
|
|
id: testUtils.DataGenerator.forKnex.posts[3].id,
|
|
withRelated: ['authors', 'author']
|
|
}).then(function () {
|
|
'Expected Error'.should.eql(true);
|
|
}).catch(function (err) {
|
|
(err instanceof common.errors.ValidationError).should.be.true;
|
|
});
|
|
});
|
|
|
|
it('[not allowed] primary authors are not equal', function () {
|
|
const data = {
|
|
author_id: testUtils.DataGenerator.forKnex.users[2].id,
|
|
authors: [{
|
|
id: testUtils.DataGenerator.forKnex.users[1].id
|
|
}]
|
|
};
|
|
|
|
return models.Post.edit(data, {
|
|
id: testUtils.DataGenerator.forKnex.posts[3].id,
|
|
withRelated: ['authors', 'author']
|
|
}).then(function () {
|
|
'Expected Error'.should.eql(true);
|
|
}).catch(function (err) {
|
|
(err instanceof common.errors.ValidationError).should.be.true;
|
|
});
|
|
});
|
|
|
|
it('[not allowed] primary authors are not equal', function () {
|
|
const data = {
|
|
author: {
|
|
id: testUtils.DataGenerator.forKnex.users[2].id
|
|
},
|
|
authors: [{
|
|
id: testUtils.DataGenerator.forKnex.users[1].id
|
|
}]
|
|
};
|
|
|
|
return models.Post.edit(data, {
|
|
id: testUtils.DataGenerator.forKnex.posts[3].id,
|
|
withRelated: ['authors', 'author']
|
|
}).then(function () {
|
|
'Expected Error'.should.eql(true);
|
|
}).catch(function (err) {
|
|
(err instanceof common.errors.ValidationError).should.be.true;
|
|
});
|
|
});
|
|
|
|
it('change post.author_id [has existing post.authors]', function () {
|
|
const data = {
|
|
author_id: testUtils.DataGenerator.forKnex.users[1].id
|
|
};
|
|
|
|
return models.Post.edit(data, {
|
|
id: testUtils.DataGenerator.forKnex.posts[3].id,
|
|
withRelated: ['authors', 'author']
|
|
}).then(function (post) {
|
|
post = post.toJSON();
|
|
post.author.id.should.eql(testUtils.DataGenerator.forKnex.users[1].id);
|
|
post.authors.length.should.eql(2);
|
|
post.authors[0].id.should.eql(testUtils.DataGenerator.forKnex.users[1].id);
|
|
post.authors[1].id.should.eql(testUtils.DataGenerator.forKnex.users[2].id);
|
|
});
|
|
});
|
|
|
|
it('change post.author_id [has existing post.authors] [without `withRelated`]', function () {
|
|
const data = {
|
|
author_id: testUtils.DataGenerator.forKnex.users[1].id
|
|
};
|
|
|
|
return models.Post.edit(data, {
|
|
id: testUtils.DataGenerator.forKnex.posts[3].id
|
|
}).then(function (post) {
|
|
post = post.toJSON();
|
|
post.author.should.eql(testUtils.DataGenerator.forKnex.users[1].id);
|
|
should.not.exist(post.authors);
|
|
return models.Post.findOne({
|
|
id: testUtils.DataGenerator.forKnex.posts[3].id,
|
|
status: 'draft'
|
|
}, {withRelated: ['authors']});
|
|
}).then(function (post) {
|
|
post = post.toJSON();
|
|
post.authors.length.should.eql(2);
|
|
post.authors[0].id.should.eql(testUtils.DataGenerator.forKnex.users[1].id);
|
|
post.authors[1].id.should.eql(testUtils.DataGenerator.forKnex.users[2].id);
|
|
});
|
|
});
|
|
|
|
it('change post.authors', function () {
|
|
testUtils.DataGenerator.forKnex.posts[3].author_id.should.not.equal(testUtils.DataGenerator.forKnex.users[3].id);
|
|
|
|
const data = {
|
|
authors: [
|
|
{
|
|
id: testUtils.DataGenerator.forKnex.users[3].id
|
|
},
|
|
{
|
|
id: testUtils.DataGenerator.forKnex.users[2].id
|
|
}
|
|
]
|
|
};
|
|
|
|
return models.Post.edit(data, {
|
|
id: testUtils.DataGenerator.forKnex.posts[3].id,
|
|
withRelated: ['authors', 'author']
|
|
}).then(function (post) {
|
|
post = post.toJSON();
|
|
post.author.id.should.eql(testUtils.DataGenerator.forKnex.users[3].id);
|
|
post.authors.length.should.eql(2);
|
|
post.authors[0].id.should.eql(testUtils.DataGenerator.forKnex.users[3].id);
|
|
post.authors[1].id.should.eql(testUtils.DataGenerator.forKnex.users[2].id);
|
|
});
|
|
});
|
|
|
|
it('change post.authors, do not include `author`', function () {
|
|
testUtils.DataGenerator.forKnex.posts[3].author_id.should.not.equal(testUtils.DataGenerator.forKnex.users[3].id);
|
|
|
|
const data = {
|
|
authors: [
|
|
{
|
|
id: testUtils.DataGenerator.forKnex.users[3].id
|
|
},
|
|
{
|
|
id: testUtils.DataGenerator.forKnex.users[2].id
|
|
}
|
|
]
|
|
};
|
|
|
|
return models.Post.edit(data, {
|
|
id: testUtils.DataGenerator.forKnex.posts[3].id,
|
|
withRelated: ['authors']
|
|
}).then(function (post) {
|
|
post = post.toJSON();
|
|
post.author.should.eql(testUtils.DataGenerator.forKnex.users[3].id);
|
|
post.authors.length.should.eql(2);
|
|
post.authors[0].id.should.eql(testUtils.DataGenerator.forKnex.users[3].id);
|
|
post.authors[1].id.should.eql(testUtils.DataGenerator.forKnex.users[2].id);
|
|
});
|
|
});
|
|
|
|
it('change post.authors and post.author_id (different primary author)', function () {
|
|
const data = {
|
|
authors: [
|
|
{
|
|
id: testUtils.DataGenerator.forKnex.users[1].id
|
|
}
|
|
],
|
|
author_id: testUtils.DataGenerator.forKnex.users[4].id
|
|
};
|
|
|
|
return models.Post.edit(data, {
|
|
id: testUtils.DataGenerator.forKnex.posts[3].id,
|
|
withRelated: ['authors', 'author']
|
|
}).then(function (post) {
|
|
post = post.toJSON();
|
|
post.author.id.should.eql(testUtils.DataGenerator.forKnex.users[1].id);
|
|
post.authors.length.should.eql(1);
|
|
post.authors[0].id.should.eql(testUtils.DataGenerator.forKnex.users[1].id);
|
|
});
|
|
});
|
|
|
|
it('change order of existing post.authors', function () {
|
|
const data = {
|
|
authors: [
|
|
{
|
|
id: testUtils.DataGenerator.forKnex.users[2].id
|
|
},
|
|
{
|
|
id: testUtils.DataGenerator.forKnex.users[0].id
|
|
}
|
|
]
|
|
};
|
|
|
|
return models.Post.edit(data, {
|
|
id: testUtils.DataGenerator.forKnex.posts[3].id,
|
|
withRelated: ['authors', 'author']
|
|
}).then(function (post) {
|
|
post = post.toJSON();
|
|
post.author.id.should.eql(testUtils.DataGenerator.forKnex.users[2].id);
|
|
post.authors.length.should.eql(2);
|
|
post.authors[0].id.should.eql(testUtils.DataGenerator.forKnex.users[2].id);
|
|
post.authors[1].id.should.eql(testUtils.DataGenerator.forKnex.users[0].id);
|
|
});
|
|
});
|
|
|
|
it('[unsupported] change post.author', function () {
|
|
const data = {
|
|
author: {
|
|
id: testUtils.DataGenerator.forKnex.users[4].id
|
|
}
|
|
};
|
|
|
|
return models.Post.edit(data, {
|
|
id: testUtils.DataGenerator.forKnex.posts[3].id,
|
|
withRelated: ['authors', 'author']
|
|
}).then(function (post) {
|
|
post = post.toJSON();
|
|
post.author.id.should.eql(testUtils.DataGenerator.forKnex.users[0].id);
|
|
post.authors.length.should.eql(2);
|
|
post.authors[0].id.should.eql(testUtils.DataGenerator.forKnex.users[0].id);
|
|
post.authors[1].id.should.eql(testUtils.DataGenerator.forKnex.users[2].id);
|
|
});
|
|
});
|
|
|
|
it('[unsupported] change post.plaintext', function () {
|
|
const data = {
|
|
plaintext: 'test'
|
|
};
|
|
|
|
return models.Post.edit(data, {
|
|
id: testUtils.DataGenerator.forKnex.posts[2].id
|
|
}).then(function (post) {
|
|
post = post.toJSON({formats: ['mobiledoc', 'plaintext', 'html']});
|
|
post.plaintext.should.eql(testUtils.DataGenerator.forKnex.posts[2].plaintext);
|
|
});
|
|
});
|
|
|
|
it('[unsupported] change post.html', function () {
|
|
const data = {
|
|
html: 'test'
|
|
};
|
|
|
|
return models.Post.edit(data, {
|
|
id: testUtils.DataGenerator.forKnex.posts[2].id
|
|
}).then(function (post) {
|
|
post = post.toJSON({formats: ['mobiledoc', 'plaintext', 'html']});
|
|
post.html.should.eql(testUtils.DataGenerator.forKnex.posts[2].html);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('destroy', function () {
|
|
it('by author', function () {
|
|
const authorId = testUtils.DataGenerator.forKnex.users[0].id;
|
|
|
|
return knex('posts_authors')
|
|
.where('author_id', authorId)
|
|
.then(function (postAuthors) {
|
|
postAuthors.length.should.eql(8);
|
|
|
|
return models.Post.destroyByAuthor({id: authorId});
|
|
})
|
|
.then(function () {
|
|
return knex('posts_authors')
|
|
.where('author_id', authorId);
|
|
})
|
|
.then(function (postAuthors) {
|
|
postAuthors.length.should.eql(0);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Permissible', function () {
|
|
describe('As Contributor', function () {
|
|
describe('Editing', function () {
|
|
it('rejects if changing status', function (done) {
|
|
var mockPostObj = {
|
|
get: sandbox.stub(),
|
|
related: sandbox.stub()
|
|
},
|
|
context = {user: 1},
|
|
unsafeAttrs = {status: 'published'};
|
|
|
|
mockPostObj.get.withArgs('status').returns('draft');
|
|
mockPostObj.related.withArgs('authors').returns({models: [{id: 1}]});
|
|
|
|
models.Post.permissible(
|
|
mockPostObj,
|
|
'edit',
|
|
context,
|
|
unsafeAttrs,
|
|
testUtils.permissions.contributor,
|
|
false,
|
|
false
|
|
).then(() => {
|
|
done(new Error('Permissible function should have rejected.'));
|
|
}).catch((error) => {
|
|
error.should.be.an.instanceof(common.errors.NoPermissionError);
|
|
should(mockPostObj.get.called).be.false();
|
|
should(mockPostObj.related.calledOnce).be.true();
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('rejects if changing author id', function (done) {
|
|
var mockPostObj = {
|
|
get: sandbox.stub(),
|
|
related: sandbox.stub()
|
|
},
|
|
context = {user: 1},
|
|
unsafeAttrs = {status: 'draft', author_id: 2};
|
|
|
|
mockPostObj.get.withArgs('author_id').returns(1);
|
|
|
|
models.Post.permissible(
|
|
mockPostObj,
|
|
'edit',
|
|
context,
|
|
unsafeAttrs,
|
|
testUtils.permissions.contributor,
|
|
false,
|
|
true
|
|
).then(() => {
|
|
done(new Error('Permissible function should have rejected.'));
|
|
}).catch((error) => {
|
|
error.should.be.an.instanceof(common.errors.NoPermissionError);
|
|
should(mockPostObj.get.calledOnce).be.true();
|
|
should(mockPostObj.related.called).be.false();
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('rejects if changing authors.0', function (done) {
|
|
var mockPostObj = {
|
|
get: sandbox.stub(),
|
|
related: sandbox.stub()
|
|
},
|
|
context = {user: 1},
|
|
unsafeAttrs = {status: 'draft', authors: [{id: 2}]};
|
|
|
|
mockPostObj.related.withArgs('authors').returns({models: [{id: 1}]});
|
|
|
|
models.Post.permissible(
|
|
mockPostObj,
|
|
'edit',
|
|
context,
|
|
unsafeAttrs,
|
|
testUtils.permissions.contributor,
|
|
false,
|
|
true
|
|
).then(() => {
|
|
done(new Error('Permissible function should have rejected.'));
|
|
}).catch((error) => {
|
|
error.should.be.an.instanceof(common.errors.NoPermissionError);
|
|
should(mockPostObj.get.called).be.false();
|
|
should(mockPostObj.related.calledTwice).be.false();
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('ignores if changes authors.1', function (done) {
|
|
var mockPostObj = {
|
|
get: sandbox.stub(),
|
|
related: sandbox.stub()
|
|
},
|
|
context = {user: 1},
|
|
unsafeAttrs = {status: 'draft', authors: [{id: 1}, {id: 2}]};
|
|
|
|
mockPostObj.related.withArgs('authors').returns({models: [{id: 1}]});
|
|
mockPostObj.get.withArgs('status').returns('draft');
|
|
|
|
models.Post.permissible(
|
|
mockPostObj,
|
|
'edit',
|
|
context,
|
|
unsafeAttrs,
|
|
testUtils.permissions.contributor,
|
|
false,
|
|
true
|
|
).then((result) => {
|
|
should.exist(result);
|
|
should(result.excludedAttrs).deepEqual(['authors', 'tags']);
|
|
should(mockPostObj.get.callCount).eql(2);
|
|
should(mockPostObj.related.callCount).eql(2);
|
|
done();
|
|
}).catch(done);
|
|
});
|
|
|
|
it('rejects if post is not draft', function (done) {
|
|
var mockPostObj = {
|
|
get: sandbox.stub(),
|
|
related: sandbox.stub()
|
|
},
|
|
context = {user: 1},
|
|
unsafeAttrs = {status: 'published', author_id: 1};
|
|
|
|
mockPostObj.get.withArgs('status').returns('published');
|
|
mockPostObj.get.withArgs('author_id').returns(1);
|
|
mockPostObj.related.withArgs('authors').returns({models: [{id: 1}]});
|
|
|
|
models.Post.permissible(
|
|
mockPostObj,
|
|
'edit',
|
|
context,
|
|
unsafeAttrs,
|
|
testUtils.permissions.contributor,
|
|
false,
|
|
true
|
|
).then(() => {
|
|
done(new Error('Permissible function should have rejected.'));
|
|
}).catch((error) => {
|
|
error.should.be.an.instanceof(common.errors.NoPermissionError);
|
|
should(mockPostObj.get.callCount).eql(3);
|
|
should(mockPostObj.related.callCount).eql(1);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('rejects if contributor is not author of post', function (done) {
|
|
var mockPostObj = {
|
|
get: sandbox.stub(),
|
|
related: sandbox.stub()
|
|
},
|
|
context = {user: 1},
|
|
unsafeAttrs = {status: 'draft', author_id: 2};
|
|
|
|
mockPostObj.get.withArgs('status').returns('draft');
|
|
mockPostObj.get.withArgs('author_id').returns(1);
|
|
mockPostObj.related.withArgs('authors').returns({models: [{id: 1}]});
|
|
|
|
models.Post.permissible(
|
|
mockPostObj,
|
|
'edit',
|
|
context,
|
|
unsafeAttrs,
|
|
testUtils.permissions.contributor,
|
|
false,
|
|
true
|
|
).then(() => {
|
|
done(new Error('Permissible function should have rejected.'));
|
|
}).catch((error) => {
|
|
error.should.be.an.instanceof(common.errors.NoPermissionError);
|
|
should(mockPostObj.get.callCount).eql(1);
|
|
should(mockPostObj.related.callCount).eql(0);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('resolves if none of the above cases are true', function () {
|
|
var mockPostObj = {
|
|
get: sandbox.stub(),
|
|
related: sandbox.stub()
|
|
},
|
|
context = {user: 1},
|
|
unsafeAttrs = {status: 'draft', author_id: 1};
|
|
|
|
mockPostObj.get.withArgs('status').returns('draft');
|
|
mockPostObj.get.withArgs('author_id').returns(1);
|
|
mockPostObj.related.withArgs('authors').returns({models: [{id: 1}]});
|
|
|
|
return models.Post.permissible(
|
|
mockPostObj,
|
|
'edit',
|
|
context,
|
|
unsafeAttrs,
|
|
testUtils.permissions.contributor,
|
|
false,
|
|
true
|
|
).then((result) => {
|
|
should.exist(result);
|
|
should(result.excludedAttrs).deepEqual(['authors', 'tags']);
|
|
should(mockPostObj.get.callCount).eql(3);
|
|
should(mockPostObj.related.callCount).eql(1);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Adding', function () {
|
|
it('rejects if "published" status', function (done) {
|
|
var mockPostObj = {
|
|
get: sandbox.stub()
|
|
},
|
|
context = {user: 1},
|
|
unsafeAttrs = {status: 'published', author_id: 1};
|
|
|
|
models.Post.permissible(
|
|
mockPostObj,
|
|
'add',
|
|
context,
|
|
unsafeAttrs,
|
|
testUtils.permissions.contributor,
|
|
false,
|
|
true
|
|
).then(() => {
|
|
done(new Error('Permissible function should have rejected.'));
|
|
}).catch((error) => {
|
|
error.should.be.an.instanceof(common.errors.NoPermissionError);
|
|
should(mockPostObj.get.called).be.false();
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('rejects if different author id', function (done) {
|
|
var mockPostObj = {
|
|
get: sandbox.stub()
|
|
},
|
|
context = {user: 1},
|
|
unsafeAttrs = {status: 'draft', author_id: 2};
|
|
|
|
models.Post.permissible(
|
|
mockPostObj,
|
|
'add',
|
|
context,
|
|
unsafeAttrs,
|
|
testUtils.permissions.contributor,
|
|
false,
|
|
true
|
|
).then(() => {
|
|
done(new Error('Permissible function should have rejected.'));
|
|
}).catch((error) => {
|
|
error.should.be.an.instanceof(common.errors.NoPermissionError);
|
|
should(mockPostObj.get.called).be.false();
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('rejects if different logged in user and `authors.0`', function (done) {
|
|
var mockPostObj = {
|
|
get: sandbox.stub()
|
|
},
|
|
context = {user: 1},
|
|
unsafeAttrs = {status: 'draft', authors: [{id: 2}]};
|
|
|
|
models.Post.permissible(
|
|
mockPostObj,
|
|
'add',
|
|
context,
|
|
unsafeAttrs,
|
|
testUtils.permissions.contributor,
|
|
false,
|
|
true
|
|
).then(() => {
|
|
done(new Error('Permissible function should have rejected.'));
|
|
}).catch((error) => {
|
|
error.should.be.an.instanceof(common.errors.NoPermissionError);
|
|
should(mockPostObj.get.called).be.false();
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('rejects if same logged in user and `authors.0`, but different author_id', function (done) {
|
|
var mockPostObj = {
|
|
get: sandbox.stub()
|
|
},
|
|
context = {user: 1},
|
|
unsafeAttrs = {status: 'draft', author_id: 3, authors: [{id: 1}]};
|
|
|
|
models.Post.permissible(
|
|
mockPostObj,
|
|
'add',
|
|
context,
|
|
unsafeAttrs,
|
|
testUtils.permissions.contributor,
|
|
false,
|
|
true
|
|
).then(() => {
|
|
done(new Error('Permissible function should have rejected.'));
|
|
}).catch((error) => {
|
|
error.should.be.an.instanceof(common.errors.NoPermissionError);
|
|
should(mockPostObj.get.called).be.false();
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('rejects if different logged in user and `authors.0`, but correct author_id', function (done) {
|
|
var mockPostObj = {
|
|
get: sandbox.stub()
|
|
},
|
|
context = {user: 1},
|
|
unsafeAttrs = {status: 'draft', author_id: 1, authors: [{id: 2}]};
|
|
|
|
models.Post.permissible(
|
|
mockPostObj,
|
|
'add',
|
|
context,
|
|
unsafeAttrs,
|
|
testUtils.permissions.contributor,
|
|
false,
|
|
true
|
|
).then(() => {
|
|
done(new Error('Permissible function should have rejected.'));
|
|
}).catch((error) => {
|
|
error.should.be.an.instanceof(common.errors.NoPermissionError);
|
|
should(mockPostObj.get.called).be.false();
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('resolves if same logged in user and `authors.0`', function (done) {
|
|
var mockPostObj = {
|
|
get: sandbox.stub()
|
|
},
|
|
context = {user: 1},
|
|
unsafeAttrs = {status: 'draft', authors: [{id: 1}]};
|
|
|
|
models.Post.permissible(
|
|
mockPostObj,
|
|
'add',
|
|
context,
|
|
unsafeAttrs,
|
|
testUtils.permissions.contributor,
|
|
false,
|
|
true
|
|
).then((result) => {
|
|
should.exist(result);
|
|
should(result.excludedAttrs).deepEqual(['authors', 'tags']);
|
|
should(mockPostObj.get.called).be.false();
|
|
done();
|
|
}).catch(done);
|
|
});
|
|
|
|
it('resolves if none of the above cases are true', function () {
|
|
var mockPostObj = {
|
|
get: sandbox.stub()
|
|
},
|
|
context = {user: 1},
|
|
unsafeAttrs = {status: 'draft', author_id: 1};
|
|
|
|
return models.Post.permissible(
|
|
mockPostObj,
|
|
'add',
|
|
context,
|
|
unsafeAttrs,
|
|
testUtils.permissions.contributor,
|
|
false,
|
|
true
|
|
).then((result) => {
|
|
should.exist(result);
|
|
should(result.excludedAttrs).deepEqual(['authors', 'tags']);
|
|
should(mockPostObj.get.called).be.false();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Destroying', function () {
|
|
it('rejects if destroying another author\'s post', function (done) {
|
|
var mockPostObj = {
|
|
get: sandbox.stub(),
|
|
related: sandbox.stub()
|
|
},
|
|
context = {user: 1};
|
|
|
|
mockPostObj.related.withArgs('authors').returns({models: [{id: 1}]});
|
|
|
|
models.Post.permissible(
|
|
mockPostObj,
|
|
'destroy',
|
|
context,
|
|
{},
|
|
testUtils.permissions.contributor,
|
|
false,
|
|
true
|
|
).then(() => {
|
|
done(new Error('Permissible function should have rejected.'));
|
|
}).catch((error) => {
|
|
error.should.be.an.instanceof(common.errors.NoPermissionError);
|
|
should(mockPostObj.get.calledOnce).be.true();
|
|
should(mockPostObj.related.calledOnce).be.true();
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('rejects if destroying a published post', function (done) {
|
|
var mockPostObj = {
|
|
get: sandbox.stub(),
|
|
related: sandbox.stub()
|
|
},
|
|
context = {user: 1};
|
|
|
|
mockPostObj.related.withArgs('authors').returns({models: [{id: 1}]});
|
|
mockPostObj.get.withArgs('status').returns('published');
|
|
|
|
models.Post.permissible(
|
|
mockPostObj,
|
|
'destroy',
|
|
context,
|
|
{},
|
|
testUtils.permissions.contributor,
|
|
false,
|
|
true
|
|
).then(() => {
|
|
done(new Error('Permissible function should have rejected.'));
|
|
}).catch((error) => {
|
|
error.should.be.an.instanceof(common.errors.NoPermissionError);
|
|
should(mockPostObj.get.calledOnce).be.true();
|
|
should(mockPostObj.related.calledOnce).be.true();
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('resolves if none of the above cases are true', function () {
|
|
var mockPostObj = {
|
|
get: sandbox.stub(),
|
|
related: sandbox.stub()
|
|
},
|
|
context = {user: 1};
|
|
|
|
mockPostObj.get.withArgs('status').returns('draft');
|
|
mockPostObj.related.withArgs('authors').returns({models: [{id: 1}]});
|
|
|
|
return models.Post.permissible(
|
|
mockPostObj,
|
|
'destroy',
|
|
context,
|
|
{},
|
|
testUtils.permissions.contributor,
|
|
false,
|
|
true
|
|
).then((result) => {
|
|
should.exist(result);
|
|
should(result.excludedAttrs).deepEqual(['authors', 'tags']);
|
|
should(mockPostObj.get.calledOnce).be.true();
|
|
should(mockPostObj.related.calledOnce).be.true();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('As Author', function () {
|
|
describe('Editing', function () {
|
|
it('rejects if editing another\'s post', function (done) {
|
|
var mockPostObj = {
|
|
get: sandbox.stub(),
|
|
related: sandbox.stub()
|
|
},
|
|
context = {user: 1},
|
|
unsafeAttrs = {author_id: 2};
|
|
|
|
mockPostObj.related.withArgs('authors').returns({models: [{id: 2}]});
|
|
mockPostObj.get.withArgs('author_id').returns(2);
|
|
|
|
models.Post.permissible(
|
|
mockPostObj,
|
|
'edit',
|
|
context,
|
|
unsafeAttrs,
|
|
testUtils.permissions.author,
|
|
false,
|
|
true
|
|
).then(() => {
|
|
done(new Error('Permissible function should have rejected.'));
|
|
}).catch((error) => {
|
|
error.should.be.an.instanceof(common.errors.NoPermissionError);
|
|
should(mockPostObj.get.called).be.false();
|
|
should(mockPostObj.related.calledOnce).be.true();
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('rejects if editing another\'s post (using `authors`)', function (done) {
|
|
var mockPostObj = {
|
|
get: sandbox.stub(),
|
|
related: sandbox.stub()
|
|
},
|
|
context = {user: 1},
|
|
unsafeAttrs = {authors: [{id: 2}]};
|
|
|
|
mockPostObj.related.withArgs('authors').returns({models: [{id: 1}]});
|
|
|
|
models.Post.permissible(
|
|
mockPostObj,
|
|
'edit',
|
|
context,
|
|
unsafeAttrs,
|
|
testUtils.permissions.author,
|
|
false,
|
|
true
|
|
).then(() => {
|
|
done(new Error('Permissible function should have rejected.'));
|
|
}).catch((error) => {
|
|
error.should.be.an.instanceof(common.errors.NoPermissionError);
|
|
should(mockPostObj.get.called).be.false();
|
|
should(mockPostObj.related.calledTwice).be.true();
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('rejects if changing author', function (done) {
|
|
var mockPostObj = {
|
|
get: sandbox.stub(),
|
|
related: sandbox.stub()
|
|
},
|
|
context = {user: 1},
|
|
unsafeAttrs = {author_id: 2};
|
|
|
|
mockPostObj.get.withArgs('author_id').returns(1);
|
|
mockPostObj.related.withArgs('authors').returns({models: [{id: 1}]});
|
|
|
|
models.Post.permissible(
|
|
mockPostObj,
|
|
'edit',
|
|
context,
|
|
unsafeAttrs,
|
|
testUtils.permissions.author,
|
|
false,
|
|
true
|
|
).then(() => {
|
|
done(new Error('Permissible function should have rejected.'));
|
|
}).catch((error) => {
|
|
error.should.be.an.instanceof(common.errors.NoPermissionError);
|
|
should(mockPostObj.get.calledOnce).be.true();
|
|
should(mockPostObj.related.calledOnce).be.true();
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('rejects if changing authors', function (done) {
|
|
var mockPostObj = {
|
|
get: sandbox.stub(),
|
|
related: sandbox.stub()
|
|
},
|
|
context = {user: 1},
|
|
unsafeAttrs = {authors: [{id: 2}]};
|
|
|
|
mockPostObj.related.withArgs('authors').returns({models: [{id: 1}]});
|
|
|
|
models.Post.permissible(
|
|
mockPostObj,
|
|
'edit',
|
|
context,
|
|
unsafeAttrs,
|
|
testUtils.permissions.author,
|
|
false,
|
|
true
|
|
).then(() => {
|
|
done(new Error('Permissible function should have rejected.'));
|
|
}).catch((error) => {
|
|
error.should.be.an.instanceof(common.errors.NoPermissionError);
|
|
should(mockPostObj.get.called).be.false();
|
|
should(mockPostObj.related.calledTwice).be.true();
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('rejects if changing authors and author_id', function (done) {
|
|
var mockPostObj = {
|
|
get: sandbox.stub(),
|
|
related: sandbox.stub()
|
|
},
|
|
context = {user: 1},
|
|
unsafeAttrs = {authors: [{id: 1}], author_id: 2};
|
|
|
|
mockPostObj.get.withArgs('author_id').returns(1);
|
|
mockPostObj.related.withArgs('authors').returns({models: [{id: 1}]});
|
|
|
|
models.Post.permissible(
|
|
mockPostObj,
|
|
'edit',
|
|
context,
|
|
unsafeAttrs,
|
|
testUtils.permissions.author,
|
|
false,
|
|
true
|
|
).then(() => {
|
|
done(new Error('Permissible function should have rejected.'));
|
|
}).catch((error) => {
|
|
error.should.be.an.instanceof(common.errors.NoPermissionError);
|
|
should(mockPostObj.get.calledOnce).be.true();
|
|
should(mockPostObj.related.calledOnce).be.true();
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('rejects if changing authors and author_id', function (done) {
|
|
var mockPostObj = {
|
|
get: sandbox.stub(),
|
|
related: sandbox.stub()
|
|
},
|
|
context = {user: 1},
|
|
unsafeAttrs = {authors: [{id: 2}], author_id: 1};
|
|
|
|
mockPostObj.get.withArgs('author_id').returns(1);
|
|
mockPostObj.related.withArgs('authors').returns({models: [{id: 1}]});
|
|
|
|
models.Post.permissible(
|
|
mockPostObj,
|
|
'edit',
|
|
context,
|
|
unsafeAttrs,
|
|
testUtils.permissions.author,
|
|
false,
|
|
true
|
|
).then(() => {
|
|
done(new Error('Permissible function should have rejected.'));
|
|
}).catch((error) => {
|
|
error.should.be.an.instanceof(common.errors.NoPermissionError);
|
|
mockPostObj.get.callCount.should.eql(1);
|
|
mockPostObj.related.callCount.should.eql(2);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('resolves if none of the above cases are true', function () {
|
|
var mockPostObj = {
|
|
get: sandbox.stub(),
|
|
related: sandbox.stub()
|
|
},
|
|
context = {user: 1},
|
|
unsafeAttrs = {author_id: 1};
|
|
|
|
mockPostObj.get.withArgs('author_id').returns(1);
|
|
mockPostObj.related.withArgs('authors').returns({models: [{id: 1}]});
|
|
|
|
return models.Post.permissible(
|
|
mockPostObj,
|
|
'edit',
|
|
context,
|
|
unsafeAttrs,
|
|
testUtils.permissions.author,
|
|
false,
|
|
true
|
|
).then(() => {
|
|
should(mockPostObj.get.calledOnce).be.true();
|
|
should(mockPostObj.related.calledOnce).be.true();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Adding', function () {
|
|
it('rejects if different author id', function (done) {
|
|
var mockPostObj = {
|
|
get: sandbox.stub()
|
|
},
|
|
context = {user: 1},
|
|
unsafeAttrs = {author_id: 2};
|
|
|
|
models.Post.permissible(
|
|
mockPostObj,
|
|
'add',
|
|
context,
|
|
unsafeAttrs,
|
|
testUtils.permissions.author,
|
|
false,
|
|
true
|
|
).then(() => {
|
|
done(new Error('Permissible function should have rejected.'));
|
|
}).catch((error) => {
|
|
error.should.be.an.instanceof(common.errors.NoPermissionError);
|
|
should(mockPostObj.get.called).be.false();
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('rejects if different authors', function (done) {
|
|
var mockPostObj = {
|
|
get: sandbox.stub(),
|
|
related: sandbox.stub()
|
|
},
|
|
context = {user: 1},
|
|
unsafeAttrs = {authors: [{id: 2}]};
|
|
|
|
mockPostObj.related.withArgs('authors').returns({models: [{id: 1}]});
|
|
|
|
models.Post.permissible(
|
|
mockPostObj,
|
|
'add',
|
|
context,
|
|
unsafeAttrs,
|
|
testUtils.permissions.author,
|
|
false,
|
|
true
|
|
).then(() => {
|
|
done(new Error('Permissible function should have rejected.'));
|
|
}).catch((error) => {
|
|
error.should.be.an.instanceof(common.errors.NoPermissionError);
|
|
should(mockPostObj.get.called).be.false();
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('resolves if none of the above cases are true', function () {
|
|
var mockPostObj = {
|
|
get: sandbox.stub()
|
|
},
|
|
context = {user: 1},
|
|
unsafeAttrs = {author_id: 1};
|
|
|
|
return models.Post.permissible(
|
|
mockPostObj,
|
|
'add',
|
|
context,
|
|
unsafeAttrs,
|
|
testUtils.permissions.author,
|
|
false,
|
|
true
|
|
).then(() => {
|
|
should(mockPostObj.get.called).be.false();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Everyone Else', function () {
|
|
it('rejects if hasUserPermissions is false and not current owner', function (done) {
|
|
var mockPostObj = {
|
|
get: sandbox.stub(),
|
|
related: sandbox.stub()
|
|
},
|
|
context = {user: 1},
|
|
unsafeAttrs = {author_id: 2};
|
|
|
|
mockPostObj.related.withArgs('authors').returns({models: [{id: 2}]});
|
|
mockPostObj.get.withArgs('author_id').returns(2);
|
|
|
|
models.Post.permissible(
|
|
mockPostObj,
|
|
'edit',
|
|
context,
|
|
unsafeAttrs,
|
|
testUtils.permissions.editor,
|
|
false,
|
|
true
|
|
).then(() => {
|
|
done(new Error('Permissible function should have rejected.'));
|
|
}).catch((error) => {
|
|
error.should.be.an.instanceof(common.errors.NoPermissionError);
|
|
should(mockPostObj.get.called).be.false();
|
|
should(mockPostObj.related.calledOnce).be.true();
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('resolves if hasUserPermission is true', function () {
|
|
var mockPostObj = {
|
|
get: sandbox.stub()
|
|
},
|
|
context = {user: 1},
|
|
unsafeAttrs = {author_id: 2};
|
|
|
|
mockPostObj.get.withArgs('author_id').returns(2);
|
|
|
|
return models.Post.permissible(
|
|
mockPostObj,
|
|
'edit',
|
|
context,
|
|
unsafeAttrs,
|
|
testUtils.permissions.editor,
|
|
true,
|
|
true
|
|
).then(() => {
|
|
should(mockPostObj.get.called).be.false();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Mobiledoc conversion', function () {
|
|
let labs = require('../../../server/services/labs');
|
|
let origLabs = _.cloneDeep(labs);
|
|
let events;
|
|
|
|
beforeEach(function () {
|
|
events = {
|
|
post: []
|
|
};
|
|
|
|
sandbox.stub(models.Post.prototype, 'emitChange').callsFake(function (event) {
|
|
events.post.push({event: event, data: this.toJSON()});
|
|
});
|
|
});
|
|
|
|
it('uses v2 if Koenig is enabled', function () {
|
|
sandbox.stub(labs, 'isSet').callsFake(function (key) {
|
|
if (key === 'koenigEditor') {
|
|
return true;
|
|
}
|
|
return origLabs.get(key);
|
|
});
|
|
|
|
let newPost = testUtils.DataGenerator.forModel.posts[2];
|
|
|
|
return models.Post.add(
|
|
newPost,
|
|
testUtils.context.editor
|
|
).then((post) => {
|
|
should.exist(post);
|
|
post.has('html').should.equal(true);
|
|
post.get('html').should.equal('<div class="kg-post">\n<h2 id="testing">testing</h2>\n<p>mctesters</p>\n<ul>\n<li>test</li>\n<li>line</li>\n<li>items</li>\n</ul>\n\n</div>');
|
|
});
|
|
});
|
|
|
|
it('uses v2 if Koenig is disabled but post is not v1 compatible', function () {
|
|
let newPost = testUtils.DataGenerator.forModel.posts[2];
|
|
|
|
newPost.mobiledoc = JSON.stringify({
|
|
version: '0.3.1',
|
|
atoms: [],
|
|
cards: [],
|
|
markups: [],
|
|
sections: [
|
|
[1, 'p', [
|
|
[0, [], 0, 'Test']
|
|
]]
|
|
]
|
|
});
|
|
|
|
return models.Post.add(
|
|
newPost,
|
|
testUtils.context.editor
|
|
).then((post) => {
|
|
should.exist(post);
|
|
post.has('html').should.equal(true);
|
|
post.get('html').should.equal('<div class="kg-post">\n<p>Test</p>\n</div>');
|
|
});
|
|
});
|
|
});
|
|
});
|