replace auto increment id's by object id (#7495)

* 🛠  bookshelf tarball, bson-objectid

* 🎨  schema changes

- change increment type to string
- add a default fallback for string length 191 (to avoid adding this logic to every single column which uses an ID)
- remove uuid, because ID now represents a global resource identifier
- keep uuid for post, because we are using this as preview id
- keep uuid for clients for now - we are using this param for Ghost-Auth

*   base model: generate ObjectId on creating event

- each new resource get's a auto generate ObjectId
- this logic won't work for attached models, this commit comes later

* 🎨  centralised attach method

When attaching models there are two things important two know

1. To be able to attach an ObjectId, we need to register the `onCreating` event the fetched model!This is caused by the Bookshelf design in general. On this target model we are attaching the new model.
2. We need to manually fetch the target model, because Bookshelf has a weird behaviour (which is known as a bug, see see https://github.com/tgriesser/bookshelf/issues/629). The most important property when attaching a model is `parentFk`, which is the foreign key. This can be null when fetching the model with the option `withRelated`. To ensure quality and consistency, the custom attach wrapper always fetches the target model manual. By fetching the target model (again) is a little performance decrease, but it also has advantages: we can register the event, and directly unregister the event again. So very clean code.

Important: please only use the custom attach wrapper in the future.

* 🎨  token model had overriden the onCreating function because of the created_at field

- we need to ensure that the base onCreating hook get's triggered for ALL models
- if not, they don't get an ObjectId assigned
- in this case: be smart and check if the target model has a created_at field

* 🎨  we don't have a uuid field anymore, remove the usages

- no default uuid creation in models
- i am pretty sure we have some more definitions in our tests (for example in the export json files), but that is too much work to delete them all

* 🎨  do not parse ID to Number

- we had various occurances of parsing all ID's to numbers
- we don't need this behaviour anymore
- ID is string
- i will adapt the ID validation in the next commit

* 🎨  change ID regex for validation

- we only allow: ID as ObjectId, ID as 1 and ID as me
- we need to keep ID 1, because our whole software relies on ID 1 (permissions etc)

* 🎨  owner fixture

- roles: [4] does not work anymore
- 4 means -> static id 4
- this worked in an auto increment system (not even in a system with distributed writes)
- with ObjectId we generate each ID automatically (for static and dynamic resources)
- it is possible to define all id's for static resources still, but that means we need to know which ID is already used and for consistency we have to define ObjectId's for these static resources
- so no static id's anymore, except of: id 1 for owner and id 0 for external usage (because this is required from our permission system)
- NOTE: please read through the comment in the user model


* 🎨  tests: DataGenerator and test utils

First of all: we need to ensure using ObjectId's in the tests. When don't, we can't ensure that ObjectId's work properly.
This commit brings lot's of dynamic into all the static defined id's.
In one of the next commits, i will adapt all the tests.

* 🚨  remove counter in Notification API

- no need to add a counter
- we simply generate ObjectId's (they are auto incremental as well)
- our id validator does only allow ObjectId as id,1 and me

* 🎨  extend contextUser in Base Model

- remove isNumber check, because id's are no longer numbers, except of id 0/1
- use existing isExternalUser
- support id 0/1 as string or number

*   Ghost Owner has id 1

- ensure we define this id in the fixtures.json
- doesn't matter if number or string

* 🎨  functional tests adaptions

- use dynamic id's

* 🎨  fix unit tests

* 🎨  integration tests adaptions

* 🎨  change importer utils

- all our export examples (test/fixtures/exports) contain id's as numbers
- fact: but we ignore them anyway when inserting into the database, see https://github.com/TryGhost/Ghost/blob/master/core/server/data/import/utils.js#L249
- in 0e6ed957cd (diff-70f514a06347c048648be464819503c4L67) i removed parsing id's to integers
- i realised that this ^ check just existed, because the userIdToMap was an object key and object keys are always strings!
- i think this logic is a little bit complicated, but i don't want to refactor this now
- this commit ensures when trying to find the user, the id comparison works again
- i've added more documentation to understand this logic ;)
- plus i renamed an attribute to improve readability

* 🎨  Data-Generator: add more defaults to createUser

- if i use the function DataGenerator.forKnex.createUser i would like to get a full set of defaults

* 🎨  test utils: change/extend function set for functional tests

- functional tests work a bit different
- they boot Ghost and seed the database
- some functional tests have mis-used the test setup
- the test setup needs two sections: integration/unit and functional tests
- any functional test is allowed to either add more data or change data in the existing Ghost db
- but what it should not do is: add test fixtures like roles or users from our DataGenerator and cross fingers it will work
- this commit adds a clean method for functional tests to add extra users

* 🎨  functional tests adaptions

- use last commit to insert users for functional tests clean
- tidy up usage of testUtils.setup or testUtils.doAuth

* 🐛  test utils: reset database before init

- ensure we don't have any left data from other tests in the database when starting ghost

* 🐛  fix test (unrelated to this PR)

- fixes a random failure
- return statement was missing

* 🎨  make changes for invites
This commit is contained in:
Katharina Irrgang 2016-11-17 10:09:11 +01:00 committed by Hannah Wolfe
parent 3d3101ad0e
commit 7eb316b786
52 changed files with 1076 additions and 739 deletions

View File

@ -177,8 +177,6 @@ invites = {
return Promise.reject(new errors.ValidationError({message: i18n.t('errors.api.invites.roleIsRequired')}));
}
options.data.invites[0].role_id = parseInt(options.data.invites[0].role_id, 10);
// @TODO move this logic to permissible
// Make sure user is allowed to add a user with this role
return dataProvider.Role.findOne({id: options.data.invites[0].role_id}).then(function (role) {

View File

@ -2,6 +2,7 @@
// RESTful API for creating notifications
var Promise = require('bluebird'),
_ = require('lodash'),
ObjectId = require('bson-objectid'),
permissions = require('../permissions'),
errors = require('../errors'),
settings = require('./settings'),
@ -12,8 +13,6 @@ var Promise = require('bluebird'),
// Holds the persistent notifications
notificationsStore = [],
// Holds the last used id
notificationCounter = 0,
notifications;
/**
@ -91,10 +90,8 @@ notifications = {
addedNotifications = [], existingNotification;
_.each(options.data.notifications, function (notification) {
notificationCounter = notificationCounter + 1;
notification = _.assign(defaults, notification, {
id: notificationCounter
id: ObjectId.generate()
});
existingNotification = _.find(notificationsStore, {message:notification.message});
@ -132,7 +129,7 @@ notifications = {
var tasks;
/**
* Adds the uuid of notification to "seenNotifications" array.
* Adds the id of notification to "seenNotifications" array.
* @param {Object} notification
* @return {*|Promise}
*/
@ -140,7 +137,7 @@ notifications = {
var context = {internal: true};
return settings.read({key: 'seenNotifications', context: context}).then(function then(response) {
var seenNotifications = JSON.parse(response.settings[0].value);
seenNotifications = _.uniqBy(seenNotifications.concat([notification.uuid]));
seenNotifications = _.uniqBy(seenNotifications.concat([notification.id]));
return settings.edit({settings: [{key: 'seenNotifications', value: seenNotifications}]}, {context: context});
});
}
@ -161,7 +158,7 @@ notifications = {
function destroyNotification(options) {
var notification = _.find(notificationsStore, function (element) {
return element.id === parseInt(options.id, 10);
return element.id === options.id;
});
if (notification && !notification.dismissible) {
@ -175,9 +172,8 @@ notifications = {
}
notificationsStore = _.reject(notificationsStore, function (element) {
return element.id === parseInt(options.id, 10);
return element.id === options.id;
});
notificationCounter = notificationCounter - 1;
if (notification.custom) {
return markAsSeen(notification);
@ -203,8 +199,6 @@ notifications = {
destroyAll: function destroyAll(options) {
return canThis(options.context).destroy.notification().then(function () {
notificationsStore = [];
notificationCounter = 0;
return notificationsStore;
}, function (err) {
return Promise.reject(new errors.NoPermissionError({

View File

@ -136,8 +136,8 @@ users = {
// @TODO move role permissions out of here
var role = options.data.users[0].roles[0],
roleId = parseInt(role.id || role, 10),
editedUserId = parseInt(options.id, 10);
roleId = role.id || role,
editedUserId = options.id;
return dataProvider.User.findOne(
{id: options.context.user, status: 'all'}, {include: ['roles']}

View File

@ -110,7 +110,7 @@ utils = {
validateOptions: function validateOptions(options) {
var globalValidations = {
id: {matches: /^\d+|me$/},
id: {matches: /^[a-f\d]{24}$|^1$|me/i},
uuid: {isUUID: true},
slug: {isSlug: true},
page: {matches: /^\d+$/},
@ -297,7 +297,7 @@ utils = {
object[docName][index] = _.omitBy(object[docName][index], _.isNull);
});
if (editId && object[docName][0].id && parseInt(editId, 10) !== parseInt(object[docName][0].id, 10)) {
if (editId && object[docName][0].id && editId !== object[docName][0].id) {
return Promise.reject(new errors.BadRequestError({
message: i18n.t('errors.api.utils.invalidIdProvided')
}));

View File

@ -63,26 +63,27 @@ utils = {
});
// We now have a list of users we need to figure out what their email addresses are
_.each(_.keys(userMap), function (userToMap) {
userToMap = parseInt(userToMap, 10);
// tableData.users has id's as numbers (see fixtures/export)
// userIdToMap === tableData.users, but it's already a string, because it's an object key and they are always strings
_.each(_.keys(userMap), function (userIdToMap) {
var foundUser = _.find(tableData.users, function (tableDataUser) {
return tableDataUser.id === userToMap;
return tableDataUser.id.toString() === userIdToMap;
});
// we now know that userToMap's email is foundUser.email - look them up in existing users
if (foundUser && _.has(foundUser, 'email') && _.has(existingUsers, foundUser.email)) {
existingUsers[foundUser.email].importId = userToMap;
userMap[userToMap] = existingUsers[foundUser.email].realId;
} else if (models.User.isOwnerUser(userToMap)) {
existingUsers[owner.email].importId = userToMap;
userMap[userToMap] = existingUsers[owner.email].realId;
} else if (models.User.isExternalUser(userToMap)) {
userMap[userToMap] = models.User.externalUser;
existingUsers[foundUser.email].importId = userIdToMap;
userMap[userIdToMap] = existingUsers[foundUser.email].realId;
} else if (models.User.isOwnerUser(userIdToMap)) {
existingUsers[owner.email].importId = userIdToMap;
userMap[userIdToMap] = existingUsers[owner.email].realId;
} else if (models.User.isExternalUser(userIdToMap)) {
userMap[userIdToMap] = models.User.externalUser;
} else {
throw new errors.DataImportError({
message: i18n.t('errors.data.import.utils.dataLinkedToUnknownUser', {userToMap: userToMap}),
message: i18n.t('errors.data.import.utils.dataLinkedToUnknownUser', {userToMap: userIdToMap}),
property: 'user.id',
value: userToMap
value: userIdToMap
});
}
});
@ -120,8 +121,9 @@ utils = {
_.each(postsWithTags, function (tagIds, postId) {
var post, tags;
post = _.find(tableData.posts, function (post) {
return post.id === parseInt(postId, 10);
return post.id === postId;
});
if (post) {
tags = _.filter(tableData.tags, function (tag) {
return _.indexOf(tagIds, tag.id) !== -1;
@ -163,7 +165,7 @@ utils = {
_.each(tableData.roles_users, function (roleUser) {
var user = _.find(tableData.users, function (user) {
return user.id === parseInt(roleUser.user_id, 10);
return user.id === roleUser.user_id;
});
// Map role_id to updated roles id

View File

@ -3,6 +3,7 @@
var _ = require('lodash'),
Promise = require('bluebird'),
models = require('../../../models'),
baseUtils = require('../../../models/base/utils'),
sequence = require('../../../utils/sequence'),
fixtures = require('./fixtures'),
@ -133,7 +134,13 @@ addFixturesForRelation = function addFixturesForRelation(relationFixture, option
if (toItems && toItems.length > 0) {
ops.push(function addRelationItems() {
return fromItem[relationFixture.from.relation]().attach(toItems, options);
return baseUtils.attach(
models[relationFixture.from.Model || relationFixture.from.model],
fromItem.id,
relationFixture.from.relation,
toItems,
options
);
});
}
});

View File

@ -12,8 +12,12 @@ function addTableColumn(tableName, table, columnName) {
// creation distinguishes between text with fieldtype, string with maxlength and all others
if (columnSpec.type === 'text' && columnSpec.hasOwnProperty('fieldtype')) {
column = table[columnSpec.type](columnName, columnSpec.fieldtype);
} else if (columnSpec.type === 'string' && columnSpec.hasOwnProperty('maxlength')) {
column = table[columnSpec.type](columnName, columnSpec.maxlength);
} else if (columnSpec.type === 'string') {
if (columnSpec.hasOwnProperty('maxlength')) {
column = table[columnSpec.type](columnName, columnSpec.maxlength);
} else {
column = table[columnSpec.type](columnName, 191);
}
} else {
column = table[columnSpec.type](columnName);
}

View File

@ -321,10 +321,11 @@
"name": "User",
"entries": [
{
"id": 1,
"name": "Ghost Owner",
"email": "ghost@ghost.org",
"status": "inactive",
"roles": [4]
"roles": []
}
]
}
@ -393,6 +394,20 @@
"entries": {
"Welcome to Ghost": ["Getting Started"]
}
},
{
"from": {
"model": "User",
"match": "name",
"relation": "roles"
},
"to": {
"model": "Role",
"match": "name"
},
"entries": {
"Ghost Owner": ["Owner"]
}
}
]
}

View File

@ -3,6 +3,7 @@
var _ = require('lodash'),
Promise = require('bluebird'),
models = require('../../../models'),
baseUtils = require('../../../models/base/utils'),
sequence = require('../../../utils/sequence'),
fixtures = require('./fixtures'),
@ -133,7 +134,13 @@ addFixturesForRelation = function addFixturesForRelation(relationFixture, option
if (toItems && toItems.length > 0) {
ops.push(function addRelationItems() {
return fromItem[relationFixture.from.relation]().attach(toItems, options);
return baseUtils.attach(
models[relationFixture.from.Model || relationFixture.from.model],
fromItem.id,
relationFixture.from.relation,
toItems,
options
);
});
}
});

View File

@ -1,6 +1,6 @@
module.exports = {
posts: {
id: {type: 'increments', nullable: false, primary: true},
id: {type: 'string', nullable: false, primary: true},
uuid: {type: 'string', maxlength: 36, nullable: false, validations: {isUUID: true}},
title: {type: 'string', maxlength: 150, nullable: false},
slug: {type: 'string', maxlength: 150, nullable: false, unique: true},
@ -16,17 +16,16 @@ module.exports = {
visibility: {type: 'string', maxlength: 150, nullable: false, defaultTo: 'public', validations: {isIn: [['public']]}},
meta_title: {type: 'string', maxlength: 150, nullable: true},
meta_description: {type: 'string', maxlength: 200, nullable: true},
author_id: {type: 'integer', nullable: false},
author_id: {type: 'string', nullable: false},
created_at: {type: 'dateTime', nullable: false},
created_by: {type: 'integer', nullable: false},
created_by: {type: 'string', nullable: false},
updated_at: {type: 'dateTime', nullable: true},
updated_by: {type: 'integer', nullable: true},
updated_by: {type: 'string', nullable: true},
published_at: {type: 'dateTime', nullable: true},
published_by: {type: 'integer', nullable: true}
published_by: {type: 'string', nullable: true}
},
users: {
id: {type: 'increments', nullable: false, primary: true},
uuid: {type: 'string', maxlength: 36, nullable: false, validations: {isUUID: true}},
id: {type: 'string', nullable: false, primary: true},
name: {type: 'string', maxlength: 150, nullable: false},
slug: {type: 'string', maxlength: 150, nullable: false, unique: true},
ghost_auth_access_token: {type: 'string', nullable: true},
@ -48,125 +47,118 @@ module.exports = {
tour: {type: 'text', maxlength: 65535, nullable: true},
last_login: {type: 'dateTime', nullable: true},
created_at: {type: 'dateTime', nullable: false},
created_by: {type: 'integer', nullable: false},
created_by: {type: 'string', nullable: false},
updated_at: {type: 'dateTime', nullable: true},
updated_by: {type: 'integer', nullable: true}
updated_by: {type: 'string', nullable: true}
},
roles: {
id: {type: 'increments', nullable: false, primary: true},
uuid: {type: 'string', maxlength: 36, nullable: false, validations: {isUUID: true}},
id: {type: 'string', nullable: false, primary: true},
name: {type: 'string', maxlength: 150, nullable: false, unique: true},
description: {type: 'string', maxlength: 200, nullable: true},
created_at: {type: 'dateTime', nullable: false},
created_by: {type: 'integer', nullable: false},
created_by: {type: 'string', nullable: false},
updated_at: {type: 'dateTime', nullable: true},
updated_by: {type: 'integer', nullable: true}
updated_by: {type: 'string', nullable: true}
},
roles_users: {
id: {type: 'increments', nullable: false, primary: true},
role_id: {type: 'integer', nullable: false},
user_id: {type: 'integer', nullable: false}
id: {type: 'string', nullable: false, primary: true},
role_id: {type: 'string', nullable: false},
user_id: {type: 'string', nullable: false}
},
permissions: {
id: {type: 'increments', nullable: false, primary: true},
uuid: {type: 'string', maxlength: 36, nullable: false, validations: {isUUID: true}},
id: {type: 'string', nullable: false, primary: true},
name: {type: 'string', maxlength: 150, nullable: false, unique: true},
object_type: {type: 'string', maxlength: 150, nullable: false},
action_type: {type: 'string', maxlength: 150, nullable: false},
object_id: {type: 'integer', nullable: true},
object_id: {type: 'string', nullable: true},
created_at: {type: 'dateTime', nullable: false},
created_by: {type: 'integer', nullable: false},
created_by: {type: 'string', nullable: false},
updated_at: {type: 'dateTime', nullable: true},
updated_by: {type: 'integer', nullable: true}
updated_by: {type: 'string', nullable: true}
},
permissions_users: {
id: {type: 'increments', nullable: false, primary: true},
user_id: {type: 'integer', nullable: false},
permission_id: {type: 'integer', nullable: false}
id: {type: 'string', nullable: false, primary: true},
user_id: {type: 'string', nullable: false},
permission_id: {type: 'string', nullable: false}
},
permissions_roles: {
id: {type: 'increments', nullable: false, primary: true},
role_id: {type: 'integer', nullable: false},
permission_id: {type: 'integer', nullable: false}
id: {type: 'string', nullable: false, primary: true},
role_id: {type: 'string', nullable: false},
permission_id: {type: 'string', nullable: false}
},
permissions_apps: {
id: {type: 'increments', nullable: false, primary: true},
app_id: {type: 'integer', nullable: false},
permission_id: {type: 'integer', nullable: false}
id: {type: 'string', nullable: false, primary: true},
app_id: {type: 'string', nullable: false},
permission_id: {type: 'string', nullable: false}
},
settings: {
id: {type: 'increments', nullable: false, primary: true},
uuid: {type: 'string', maxlength: 36, nullable: false, validations: {isUUID: true}},
id: {type: 'string', nullable: false, primary: true},
key: {type: 'string', maxlength: 150, nullable: false, unique: true},
value: {type: 'text', maxlength: 65535, nullable: true},
type: {type: 'string', maxlength: 150, nullable: false, defaultTo: 'core', validations: {isIn: [['core', 'blog', 'theme', 'app', 'plugin', 'private']]}},
created_at: {type: 'dateTime', nullable: false},
created_by: {type: 'integer', nullable: false},
created_by: {type: 'string', nullable: false},
updated_at: {type: 'dateTime', nullable: true},
updated_by: {type: 'integer', nullable: true}
updated_by: {type: 'string', nullable: true}
},
tags: {
id: {type: 'increments', nullable: false, primary: true},
uuid: {type: 'string', maxlength: 36, nullable: false, validations: {isUUID: true}},
id: {type: 'string', nullable: false, primary: true},
name: {type: 'string', maxlength: 150, nullable: false, validations: {matches: /^([^,]|$)/}},
slug: {type: 'string', maxlength: 150, nullable: false, unique: true},
description: {type: 'string', maxlength: 200, nullable: true},
image: {type: 'text', maxlength: 2000, nullable: true},
parent_id: {type: 'integer', nullable: true},
parent_id: {type: 'string', nullable: true},
visibility: {type: 'string', maxlength: 150, nullable: false, defaultTo: 'public', validations: {isIn: [['public', 'internal']]}},
meta_title: {type: 'string', maxlength: 150, nullable: true},
meta_description: {type: 'string', maxlength: 200, nullable: true},
created_at: {type: 'dateTime', nullable: false},
created_by: {type: 'integer', nullable: false},
created_by: {type: 'string', nullable: false},
updated_at: {type: 'dateTime', nullable: true},
updated_by: {type: 'integer', nullable: true}
updated_by: {type: 'string', nullable: true}
},
posts_tags: {
id: {type: 'increments', nullable: false, primary: true},
post_id: {type: 'integer', nullable: false, unsigned: true, references: 'posts.id'},
tag_id: {type: 'integer', nullable: false, unsigned: true, references: 'tags.id'},
sort_order: {type: 'integer', nullable: false, unsigned: true, defaultTo: 0}
id: {type: 'string', nullable: false, primary: true},
post_id: {type: 'string', nullable: false, references: 'posts.id'},
tag_id: {type: 'string', nullable: false, references: 'tags.id'},
sort_order: {type: 'integer', nullable: false, unsigned: true, defaultTo: 0}
},
apps: {
id: {type: 'increments', nullable: false, primary: true},
uuid: {type: 'string', maxlength: 36, nullable: false, validations: {isUUID: true}},
id: {type: 'string', nullable: false, primary: true},
name: {type: 'string', maxlength: 150, nullable: false, unique: true},
slug: {type: 'string', maxlength: 150, nullable: false, unique: true},
version: {type: 'string', maxlength: 150, nullable: false},
status: {type: 'string', maxlength: 150, nullable: false, defaultTo: 'inactive'},
created_at: {type: 'dateTime', nullable: false},
created_by: {type: 'integer', nullable: false},
created_by: {type: 'string', nullable: false},
updated_at: {type: 'dateTime', nullable: true},
updated_by: {type: 'integer', nullable: true}
updated_by: {type: 'string', nullable: true}
},
app_settings: {
id: {type: 'increments', nullable: false, primary: true},
uuid: {type: 'string', maxlength: 36, nullable: false, validations: {isUUID: true}},
id: {type: 'string', nullable: false, primary: true},
key: {type: 'string', maxlength: 150, nullable: false, unique: true},
value: {type: 'text', maxlength: 65535, nullable: true},
app_id: {type: 'integer', nullable: false, unsigned: true, references: 'apps.id'},
app_id: {type: 'string', nullable: false, references: 'apps.id'},
created_at: {type: 'dateTime', nullable: false},
created_by: {type: 'integer', nullable: false},
created_by: {type: 'string', nullable: false},
updated_at: {type: 'dateTime', nullable: true},
updated_by: {type: 'integer', nullable: true}
updated_by: {type: 'string', nullable: true}
},
app_fields: {
id: {type: 'increments', nullable: false, primary: true},
uuid: {type: 'string', maxlength: 36, nullable: false, validations: {isUUID: true}},
id: {type: 'string', nullable: false, primary: true},
key: {type: 'string', maxlength: 150, nullable: false},
value: {type: 'text', maxlength: 65535, nullable: true},
type: {type: 'string', maxlength: 150, nullable: false, defaultTo: 'html'},
app_id: {type: 'integer', nullable: false, unsigned: true, references: 'apps.id'},
relatable_id: {type: 'integer', nullable: false, unsigned: true},
app_id: {type: 'string', nullable: false, references: 'apps.id'},
relatable_id: {type: 'string', nullable: false},
relatable_type: {type: 'string', maxlength: 150, nullable: false, defaultTo: 'posts'},
active: {type: 'bool', nullable: false, defaultTo: true},
created_at: {type: 'dateTime', nullable: false},
created_by: {type: 'integer', nullable: false},
created_by: {type: 'string', nullable: false},
updated_at: {type: 'dateTime', nullable: true},
updated_by: {type: 'integer', nullable: true}
updated_by: {type: 'string', nullable: true}
},
clients: {
id: {type: 'increments', nullable: false, primary: true},
id: {type: 'string', nullable: false, primary: true},
uuid: {type: 'string', maxlength: 36, nullable: false},
name: {type: 'string', maxlength: 150, nullable: false, unique: true},
slug: {type: 'string', maxlength: 150, nullable: false, unique: true},
@ -178,57 +170,55 @@ module.exports = {
type: {type: 'string', maxlength: 150, nullable: false, defaultTo: 'ua', validations: {isIn: [['ua', 'web', 'native']]}},
description: {type: 'string', maxlength: 200, nullable: true},
created_at: {type: 'dateTime', nullable: false},
created_by: {type: 'integer', nullable: false},
created_by: {type: 'string', nullable: false},
updated_at: {type: 'dateTime', nullable: true},
updated_by: {type: 'integer', nullable: true}
updated_by: {type: 'string', nullable: true}
},
client_trusted_domains: {
id: {type: 'increments', nullable: false, primary: true},
uuid: {type: 'string', maxlength: 36, nullable: false},
client_id: {type: 'integer', nullable: false, unsigned: true, references: 'clients.id'},
id: {type: 'string', nullable: false, primary: true},
client_id: {type: 'string', nullable: false, references: 'clients.id'},
trusted_domain: {type: 'string', maxlength: 2000, nullable: true}
},
accesstokens: {
id: {type: 'increments', nullable: false, primary: true},
id: {type: 'string', nullable: false, primary: true},
token: {type: 'string', maxlength: 191, nullable: false, unique: true},
user_id: {type: 'integer', nullable: false, unsigned: true, references: 'users.id'},
client_id: {type: 'integer', nullable: false, unsigned: true, references: 'clients.id'},
user_id: {type: 'string', nullable: false, references: 'users.id'},
client_id: {type: 'string', nullable: false, references: 'clients.id'},
expires: {type: 'bigInteger', nullable: false}
},
refreshtokens: {
id: {type: 'increments', nullable: false, primary: true},
id: {type: 'string', nullable: false, primary: true},
token: {type: 'string', maxlength: 191, nullable: false, unique: true},
user_id: {type: 'integer', nullable: false, unsigned: true, references: 'users.id'},
client_id: {type: 'integer', nullable: false, unsigned: true, references: 'clients.id'},
user_id: {type: 'string', nullable: false, references: 'users.id'},
client_id: {type: 'string', nullable: false, references: 'clients.id'},
expires: {type: 'bigInteger', nullable: false}
},
subscribers: {
id: {type: 'increments', nullable: false, primary: true},
uuid: {type: 'string', maxlength: 36, nullable: false, validations: {isUUID: true}},
id: {type: 'string', nullable: false, primary: true},
name: {type: 'string', maxlength: 150, nullable: true},
email: {type: 'string', maxlength: 191, nullable: false, unique: true, validations: {isEmail: true}},
status: {type: 'string', maxlength: 150, nullable: false, defaultTo: 'pending', validations: {isIn: [['subscribed', 'pending', 'unsubscribed']]}},
post_id: {type: 'integer', nullable: true, unsigned: true, references: 'posts.id'},
post_id: {type: 'string', nullable: true, references: 'posts.id'},
subscribed_url: {type: 'text', maxlength: 2000, nullable: true, validations: {isEmptyOrURL: true}},
subscribed_referrer: {type: 'text', maxlength: 2000, nullable: true, validations: {isEmptyOrURL: true}},
unsubscribed_url: {type: 'text', maxlength: 2000, nullable: true, validations: {isEmptyOrURL: true}},
unsubscribed_at: {type: 'dateTime', nullable: true},
created_at: {type: 'dateTime', nullable: false},
created_by: {type: 'integer', nullable: false},
created_by: {type: 'string', nullable: false},
updated_at: {type: 'dateTime', nullable: true},
updated_by: {type: 'integer', nullable: true}
updated_by: {type: 'string', nullable: true}
},
invites: {
id: {type: 'increments', nullable: false, primary: true},
role_id: {type: 'integer', nullable: false, unsigned: true},
id: {type: 'string', nullable: false, primary: true},
role_id: {type: 'string', nullable: false},
status: {type: 'string', maxlength: 150, nullable: false, defaultTo: 'pending', validations: {isIn: [['pending', 'sent']]}},
token: {type: 'string', maxlength: 191, nullable: false, unique: true},
email: {type: 'string', maxlength: 191, nullable: false, unique: true, validations: {isEmail: true}},
expires: {type: 'bigInteger', nullable: false},
created_at: {type: 'dateTime', nullable: false},
created_by: {type: 'integer', nullable: false},
created_by: {type: 'string', nullable: false},
updated_at: {type: 'dateTime', nullable: true},
updated_by: {type: 'integer', nullable: true}
updated_by: {type: 'string', nullable: true}
},
brute: {
key: {type: 'string'},

View File

@ -112,7 +112,7 @@ generateFeed = function generateFeed(data) {
item = {
title: post.title,
description: post.meta_description || downsize(htmlContent.html(), {words: 50}),
guid: post.uuid,
guid: post.id,
url: itemUrl,
date: post.published_at,
categories: generateTags(post),

View File

@ -9,7 +9,7 @@ var _ = require('lodash'),
bookshelf = require('bookshelf'),
moment = require('moment'),
Promise = require('bluebird'),
uuid = require('node-uuid'),
ObjectId = require('bson-objectid'),
config = require('../../config'),
db = require('../../data/db'),
errors = require('../../errors'),
@ -59,9 +59,7 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
// Bookshelf `defaults` - default values setup on every model creation
defaults: function defaults() {
return {
uuid: uuid.v4()
};
return {};
},
// When loading an instance, subclasses can specify default to fetch
@ -108,7 +106,12 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
},
onCreating: function onCreating(newObj, attr, options) {
if (!this.get('created_by')) {
// id = 0 is still a valid value for external usage
if (_.isUndefined(newObj.id) || _.isNull(newObj.id)) {
newObj.setId();
}
if (schema.tables[this.tableName].hasOwnProperty('created_by') && !this.get('created_by')) {
this.set('created_by', this.contextUser(options));
}
},
@ -182,7 +185,7 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
options = options || {};
options.context = options.context || {};
if (_.isNumber(options.context.user)) {
if (options.context.user || ghostBookshelf.Model.isExternalUser(options.context.user)) {
return options.context.user;
} else if (options.context.internal) {
return ghostBookshelf.Model.internalUser;
@ -248,27 +251,37 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
hasDateChanged: function (attr) {
return moment(this.get(attr)).diff(moment(this.updated(attr))) !== 0;
},
/**
* we auto generate a GUID for each resource
* no auto increment
*/
setId: function setId() {
this.set('id', ObjectId.generate());
}
}, {
// ## Data Utility Functions
/**
* please use these static definitions when comparing id's
* we keep type number, because we have too many check's where we rely on
* context.user ? true : false (if context.user is 0 as number, this condition is false)
*/
internalUser: 1,
ownerUser: 1,
externalUser: 0,
isOwnerUser: function isOwnerUser(id) {
return id === ghostBookshelf.Model.ownerUser;
return id === ghostBookshelf.Model.ownerUser || id === ghostBookshelf.Model.ownerUser.toString();
},
isInternalUser: function isInternalUser(id) {
return id === ghostBookshelf.Model.internalUser;
return id === ghostBookshelf.Model.internalUser || id === ghostBookshelf.Model.internalUser.toString();
},
isExternalUser: function isExternalUser(id) {
return id === ghostBookshelf.Model.externalUser;
return id === ghostBookshelf.Model.externalUser || id === ghostBookshelf.Model.externalUser.toString();
},
/**
@ -477,6 +490,10 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
if (options.importing) {
model.hasTimestamps = false;
}
// Bookshelf determines whether an operation is an update or an insert based on the id
// Ghost auto-generates Object id's, so we need to tell Bookshelf here that we are inserting data
options.method = 'insert';
return model.save(null, options);
},

View File

@ -15,10 +15,6 @@ Basetoken = ghostBookshelf.Model.extend({
return this.belongsTo('Client');
},
// override for base function since we don't have
// a created_by field for sessions
onCreating: function onCreating() {},
// override for base function since we don't have
// a updated_by field for sessions
onSaving: function onSaving() {

View File

@ -3,7 +3,65 @@
* Parts of the model code which can be split out and unit tested
*/
var _ = require('lodash'),
tagUpdate;
Promise = require('bluebird'),
ObjectId = require('bson-objectid'),
errors = require('../../errors'),
tagUpdate, attach;
/**
* Attach wrapper (please never call attach manual!)
*
* We register the creating event to be able to hook into the model creation process of Bookshelf.
* We need to load the model again, because of a known bookshelf issue:
* see https://github.com/tgriesser/bookshelf/issues/629
* (withRelated option causes a null value for the foreign key)
*
* roles [1,2]
* roles [{id: 1}, {id: 2}]
* roles [{role_id: 1}]
* roles [BookshelfModel]
*/
attach = function attach(Model, effectedModelId, relation, modelsToAttach, options) {
options = options || {};
var fetchedModel,
localOptions = {transacting: options.transacting};
return Model.forge({id: effectedModelId}).fetch(localOptions)
.then(function successFetchedModel(_fetchedModel) {
fetchedModel = _fetchedModel;
if (!fetchedModel) {
throw new errors.NotFoundError({level: 'critical', help: effectedModelId});
}
fetchedModel.related(relation).on('creating', function (collection, data) {
data.id = ObjectId.generate();
});
return Promise.resolve(modelsToAttach)
.then(function then(models) {
models = _.map(models, function mapper(model) {
if (model.id) {
return model.id;
} else if (!_.isObject(model)) {
return model.toString();
} else {
return model;
}
});
return fetchedModel.related(relation).attach(models, localOptions);
});
})
.finally(function () {
if (!fetchedModel) {
return;
}
fetchedModel.related(relation).off('creating');
});
};
tagUpdate = {
fetchCurrentPost: function fetchCurrentPost(PostModel, id, options) {
@ -25,18 +83,18 @@ tagUpdate = {
};
},
attachTagToPost: function attachTagToPost(post, tag, index, options) {
attachTagToPost: function attachTagToPost(Post, postId, tag, index, options) {
return function () {
// See tgriesser/bookshelf#294 for an explanation of _.omit(options, 'query')
return post.tags().attach({tag_id: tag.id, sort_order: index}, _.omit(options, 'query'));
return attach(Post, postId, 'tags', [{tag_id: tag.id, sort_order: index}], _.omit(options, 'query'));
};
},
createTagThenAttachTagToPost: function createTagThenAttachTagToPost(TagModel, post, tag, index, options) {
createTagThenAttachTagToPost: function createTagThenAttachTagToPost(PostModel, TagModel, post, tag, index, options) {
var fields = ['name', 'slug', 'description', 'image', 'visibility', 'parent_id', 'meta_title', 'meta_description'];
return function () {
return TagModel.add(_.pick(tag, fields), options).then(function then(createdTag) {
return tagUpdate.attachTagToPost(post, createdTag, index, options)();
return tagUpdate.attachTagToPost(PostModel, post.id, createdTag, index, options)();
});
};
},
@ -52,10 +110,11 @@ tagUpdate = {
// Test if two tags are the same, checking ID first, and falling back to name
tagsAreEqual: function tagsAreEqual(tag1, tag2) {
if (tag1.hasOwnProperty('id') && tag2.hasOwnProperty('id')) {
return parseInt(tag1.id, 10) === parseInt(tag2.id, 10);
return tag1.id === tag2.id;
}
return tag1.name.toString() === tag2.name.toString();
},
tagSetsAreEqual: function tagSetsAreEqual(tags1, tags2) {
// If the lengths are different, they cannot be the same
if (tags1.length !== tags2.length) {
@ -68,4 +127,5 @@ tagUpdate = {
}
};
module.exports.attach = attach;
module.exports.tagUpdate = tagUpdate;

View File

@ -332,7 +332,7 @@ Post = ghostBookshelf.Model.extend({
var tag;
if (tagsToCreate.indexOf(newTag.name) > -1) {
tagOps.push(baseUtils.tagUpdate.createTagThenAttachTagToPost(TagModel, savedModel, newTag, index, options));
tagOps.push(baseUtils.tagUpdate.createTagThenAttachTagToPost(Post, TagModel, savedModel, newTag, index, options));
} else {
// try to find a tag on the current post which matches
tag = _.find(currentTags, function (currentTag) {
@ -350,7 +350,7 @@ Post = ghostBookshelf.Model.extend({
});
if (tag) {
tagOps.push(baseUtils.tagUpdate.attachTagToPost(savedModel, tag, index, options));
tagOps.push(baseUtils.tagUpdate.attachTagToPost(Post, savedModel.id, tag, index, options));
}
}
});

View File

@ -1,6 +1,5 @@
var Settings,
ghostBookshelf = require('./base'),
uuid = require('node-uuid'),
_ = require('lodash'),
errors = require('../errors'),
Promise = require('bluebird'),
@ -46,7 +45,6 @@ Settings = ghostBookshelf.Model.extend({
defaults: function defaults() {
return {
uuid: uuid.v4(),
type: 'core'
};
},

View File

@ -3,7 +3,6 @@ var ghostBookshelf = require('./base'),
events = require('../events'),
i18n = require('../i18n'),
Promise = require('bluebird'),
uuid = require('node-uuid'),
Subscriber,
Subscribers;
@ -16,7 +15,6 @@ Subscriber = ghostBookshelf.Model.extend({
defaults: function defaults() {
return {
uuid: uuid.v4(),
status: 'subscribed'
};
},

View File

@ -3,6 +3,7 @@ var _ = require('lodash'),
bcrypt = require('bcryptjs'),
validator = require('validator'),
ghostBookshelf = require('./base'),
baseUtils = require('./base/utils'),
errors = require('../errors'),
logging = require('../logging'),
utils = require('../utils'),
@ -368,7 +369,7 @@ User = ghostBookshelf.Model.extend({
return user;
}
roleId = parseInt(data.roles[0].id || data.roles[0], 10);
roleId = data.roles[0].id || data.roles[0];
return user.roles().fetch().then(function then(roles) {
// return if the role is already assigned
@ -425,6 +426,12 @@ User = ghostBookshelf.Model.extend({
});
}
/**
* We need this default author role because of the following Ghost feature:
* You setup your blog and you can invite people instantly, but without choosing a role.
* roles: [] -> no default role (used for owner creation, see fixtures.json)
* roles: undefined -> default role
*/
roles = data.roles || getAuthorRole();
delete data.roles;
@ -433,20 +440,7 @@ User = ghostBookshelf.Model.extend({
// Assign the userData to our created user so we can pass it back
userData = addedUser;
// if we are given a "role" object, only pass in the role ID in place of the full object
return Promise.resolve(roles).then(function then(roles) {
roles = _.map(roles, function mapper(role) {
if (_.isString(role)) {
return parseInt(role, 10);
} else if (_.isNumber(role)) {
return role;
} else {
return parseInt(role.id, 10);
}
});
return addedUser.roles().attach(roles, options);
});
return baseUtils.attach(User, userData.id, 'roles', roles, options);
}).then(function then() {
// find and return the added user
return self.findOne({id: userData.id, status: 'all'}, options);
@ -638,7 +632,7 @@ User = ghostBookshelf.Model.extend({
changePassword: function changePassword(object, options) {
var self = this,
newPassword = object.newPassword,
userId = parseInt(object.user_id),
userId = object.user_id,
oldPassword = object.oldPassword,
isLoggedInUser = userId === options.context.user,
user;

View File

@ -73,7 +73,7 @@ function createCustomNotification(message) {
getSeenNotifications = api.settings.read(_.extend({key: 'seenNotifications'}, internal));
return Promise.join(getAllNotifications, getSeenNotifications, function joined(all, seen) {
var isSeen = _.includes(JSON.parse(seen.settings[0].value || []), notification.uuid),
var isSeen = _.includes(JSON.parse(seen.settings[0].value || []), notification.id),
isDuplicate = _.some(all.notifications, {message: notification.message});
if (!isSeen && !isDuplicate) {

View File

@ -43,7 +43,8 @@ describe('Authentication API', function () {
password: user.password,
client_id: 'ghost-admin',
client_secret: 'not_available'
}).expect('Content-Type', /json/)
})
.expect('Content-Type', /json/)
// TODO: make it possible to override oauth2orize's header so that this is consistent
.expect('Cache-Control', 'no-store')
.expect(200)

View File

@ -2,6 +2,7 @@ var testUtils = require('../../../utils'),
should = require('should'),
supertest = require('supertest'),
_ = require('lodash'),
ObjectId = require('bson-objectid'),
ghost = testUtils.startGhost,
request;
@ -190,7 +191,7 @@ describe('Post API', function () {
// ## Read
describe('Read', function () {
it('can retrieve a post by id', function (done) {
request.get(testUtils.API.getApiQuery('posts/1/'))
request.get(testUtils.API.getApiQuery('posts/' + testUtils.DataGenerator.Content.posts[0].id + '/'))
.set('Authorization', 'Bearer ' + accesstoken)
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
@ -205,13 +206,13 @@ describe('Post API', function () {
should.exist(jsonResponse);
should.exist(jsonResponse.posts);
testUtils.API.checkResponse(jsonResponse.posts[0], 'post');
jsonResponse.posts[0].id.should.equal(1);
jsonResponse.posts[0].id.should.equal(testUtils.DataGenerator.Content.posts[0].id);
jsonResponse.posts[0].page.should.not.be.ok();
_.isBoolean(jsonResponse.posts[0].featured).should.eql(true);
_.isBoolean(jsonResponse.posts[0].page).should.eql(true);
jsonResponse.posts[0].author.should.be.a.Number();
jsonResponse.posts[0].author.should.be.a.String();
testUtils.API.isISO8601(jsonResponse.posts[0].created_at).should.be.true();
jsonResponse.posts[0].created_by.should.be.a.Number();
jsonResponse.posts[0].created_by.should.be.a.String();
// Tags aren't included by default
should.not.exist(jsonResponse.posts[0].tags);
done();
@ -238,8 +239,8 @@ describe('Post API', function () {
jsonResponse.posts[0].page.should.not.be.ok();
_.isBoolean(jsonResponse.posts[0].featured).should.eql(true);
_.isBoolean(jsonResponse.posts[0].page).should.eql(true);
jsonResponse.posts[0].author.should.be.a.Number();
jsonResponse.posts[0].created_by.should.be.a.Number();
jsonResponse.posts[0].author.should.be.a.String();
jsonResponse.posts[0].created_by.should.be.a.String();
// Tags aren't included by default
should.not.exist(jsonResponse.posts[0].tags);
done();
@ -247,7 +248,7 @@ describe('Post API', function () {
});
it('can retrieve a post with author, created_by, and tags', function (done) {
request.get(testUtils.API.getApiQuery('posts/1/?include=author,tags,created_by'))
request.get(testUtils.API.getApiQuery('posts/' + testUtils.DataGenerator.Content.posts[0].id + '/?include=author,tags,created_by'))
.set('Authorization', 'Bearer ' + accesstoken)
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
@ -273,7 +274,7 @@ describe('Post API', function () {
});
it('can retrieve next and previous posts', function (done) {
request.get(testUtils.API.getApiQuery('posts/3/?include=next,previous'))
request.get(testUtils.API.getApiQuery('posts/' + testUtils.DataGenerator.Content.posts[2].id + '/?include=next,previous'))
.set('Authorization', 'Bearer ' + accesstoken)
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
@ -299,7 +300,7 @@ describe('Post API', function () {
});
it('can retrieve a static page', function (done) {
request.get(testUtils.API.getApiQuery('posts/7/'))
request.get(testUtils.API.getApiQuery('posts/' + testUtils.DataGenerator.Content.posts[5].id + '/'))
.set('Authorization', 'Bearer ' + accesstoken)
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
@ -529,7 +530,7 @@ describe('Post API', function () {
// ## edit
describe('Edit', function () {
it('can edit a post', function (done) {
request.get(testUtils.API.getApiQuery('posts/1/?include=tags'))
request.get(testUtils.API.getApiQuery('posts/' + testUtils.DataGenerator.Content.posts[0].id + '/?include=tags'))
.set('Authorization', 'Bearer ' + accesstoken)
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
@ -540,12 +541,13 @@ describe('Post API', function () {
var jsonResponse = res.body,
changedTitle = 'My new Title',
changedAuthor = 2;
changedAuthor = ObjectId.generate();
should.exist(jsonResponse.posts[0]);
jsonResponse.posts[0].title = changedTitle;
jsonResponse.posts[0].author = changedAuthor;
request.put(testUtils.API.getApiQuery('posts/1/'))
request.put(testUtils.API.getApiQuery('posts/' + testUtils.DataGenerator.Content.posts[0].id + '/'))
.set('Authorization', 'Bearer ' + accesstoken)
.send(jsonResponse)
.expect('Content-Type', /json/)
@ -659,7 +661,7 @@ describe('Post API', function () {
});
it('can change a post to a static page', function (done) {
request.get(testUtils.API.getApiQuery('posts/1/?include=tags'))
request.get(testUtils.API.getApiQuery('posts/' + testUtils.DataGenerator.Content.posts[0].id + '/?include=tags'))
.set('Authorization', 'Bearer ' + accesstoken)
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
@ -674,7 +676,7 @@ describe('Post API', function () {
jsonResponse.posts[0].page.should.not.be.ok();
jsonResponse.posts[0].page = true;
request.put(testUtils.API.getApiQuery('posts/1/'))
request.put(testUtils.API.getApiQuery('posts/' + testUtils.DataGenerator.Content.posts[0].id + '/'))
.set('Authorization', 'Bearer ' + accesstoken)
.send(jsonResponse)
.expect('Content-Type', /json/)
@ -697,7 +699,7 @@ describe('Post API', function () {
});
it('can change a static page to a post', function (done) {
request.get(testUtils.API.getApiQuery('posts/7/'))
request.get(testUtils.API.getApiQuery('posts/' + testUtils.DataGenerator.Content.posts[5].id + '/'))
.set('Authorization', 'Bearer ' + accesstoken)
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
@ -712,7 +714,7 @@ describe('Post API', function () {
jsonResponse.posts[0].page.should.be.ok();
jsonResponse.posts[0].page = false;
request.put(testUtils.API.getApiQuery('posts/7/'))
request.put(testUtils.API.getApiQuery('posts/' + testUtils.DataGenerator.Content.posts[5].id + '/'))
.set('Authorization', 'Bearer ' + accesstoken)
.send(jsonResponse)
.expect('Content-Type', /json/)
@ -735,7 +737,7 @@ describe('Post API', function () {
});
it('can\'t edit post with invalid page field', function (done) {
request.get(testUtils.API.getApiQuery('posts/7/'))
request.get(testUtils.API.getApiQuery('posts/' + testUtils.DataGenerator.Content.posts[5].id + '/'))
.set('Authorization', 'Bearer ' + accesstoken)
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
@ -750,7 +752,7 @@ describe('Post API', function () {
jsonResponse.posts[0].page.should.eql(false);
jsonResponse.posts[0].page = changedValue;
request.put(testUtils.API.getApiQuery('posts/7/'))
request.put(testUtils.API.getApiQuery('posts/' + testUtils.DataGenerator.Content.posts[5].id + '/'))
.set('Authorization', 'Bearer ' + accesstoken)
.send(jsonResponse)
.expect('Content-Type', /json/)
@ -771,7 +773,7 @@ describe('Post API', function () {
});
it('can\'t edit a post with invalid accesstoken', function (done) {
request.get(testUtils.API.getApiQuery('posts/1/'))
request.get(testUtils.API.getApiQuery('posts/' + testUtils.DataGenerator.Content.posts[0].id + '/'))
.set('Authorization', 'Bearer ' + accesstoken)
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
@ -782,7 +784,7 @@ describe('Post API', function () {
}
var jsonResponse = res.body;
request.put(testUtils.API.getApiQuery('posts/1/'))
request.put(testUtils.API.getApiQuery('posts/' + testUtils.DataGenerator.Content.posts[0].id + '/'))
.set('Authorization', 'Bearer ' + 'invalidtoken')
.send(jsonResponse)
.expect('Content-Type', /json/)
@ -800,7 +802,7 @@ describe('Post API', function () {
});
it('throws an error if there is an id mismatch', function (done) {
request.get(testUtils.API.getApiQuery('posts/1/'))
request.get(testUtils.API.getApiQuery('posts/' + testUtils.DataGenerator.Content.posts[0].id + '/'))
.set('Authorization', 'Bearer ' + accesstoken)
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
@ -812,7 +814,7 @@ describe('Post API', function () {
var jsonResponse = res.body;
should.exist(jsonResponse);
request.put(testUtils.API.getApiQuery('posts/2/'))
request.put(testUtils.API.getApiQuery('posts/' + testUtils.DataGenerator.Content.posts[1].id + '/'))
.set('Authorization', 'Bearer ' + accesstoken)
.send(jsonResponse)
.expect('Content-Type', /json/)
@ -830,7 +832,7 @@ describe('Post API', function () {
});
it('published_at = null', function (done) {
request.get(testUtils.API.getApiQuery('posts/1/?include=tags'))
request.get(testUtils.API.getApiQuery('posts/' + testUtils.DataGenerator.Content.posts[0].id + '/?include=tags'))
.set('Authorization', 'Bearer ' + accesstoken)
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
@ -845,7 +847,7 @@ describe('Post API', function () {
jsonResponse.title = changedValue;
jsonResponse.published_at = null;
request.put(testUtils.API.getApiQuery('posts/1/'))
request.put(testUtils.API.getApiQuery('posts/' + testUtils.DataGenerator.Content.posts[0].id + '/'))
.set('Authorization', 'Bearer ' + accesstoken)
.send(jsonResponse)
.expect('Content-Type', /json/)
@ -872,7 +874,7 @@ describe('Post API', function () {
});
it('can\'t edit non existent post', function (done) {
request.get(testUtils.API.getApiQuery('posts/1/'))
request.get(testUtils.API.getApiQuery('posts/' + testUtils.DataGenerator.Content.posts[0].id + '/'))
.set('Authorization', 'Bearer ' + accesstoken)
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
@ -885,8 +887,9 @@ describe('Post API', function () {
changedValue = 'My new Title';
should.exist(jsonResponse.posts[0].title);
jsonResponse.posts[0].testvalue = changedValue;
jsonResponse.posts[0].id = 99;
request.put(testUtils.API.getApiQuery('posts/99/'))
jsonResponse.posts[0].id = ObjectId.generate();
request.put(testUtils.API.getApiQuery('posts/' + jsonResponse.posts[0].id + '/'))
.set('Authorization', 'Bearer ' + accesstoken)
.send(jsonResponse)
.expect('Content-Type', /json/)
@ -910,7 +913,8 @@ describe('Post API', function () {
// ## delete
describe('Delete', function () {
it('can delete a post', function (done) {
var deletePostId = 1;
var deletePostId = testUtils.DataGenerator.Content.posts[0].id;
request.del(testUtils.API.getApiQuery('posts/' + deletePostId + '/'))
.set('Authorization', 'Bearer ' + accesstoken)
.expect('Cache-Control', testUtils.cacheRules.private)
@ -928,7 +932,7 @@ describe('Post API', function () {
});
it('can\'t delete a non existent post', function (done) {
request.del(testUtils.API.getApiQuery('posts/99/'))
request.del(testUtils.API.getApiQuery('posts/' + ObjectId.generate() + '/'))
.set('Authorization', 'Bearer ' + accesstoken)
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
@ -1048,7 +1052,7 @@ describe('Post API', function () {
it('Can read a post', function (done) {
// nothing should have changed here
request.get(testUtils.API.getApiQuery('posts/2/'))
request.get(testUtils.API.getApiQuery('posts/' + testUtils.DataGenerator.Content.posts[1].id + '/'))
.set('Authorization', 'Bearer ' + accesstoken)
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
@ -1071,7 +1075,7 @@ describe('Post API', function () {
});
it('Can edit a post', function (done) {
request.get(testUtils.API.getApiQuery('posts/2/?include=tags'))
request.get(testUtils.API.getApiQuery('posts/' + testUtils.DataGenerator.Content.posts[1].id + '/?include=tags'))
.set('Authorization', 'Bearer ' + accesstoken)
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
@ -1086,7 +1090,7 @@ describe('Post API', function () {
should.exist(jsonResponse.posts);
jsonResponse.posts[0].title = changedValue;
request.put(testUtils.API.getApiQuery('posts/2/'))
request.put(testUtils.API.getApiQuery('posts/' + testUtils.DataGenerator.Content.posts[1].id + '/'))
.set('Authorization', 'Bearer ' + accesstoken)
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)

View File

@ -64,7 +64,7 @@ describe('Settings API', function () {
should.exist(jsonResponse);
should.exist(jsonResponse.settings);
testUtils.API.checkResponseValue(jsonResponse.settings[0], ['id', 'uuid', 'key', 'value', 'type', 'created_at', 'created_by', 'updated_at', 'updated_by']);
testUtils.API.checkResponseValue(jsonResponse.settings[0], ['id', 'key', 'value', 'type', 'created_at', 'created_by', 'updated_at', 'updated_by']);
jsonResponse.settings[0].key.should.eql('title');
testUtils.API.isISO8601(jsonResponse.settings[0].created_at).should.be.true();
done();

View File

@ -1,10 +1,7 @@
var supertest = require('supertest'),
should = require('should'),
testUtils = require('../../../utils'),
user1 = testUtils.DataGenerator.forModel.users[0],
user2 = testUtils.DataGenerator.forModel.users[1],
config = require('../../../../../core/server/config'),
ghostSetup = testUtils.setup('settings', 'users:roles', 'perms:setting', 'perms:notification', 'perms:init'),
ghost = testUtils.startGhost,
failedLoginAttempt,
count,
@ -12,12 +9,25 @@ var supertest = require('supertest'),
request;
describe('Spam Prevention API', function () {
var author,
owner = testUtils.DataGenerator.Content.users[0];
before(function (done) {
ghostSetup()
.then(ghost)
ghost()
.then(function (ghostServer) {
request = supertest.agent(ghostServer.rootApp);
// in functional tests we start Ghost and the database get's migrated/seeded
// no need to add or care about any missing data (settings, permissions) except of extra data we would like to add or override
// override Ghost fixture owner and add some extra users
return testUtils.setup('owner:post')();
}).then(function () {
return testUtils.createUser({
user: testUtils.DataGenerator.forKnex.createUser({email: 'test+1@ghost.org'}),
role: testUtils.DataGenerator.Content.roles[1]
});
}).then(function (user) {
author = user;
done();
}).then(testUtils.clearBruteData)
.catch(done);
@ -88,7 +98,7 @@ describe('Spam Prevention API', function () {
});
};
failedLoginAttempt(user1.email);
failedLoginAttempt(owner.email);
});
it('Too many failed login attempts for multiple users results in 429 TooManyRequestsError', function (done) {
@ -138,16 +148,16 @@ describe('Spam Prevention API', function () {
}
if (count < config.get('spam:user_login:freeRetries') + 1) {
return failedLoginAttempt(user1.email);
return failedLoginAttempt(owner.email);
}
if (count < config.get('spam:global_block:freeRetries') + 1) {
return failedLoginAttempt(user2.email);
return failedLoginAttempt(author.email);
}
tooManyFailedLoginAttempts(user2.email);
tooManyFailedLoginAttempts(author.email);
});
};
failedLoginAttempt(user1.email);
failedLoginAttempt(owner.email);
});
});

View File

@ -10,7 +10,7 @@ var testUtils = require('../../../utils'),
describe('Themes API', function () {
var scope = {
ownerownerAccessToken: '',
ownerAccessToken: '',
editorAccessToken: '',
uploadTheme: function uploadTheme(options) {
var themePath = options.themePath,
@ -20,19 +20,26 @@ describe('Themes API', function () {
return request.post(testUtils.API.getApiQuery('themes/upload'))
.set('Authorization', 'Bearer ' + accessToken)
.attach(fieldName, themePath);
}
},
editor: null
};
before(function (done) {
ghost().then(function (ghostServer) {
request = supertest.agent(ghostServer.rootApp);
}).then(function () {
return testUtils.doAuth(request, 'perms:init', 'users:no-owner');
return testUtils.doAuth(request);
}).then(function (token) {
scope.ownerAccessToken = token;
// 2 === Editor
request.userIndex = 2;
return testUtils.createUser({
user: testUtils.DataGenerator.forKnex.createUser({email: 'test+1@ghost.org'}),
role: testUtils.DataGenerator.Content.roles[1]
});
}).then(function (user) {
scope.editor = user;
request.user = scope.editor;
return testUtils.doAuth(request);
}).then(function (token) {
scope.editorAccessToken = token;

View File

@ -1,12 +1,15 @@
var testUtils = require('../../../utils'),
should = require('should'),
supertest = require('supertest'),
ObjectId = require('bson-objectid'),
ghost = testUtils.startGhost,
request;
describe('User API', function () {
var ownerAccessToken = '',
editorAccessToken = '';
editorAccessToken = '',
authorAccessToken = '',
editor, author;
before(function (done) {
// starting ghost automatically populates the db
@ -14,15 +17,36 @@ describe('User API', function () {
ghost().then(function (ghostServer) {
request = supertest.agent(ghostServer.rootApp);
}).then(function () {
return testUtils.doAuth(request, 'users:no-owner');
// create editor
return testUtils.createUser({
user: testUtils.DataGenerator.forKnex.createUser({email: 'test+1@ghost.org'}),
role: testUtils.DataGenerator.Content.roles[1]
});
}).then(function (_user1) {
editor = _user1;
// create author
return testUtils.createUser({
user: testUtils.DataGenerator.forKnex.createUser({email: 'test+2@ghost.org'}),
role: testUtils.DataGenerator.Content.roles[2]
});
}).then(function (_user2) {
author = _user2;
// by default we login with the owner
return testUtils.doAuth(request);
}).then(function (token) {
ownerAccessToken = token;
// 2 === editor
request.userIndex = 2;
request.user = editor;
return testUtils.doAuth(request);
}).then(function (token) {
editorAccessToken = token;
request.user = author;
return testUtils.doAuth(request);
}).then(function (token) {
authorAccessToken = token;
done();
}).catch(done);
});
@ -50,7 +74,10 @@ describe('User API', function () {
should.exist(jsonResponse.users);
testUtils.API.checkResponse(jsonResponse, 'users');
jsonResponse.users.should.have.length(4);
// owner use when Ghost starts
// and two extra users, see createUser in before
jsonResponse.users.should.have.length(3);
testUtils.API.checkResponse(jsonResponse.users[0], 'user');
testUtils.API.isISO8601(jsonResponse.users[0].last_login).should.be.true();
testUtils.API.isISO8601(jsonResponse.users[0].created_at).should.be.true();
@ -80,7 +107,7 @@ describe('User API', function () {
should.exist(jsonResponse.users);
testUtils.API.checkResponse(jsonResponse, 'users');
jsonResponse.users.should.have.length(4);
jsonResponse.users.should.have.length(3);
testUtils.API.checkResponse(jsonResponse.users[0], 'user');
done();
});
@ -102,7 +129,7 @@ describe('User API', function () {
should.exist(jsonResponse.users);
testUtils.API.checkResponse(jsonResponse, 'users');
jsonResponse.users.should.have.length(4);
jsonResponse.users.should.have.length(3);
testUtils.API.checkResponse(jsonResponse.users[0], 'user', 'roles');
done();
});
@ -133,7 +160,7 @@ describe('User API', function () {
});
it('can retrieve a user by id', function (done) {
request.get(testUtils.API.getApiQuery('users/2/'))
request.get(testUtils.API.getApiQuery('users/' + author.id + '/'))
.set('Authorization', 'Bearer ' + ownerAccessToken)
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
@ -272,7 +299,7 @@ describe('User API', function () {
});
it('can\'t retrieve non existent user by id', function (done) {
request.get(testUtils.API.getApiQuery('users/99/'))
request.get(testUtils.API.getApiQuery('users/' + ObjectId.generate() + '/'))
.set('Authorization', 'Bearer ' + ownerAccessToken)
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
@ -329,6 +356,7 @@ describe('User API', function () {
var jsonResponse = res.body,
changedValue = 'http://joe-bloggs.ghost.org',
dataToSend;
should.exist(jsonResponse.users[0]);
testUtils.API.checkResponse(jsonResponse.users[0], 'user');
@ -396,10 +424,10 @@ describe('User API', function () {
describe('As Editor', function () {
describe('success cases', function () {
it('can edit himself', function (done) {
request.put(testUtils.API.getApiQuery('users/3/'))
request.put(testUtils.API.getApiQuery('users/' + editor.id + '/'))
.set('Authorization', 'Bearer ' + editorAccessToken)
.send({
users: [{id: 3, name: 'test'}]
users: [{id: editor.id, name: 'test'}]
})
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
@ -416,10 +444,56 @@ describe('User API', function () {
describe('error cases', function () {
it('can\'t edit the owner', function (done) {
request.put(testUtils.API.getApiQuery('users/1/'))
request.put(testUtils.API.getApiQuery('users/' + testUtils.DataGenerator.Content.users[0].id + '/'))
.set('Authorization', 'Bearer ' + editorAccessToken)
.send({
users: [{id: 1}]
users: [{
id: testUtils.DataGenerator.Content.users[0].id
}]
})
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(403)
.end(function (err) {
if (err) {
return done(err);
}
done();
});
});
});
});
describe('As Author', function () {
describe('success cases', function () {
it('can edit himself', function (done) {
request.put(testUtils.API.getApiQuery('users/' + author.id + '/'))
.set('Authorization', 'Bearer ' + authorAccessToken)
.send({
users: [{id: author.id, name: 'test'}]
})
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(200)
.end(function (err) {
if (err) {
return done(err);
}
done();
});
});
});
describe('error cases', function () {
it('can\'t edit the owner', function (done) {
request.put(testUtils.API.getApiQuery('users/' + testUtils.DataGenerator.Content.users[0].id + '/'))
.set('Authorization', 'Bearer ' + authorAccessToken)
.send({
users: [{
id: testUtils.DataGenerator.Content.users[0].id
}]
})
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)

View File

@ -240,7 +240,7 @@ describe('Frontend Routing', function () {
it('should redirect to editor', function (done) {
request.get('/welcome-to-ghost/edit/')
.expect('Location', '/ghost/editor/1/')
.expect('Location', /ghost\/editor\/\w+/)
.expect('Cache-Control', testUtils.cacheRules.public)
.expect(302)
.end(doEnd(done));
@ -375,7 +375,7 @@ describe('Frontend Routing', function () {
it('should redirect to editor', function (done) {
request.get('/static-page-test/edit/')
.expect('Location', /^\/ghost\/editor\/[0-9]\/$/)
.expect('Location', /ghost\/editor\/\w+/)
.expect('Cache-Control', testUtils.cacheRules.public)
.expect(302)
.end(doEnd(done));

View File

@ -7,7 +7,7 @@ var testUtils = require('../../utils'),
TagAPI = require('../../../server/api/tags'),
UserAPI = require('../../../server/api/users');
describe('Filter Param Spec', function () {
describe('Advanced Browse', function () {
// Initialise the DB just once, the tests are fetch-only
before(testUtils.teardown);
before(testUtils.setup('filter'));
@ -20,7 +20,7 @@ describe('Filter Param Spec', function () {
describe('Advanced Use Cases', function () {
describe('1. Posts - filter: "tags: [photo, video] + id: -4", limit: "3", include: "tags"', function () {
it('Will fetch 3 posts with tags which match `photo` or `video` and are not the post with id 4.', function (done) {
PostAPI.browse({filter: 'tags: [photo, video] + id: -4', limit: 3, include: 'tags'}).then(function (result) {
PostAPI.browse({filter: 'tags: [photo, video] + id: -' + testUtils.filterData.data.posts[3].id, limit: 3, include: 'tags'}).then(function (result) {
var ids;
// 1. Result should have the correct base structure
should.exist(result);
@ -33,14 +33,14 @@ describe('Filter Param Spec', function () {
// None of the items returned should be the post with id 4, as that was excluded
ids = _.map(result.posts, 'id');
ids.should.not.containEql(4);
ids.should.not.containEql(testUtils.filterData.data.posts[3].id);
// Should not contain draft
ids.should.not.containEql(19);
ids.should.not.containEql(testUtils.filterData.data.posts[18].id);
// The ordering specifies that any post which matches both tags should be first
// Post 2 is the first in the list to have both tags
ids[0].should.eql(2);
ids[0].should.eql(testUtils.filterData.data.posts[1].id);
// Each post should have a tag which matches either 'photo' or 'video'
_.each(result.posts, function (post) {
@ -82,7 +82,17 @@ describe('Filter Param Spec', function () {
result.posts.should.be.an.Array().with.lengthOf(9);
ids = _.map(result.posts, 'id');
ids.should.eql([14, 11, 9, 8, 7, 6, 5, 3, 2]);
ids.should.eql([
testUtils.filterData.data.posts[13].id,
testUtils.filterData.data.posts[10].id,
testUtils.filterData.data.posts[8].id,
testUtils.filterData.data.posts[7].id,
testUtils.filterData.data.posts[6].id,
testUtils.filterData.data.posts[5].id,
testUtils.filterData.data.posts[4].id,
testUtils.filterData.data.posts[2].id,
testUtils.filterData.data.posts[1].id
]);
_.each(result.posts, function (post) {
post.page.should.be.false();
@ -165,7 +175,14 @@ describe('Filter Param Spec', function () {
});
ids = _.map(result.posts, 'id');
ids.should.eql([14, 12, 11, 9, 8, 7]);
ids.should.eql([
testUtils.filterData.data.posts[13].id,
testUtils.filterData.data.posts[11].id,
testUtils.filterData.data.posts[10].id,
testUtils.filterData.data.posts[8].id,
testUtils.filterData.data.posts[7].id,
testUtils.filterData.data.posts[6].id
]);
// 3. The meta object should contain the right details
result.meta.should.have.property('pagination');
@ -197,7 +214,11 @@ describe('Filter Param Spec', function () {
result.users.should.be.an.Array().with.lengthOf(2);
ids = _.map(result.users, 'id');
ids.should.eql([1, 2]);
ids.should.eql([
testUtils.filterData.data.posts[0].id,
testUtils.filterData.data.posts[1].id
]);
// TODO: add the order
// TODO: manage the count
@ -252,7 +273,11 @@ describe('Filter Param Spec', function () {
result.users.should.be.an.Array().with.lengthOf(2);
ids = _.map(result.users, 'id');
ids.should.eql([2, 1]);
ids.should.eql([
testUtils.filterData.data.users[1].id,
testUtils.filterData.data.users[0].id
]);
should.exist(result.users[0].website);
should.exist(result.users[1].website);
@ -286,9 +311,10 @@ describe('Filter Param Spec', function () {
result.tags.should.be.an.Array().with.lengthOf(3);
ids = _.map(result.tags, 'id');
ids.should.containEql(4);
ids.should.containEql(3);
ids.should.containEql(2);
ids.should.containEql(testUtils.filterData.data.tags[3].id);
ids.should.containEql(testUtils.filterData.data.tags[2].id);
ids.should.containEql(testUtils.filterData.data.tags[1].id);
// @TODO standardise how alphabetical ordering is done across DBs (see #6104)
// ids.should.eql([4, 2, 3]);
@ -406,7 +432,14 @@ describe('Filter Param Spec', function () {
}).count.posts.should.eql(3);
ids = _.map(result.tags, 'id');
ids.should.eql([4, 3, 1, 2, 6, 5]);
ids.should.eql([
testUtils.filterData.data.tags[3].id,
testUtils.filterData.data.tags[2].id,
testUtils.filterData.data.tags[0].id,
testUtils.filterData.data.tags[1].id,
testUtils.filterData.data.tags[5].id,
testUtils.filterData.data.tags[4].id
]);
// 3. The meta object should contain the right details
result.meta.should.have.property('pagination');
@ -493,7 +526,12 @@ describe('Filter Param Spec', function () {
}).count.posts.should.eql(0);
ids = _.map(result.users, 'id');
ids.should.eql([3, 2, 1]);
ids.should.eql([
testUtils.filterData.data.users[2].id,
testUtils.filterData.data.users[1].id,
testUtils.filterData.data.users[0].id
]);
// 3. The meta object should contain the right details
result.meta.should.have.property('pagination');
@ -534,7 +572,12 @@ describe('Filter Param Spec', function () {
result.posts.should.be.an.Array().with.lengthOf(4);
ids = _.map(result.posts, 'id');
ids.should.eql([11, 9, 3, 2]);
ids.should.eql([
testUtils.filterData.data.posts[10].id,
testUtils.filterData.data.posts[8].id,
testUtils.filterData.data.posts[2].id,
testUtils.filterData.data.posts[1].id
]);
// 3. The meta object should contain the right details
result.meta.should.have.property('pagination');
@ -566,7 +609,13 @@ describe('Filter Param Spec', function () {
result.posts.should.be.an.Array().with.lengthOf(5);
ids = _.map(result.posts, 'id');
ids.should.eql([13, 12, 11, 10, 9]);
ids.should.eql([
testUtils.filterData.data.posts[12].id,
testUtils.filterData.data.posts[11].id,
testUtils.filterData.data.posts[10].id,
testUtils.filterData.data.posts[9].id,
testUtils.filterData.data.posts[8].id
]);
// 3. The meta object should contain the right details
result.meta.should.have.property('pagination');
@ -600,7 +649,23 @@ describe('Filter Param Spec', function () {
result.posts.should.be.an.Array().with.lengthOf(15);
ids = _.map(result.posts, 'id');
ids.should.eql([20, 18, 17, 16, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4]);
ids.should.eql([
testUtils.filterData.data.posts[19].id,
testUtils.filterData.data.posts[17].id,
testUtils.filterData.data.posts[16].id,
testUtils.filterData.data.posts[15].id,
testUtils.filterData.data.posts[13].id,
testUtils.filterData.data.posts[12].id,
testUtils.filterData.data.posts[11].id,
testUtils.filterData.data.posts[10].id,
testUtils.filterData.data.posts[9].id,
testUtils.filterData.data.posts[8].id,
testUtils.filterData.data.posts[7].id,
testUtils.filterData.data.posts[6].id,
testUtils.filterData.data.posts[5].id,
testUtils.filterData.data.posts[4].id,
testUtils.filterData.data.posts[3].id
]);
// 3. The meta object should contain the right details
result.meta.should.have.property('pagination');
@ -637,7 +702,11 @@ describe('Filter Param Spec', function () {
// Match exact items
ids = _.map(result.posts, 'id');
ids.should.eql([14, 8, 5]);
ids.should.eql([
testUtils.filterData.data.posts[13].id,
testUtils.filterData.data.posts[7].id,
testUtils.filterData.data.posts[4].id
]);
// 3. The meta object should contain the right details
result.meta.should.have.property('pagination');
@ -674,7 +743,23 @@ describe('Filter Param Spec', function () {
// Match exact items
ids = _.map(result.posts, 'id');
ids.should.eql([20, 18, 17, 16, 13, 12, 11, 10, 9, 7, 6, 4, 3, 2, 1]);
ids.should.eql([
testUtils.filterData.data.posts[19].id,
testUtils.filterData.data.posts[17].id,
testUtils.filterData.data.posts[16].id,
testUtils.filterData.data.posts[15].id,
testUtils.filterData.data.posts[12].id,
testUtils.filterData.data.posts[11].id,
testUtils.filterData.data.posts[10].id,
testUtils.filterData.data.posts[9].id,
testUtils.filterData.data.posts[8].id,
testUtils.filterData.data.posts[6].id,
testUtils.filterData.data.posts[5].id,
testUtils.filterData.data.posts[3].id,
testUtils.filterData.data.posts[2].id,
testUtils.filterData.data.posts[1].id,
testUtils.filterData.data.posts[0].id
]);
// 3. The meta object should contain the right details
result.meta.should.have.property('pagination');
@ -713,7 +798,26 @@ describe('Filter Param Spec', function () {
// Match exact items
ids = _.map(result.posts, 'id');
ids.should.eql([20, 18, 17, 16, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]);
ids.should.eql([
testUtils.filterData.data.posts[19].id,
testUtils.filterData.data.posts[17].id,
testUtils.filterData.data.posts[16].id,
testUtils.filterData.data.posts[15].id,
testUtils.filterData.data.posts[13].id,
testUtils.filterData.data.posts[12].id,
testUtils.filterData.data.posts[11].id,
testUtils.filterData.data.posts[10].id,
testUtils.filterData.data.posts[9].id,
testUtils.filterData.data.posts[8].id,
testUtils.filterData.data.posts[7].id,
testUtils.filterData.data.posts[6].id,
testUtils.filterData.data.posts[5].id,
testUtils.filterData.data.posts[4].id,
testUtils.filterData.data.posts[3].id,
testUtils.filterData.data.posts[2].id,
testUtils.filterData.data.posts[1].id,
testUtils.filterData.data.posts[0].id
]);
// 3. The meta object should contain the right details
result.meta.should.have.property('pagination');
@ -751,7 +855,10 @@ describe('Filter Param Spec', function () {
// Match exact items
ids = _.map(result.posts, 'id');
ids.should.eql([21, 15]);
ids.should.eql([
testUtils.filterData.data.posts[20].id,
testUtils.filterData.data.posts[14].id
]);
// 3. The meta object should contain the right details
result.meta.should.have.property('pagination');

View File

@ -4,7 +4,6 @@ var testUtils = require('../../utils'),
sinon = require('sinon'),
Promise = require('bluebird'),
uid = require('../../../server/utils').uid,
globalUtils = require('../../../server/utils'),
AuthAPI = require('../../../server/api/authentication'),
mail = require('../../../server/api/mail'),
models = require('../../../server/models'),
@ -15,7 +14,6 @@ var testUtils = require('../../utils'),
Refreshtoken,
User;
// @TODO: group tests by api call, not by setup completed or not
describe('Authentication API', function () {
var testInvite = {
invitation: [{
@ -33,7 +31,6 @@ describe('Authentication API', function () {
testReset = {
passwordreset: [{
token: 'abc',
oldPassword: 'Sl1m3rson',
newPassword: 'abcdefgh',
ne2Password: 'abcdefgh'
}]
@ -61,7 +58,7 @@ describe('Authentication API', function () {
User = require('../../../server/models/user').User;
});
beforeEach(testUtils.setup('roles', 'owner:pre', 'settings', 'perms:setting', 'perms:mail', 'perms:init'));
beforeEach(testUtils.setup('owner:pre', 'settings', 'perms:setting', 'perms:mail', 'perms:init'));
describe('Invalid database state', function () {
it('should not allow setup to be run if owner missing from database', function (done) {
@ -89,7 +86,7 @@ describe('Authentication API', function () {
describe('Not completed', function () {
// TODO: stub settings
beforeEach(testUtils.setup('roles', 'owner:pre', 'settings', 'perms:setting', 'perms:mail', 'perms:init'));
beforeEach(testUtils.setup('owner:pre', 'settings', 'perms:setting', 'perms:mail', 'perms:init'));
it('should report that setup has not been completed', function (done) {
AuthAPI.isSetup().then(function (result) {
@ -117,7 +114,7 @@ describe('Authentication API', function () {
var newUser = result.users[0];
newUser.id.should.equal(1);
newUser.id.should.equal(testUtils.DataGenerator.Content.users[0].id);
newUser.name.should.equal(setupData.name);
newUser.email.should.equal(setupData.email);
@ -141,7 +138,7 @@ describe('Authentication API', function () {
var newUser = result.users[0];
newUser.id.should.equal(1);
newUser.id.should.equal(testUtils.DataGenerator.Content.users[0].id);
newUser.name.should.equal(setupData.name);
newUser.email.should.equal(setupData.email);
@ -257,10 +254,10 @@ describe('Authentication API', function () {
it('should allow an invitation to be accepted', function () {
var invite;
return models.Invite.add({email: '123@meins.de', role_id: testUtils.roles.ids.admin}, context.internal)
return models.Invite.add({email: '123@meins.de', role_id: testUtils.DataGenerator.Content.roles[0].id}, context.internal)
.then(function (_invite) {
invite = _invite;
invite.toJSON().role_id.should.eql(testUtils.roles.ids.admin);
invite.toJSON().role_id.should.eql(testUtils.DataGenerator.Content.roles[0].id);
return models.Invite.edit({status: 'sent'}, _.merge({}, {id: invite.id}, context.internal));
})
@ -287,7 +284,8 @@ describe('Authentication API', function () {
}, _.merge({include: ['roles']}, context.internal));
})
.then(function (user) {
user.toJSON().roles.length.should.eql(testUtils.roles.ids.admin);
user.toJSON().roles.length.should.eql(1);
user.toJSON().roles[0].id.should.eql(testUtils.DataGenerator.Content.roles[0].id);
});
});
@ -350,7 +348,7 @@ describe('Authentication API', function () {
}).catch(done);
});
it('should NOT allow a password reset', function (done) {
it('should allow a password reset', function (done) {
AuthAPI.resetPassword(testReset).then(function () {
done(new Error('password reset did not fail on token validation'));
}).catch(function (err) {
@ -364,47 +362,15 @@ describe('Authentication API', function () {
}).catch(done);
});
it('should allow a password reset', function (done) {
sandbox.stub(globalUtils.tokens.resetToken, 'generateHash').returns('valid-token');
sandbox.stub(globalUtils.tokens.resetToken, 'extract').returns({email: 'jbloggs@example.com', expires: Date.now() + (60 * 1000)});
sandbox.stub(globalUtils.tokens.resetToken, 'compare').returns(true);
models.User.edit({status: 'locked'}, {id: 1})
.then(function (user) {
user.get('status').should.eql('locked');
return AuthAPI.generateResetToken(testGenerateReset);
})
.then(function () {
return AuthAPI.resetPassword(testReset);
})
.then(function () {
return models.User.findOne({id: 1});
})
.then(function (user) {
user.get('status').should.eql('active');
return models.User.isPasswordCorrect({
plainPassword: testReset.passwordreset[0].newPassword,
hashedPassword: user.get('password')
});
})
.then(function () {
done();
})
.catch(function (err) {
done(err);
});
});
it('should allow an access token to be revoked', function (done) {
var id = uid(191);
Accesstoken.add({
token: id,
expires: Date.now() + 8640000,
user_id: 1,
client_id: 1
}).then(function (token) {
user_id: testUtils.DataGenerator.Content.users[0].id,
client_id: testUtils.DataGenerator.forKnex.clients[0].id
}, testUtils.context.internal).then(function (token) {
should.exist(token);
token.get('token').should.equal(id);
@ -469,8 +435,8 @@ describe('Authentication API', function () {
Refreshtoken.add({
token: id,
expires: Date.now() + 8640000,
user_id: 1,
client_id: 1
user_id: testUtils.DataGenerator.Content.users[0].id,
client_id: testUtils.DataGenerator.forKnex.clients[0].id
}).then(function (token) {
should.exist(token);
token.get('token').should.equal(id);
@ -497,8 +463,8 @@ describe('Authentication API', function () {
Accesstoken.add({
token: id,
expires: Date.now() + 8640000,
user_id: 1,
client_id: 1
user_id: testUtils.DataGenerator.Content.users[0].id,
client_id: testUtils.DataGenerator.forKnex.clients[0].id
}).then(function (token) {
should.exist(token);
token.get('token').should.equal(id);
@ -520,7 +486,7 @@ describe('Authentication API', function () {
describe('Setup Update', function () {
describe('Setup not complete', function () {
beforeEach(testUtils.setup('roles', 'owner:pre', 'settings', 'perms:setting', 'perms:init'));
beforeEach(testUtils.setup('owner:pre', 'settings', 'perms:setting', 'perms:init'));
it('should report that setup has not been completed', function (done) {
AuthAPI.isSetup().then(function (result) {
@ -614,7 +580,7 @@ describe('Authentication API', function () {
var newUser = result.users[0];
newUser.id.should.equal(1);
newUser.id.should.equal(testUtils.DataGenerator.Content.users[0].id);
newUser.name.should.equal(setupData.name);
newUser.email.should.equal(setupData.email);

View File

@ -2,6 +2,7 @@ var testUtils = require('../../utils'),
should = require('should'),
sinon = require('sinon'),
_ = require('lodash'),
ObjectId = require('bson-objectid'),
Promise = require('bluebird'),
InvitesAPI = require('../../../server/api/invites'),
mail = require('../../../server/api/mail'),
@ -130,7 +131,7 @@ describe('Invites API', function () {
describe('Destroy', function () {
it('destroy invite', function (done) {
InvitesAPI.destroy(_.merge({}, testUtils.context.owner, {id: 1}))
InvitesAPI.destroy(_.merge({}, testUtils.context.owner, {id: testUtils.DataGenerator.forKnex.invites[0].id}))
.then(function () {
return InvitesAPI.read(_.merge({}, testUtils.context.owner, {
email: 'test1@ghost.org'
@ -142,7 +143,7 @@ describe('Invites API', function () {
});
it('destroy invite: id does not exist', function (done) {
InvitesAPI.destroy({context: {user: 1}, id: 100})
InvitesAPI.destroy(_.merge({id: ObjectId.generate()}, testUtils.context.owner))
.then(function () {
throw new Error('expect error on destroy invite');
})

View File

@ -1,9 +1,7 @@
var testUtils = require('../../utils'),
should = require('should'),
_ = require('lodash'),
uuid = require('node-uuid'),
// Stuff we are testing
ObjectId = require('bson-objectid'),
NotificationsAPI = require('../../../server/api/notifications'),
SettingsAPI = require('../../../server/api/settings');
@ -61,7 +59,8 @@ describe('Notifications API', function () {
var msg = {
type: 'info',
message: 'Hello, this is dog number 3',
id: 99
// id can't be passed from outside
id: ObjectId.generate()
};
NotificationsAPI.add({notifications: [msg]}, testUtils.context.internal).then(function (result) {
@ -71,8 +70,7 @@ describe('Notifications API', function () {
should.exist(result.notifications);
notification = result.notifications[0];
notification.id.should.be.a.Number();
notification.id.should.not.equal(99);
notification.id.should.not.equal(msg.id);
should.exist(notification.status);
notification.status.should.equal('alert');
@ -156,7 +154,6 @@ describe('Notifications API', function () {
type: 'info',
location: 'test.to-be-deleted',
custom: true,
uuid: uuid.v4(),
dismissible: true,
message: 'Hello, this is dog number 4'
};
@ -170,7 +167,7 @@ describe('Notifications API', function () {
return SettingsAPI.read(_.extend({key: 'seenNotifications'}, testUtils.context.internal));
}).then(function (response) {
should.exist(response);
response.settings[0].value.should.containEql(customNotification.uuid);
response.settings[0].value.should.containEql(notification.id);
done();
}).catch(done);

View File

@ -1,6 +1,7 @@
var Promise = require('bluebird'),
should = require('should'),
_ = require('lodash'),
ObjectId = require('bson-objectid'),
testUtils = require('../../utils'),
configUtils = require('../../utils/configUtils'),
errors = require('../../../server/errors'),
@ -39,10 +40,6 @@ describe('Post API', function () {
.catch(done);
});
function extractFirstPost(posts) {
return _.filter(posts, {id: 1})[0];
}
should.exist(PostAPI);
describe('Browse', function () {
@ -75,7 +72,7 @@ describe('Post API', function () {
});
it('can fetch featured posts for user 1', function (done) {
PostAPI.browse({context: {user: 1}, filter: 'featured:true'}).then(function (results) {
PostAPI.browse(_.merge({filter: 'featured:true'}, testUtils.context.owner)).then(function (results) {
should.exist(results.posts);
results.posts.length.should.eql(4);
results.posts[0].featured.should.eql(true);
@ -84,7 +81,7 @@ describe('Post API', function () {
});
it('can fetch featured posts for user 2', function (done) {
PostAPI.browse({context: {user: 2}, filter: 'featured:true'}).then(function (results) {
PostAPI.browse(_.merge({filter: 'featured:true'}, testUtils.context.admin)).then(function (results) {
should.exist(results.posts);
results.posts.length.should.eql(4);
results.posts[0].featured.should.eql(true);
@ -93,7 +90,7 @@ describe('Post API', function () {
});
it('can exclude featured posts for user 1', function (done) {
PostAPI.browse({context: {user: 1}, status: 'all', filter: 'featured:false'}).then(function (results) {
PostAPI.browse(_.merge({status: 'all', filter: 'featured:false'}, testUtils.context.owner)).then(function (results) {
should.exist(results.posts);
results.posts.length.should.eql(1);
results.posts[0].featured.should.eql(false);
@ -475,7 +472,7 @@ describe('Post API', function () {
should.exist(results);
should.exist(results.posts);
results.posts.length.should.be.above(0);
firstPost = extractFirstPost(results.posts);
firstPost = _.find(results.posts, {title: testUtils.DataGenerator.Content.posts[0].title});
return PostAPI.read({slug: firstPost.slug, include: 'tags'});
}).then(function (found) {
var post;
@ -534,16 +531,16 @@ describe('Post API', function () {
});
it('can fetch post with by id', function (done) {
PostAPI.read({context: {user: 1}, id: 2, status: 'all'}).then(function (results) {
PostAPI.read({context: {user: testUtils.DataGenerator.Content.users[1].id}, id: testUtils.DataGenerator.Content.posts[1].id, status: 'all'}).then(function (results) {
should.exist(results.posts);
results.posts[0].id.should.eql(2);
results.posts[0].id.should.eql(testUtils.DataGenerator.Content.posts[1].id);
results.posts[0].slug.should.eql('ghostly-kitchen-sink');
done();
}).catch(done);
});
it('can include tags', function (done) {
PostAPI.read({context: {user: 1}, id: 3, include: 'tags'}).then(function (results) {
PostAPI.read({context: {user: testUtils.DataGenerator.Content.users[1].id}, id: testUtils.DataGenerator.Content.posts[2].id, include: 'tags'}).then(function (results) {
should.exist(results.posts[0].tags);
results.posts[0].tags[0].slug.should.eql('chorizo');
done();
@ -551,7 +548,7 @@ describe('Post API', function () {
});
it('can include author', function (done) {
PostAPI.read({context: {user: 1}, id: 2, include: 'author'}).then(function (results) {
PostAPI.read({context: {user: testUtils.DataGenerator.Content.users[1].id}, id: testUtils.DataGenerator.Content.posts[1].id, include: 'author'}).then(function (results) {
should.exist(results.posts[0].author.name);
results.posts[0].author.name.should.eql('Joe Bloggs');
done();
@ -559,7 +556,7 @@ describe('Post API', function () {
});
it('can include next post', function (done) {
PostAPI.read({context: {user: 1}, id: 3, include: 'next'}).then(function (results) {
PostAPI.read({context: {user: testUtils.DataGenerator.Content.users[1].id}, id: testUtils.DataGenerator.Content.posts[2].id, include: 'next'}).then(function (results) {
should.exist(results.posts[0].next.slug);
results.posts[0].next.slug.should.eql('not-so-short-bit-complex');
done();
@ -567,7 +564,7 @@ describe('Post API', function () {
});
it('can include next post with author and tags', function (done) {
PostAPI.read({context: {user: 1}, id: 3, include: 'next,next.tags,next.author'}).then(function (results) {
PostAPI.read({context: {user: testUtils.DataGenerator.Content.users[1].id}, id: testUtils.DataGenerator.Content.posts[2].id, include: 'next,next.tags,next.author'}).then(function (results) {
should.exist(results.posts[0].next.slug);
results.posts[0].next.slug.should.eql('not-so-short-bit-complex');
results.posts[0].next.author.should.be.an.Object();
@ -577,10 +574,10 @@ describe('Post API', function () {
});
it('can include next post with just tags', function (done) {
PostAPI.read({context: {user: 1}, id: 2, include: 'next,next.tags'}).then(function (results) {
PostAPI.read({context: {user: testUtils.DataGenerator.Content.users[1].id}, id: testUtils.DataGenerator.Content.posts[1].id, include: 'next,next.tags'}).then(function (results) {
should.exist(results.posts[0].next.slug);
results.posts[0].next.slug.should.eql('short-and-sweet');
results.posts[0].next.author.should.eql(1);
results.posts[0].next.author.should.eql('1');
results.posts[0].next.tags.should.be.an.Array();
results.posts[0].next.tags[0].name.should.eql('chorizo');
done();
@ -588,7 +585,7 @@ describe('Post API', function () {
});
it('can include previous post', function (done) {
PostAPI.read({context: {user: 1}, id: 3, include: 'previous'}).then(function (results) {
PostAPI.read({context: {user: testUtils.DataGenerator.Content.users[1].id}, id: testUtils.DataGenerator.Content.posts[2].id, include: 'previous'}).then(function (results) {
should.exist(results.posts[0].previous.slug);
results.posts[0].previous.slug.should.eql('ghostly-kitchen-sink');
done();
@ -596,7 +593,7 @@ describe('Post API', function () {
});
it('can include previous post with author and tags', function (done) {
PostAPI.read({context: {user: 1}, id: 3, include: 'previous,previous.author,previous.tags'}).then(function (results) {
PostAPI.read({context: {user: testUtils.DataGenerator.Content.users[1].id}, id: testUtils.DataGenerator.Content.posts[2].id, include: 'previous,previous.author,previous.tags'}).then(function (results) {
should.exist(results.posts[0].previous.slug);
results.posts[0].previous.slug.should.eql('ghostly-kitchen-sink');
results.posts[0].previous.author.should.be.an.Object();
@ -609,7 +606,7 @@ describe('Post API', function () {
});
it('can include previous post with just author', function (done) {
PostAPI.read({context: {user: 1}, id: 3, include: 'previous,previous.author'}).then(function (results) {
PostAPI.read({context: {user: testUtils.DataGenerator.Content.users[1].id}, id: testUtils.DataGenerator.Content.posts[2].id, include: 'previous,previous.author'}).then(function (results) {
should.exist(results.posts[0].previous.slug);
should.not.exist(results.posts[0].previous.tags);
results.posts[0].previous.slug.should.eql('ghostly-kitchen-sink');
@ -634,7 +631,7 @@ describe('Post API', function () {
describe('Destroy', function () {
it('can delete a post', function (done) {
var options = {context: {user: 1}, id: 1};
var options = {context: {user: testUtils.DataGenerator.Content.users[1].id}, id: testUtils.DataGenerator.Content.posts[0].id};
PostAPI.read(options).then(function (results) {
should.exist(results.posts[0]);
@ -652,7 +649,7 @@ describe('Post API', function () {
});
it('returns an error when attempting to delete a non-existent post', function (done) {
var options = {context: {user: 1}, id: 123456788};
var options = {context: {user: testUtils.DataGenerator.Content.users[1].id}, id: ObjectId.generate()};
PostAPI.destroy(options).then(function () {
done(new Error('No error was thrown'));
@ -666,26 +663,28 @@ describe('Post API', function () {
describe('Edit', function () {
it('can edit own post', function (done) {
PostAPI.edit({posts:[{status: 'test'}]}, {context: {user: 1}, id: 1}).then(function (results) {
PostAPI.edit({posts:[{status: 'test'}]}, {context: {user: testUtils.DataGenerator.Content.users[1].id}, id: testUtils.DataGenerator.Content.posts[0].id}).then(function (results) {
should.exist(results.posts);
done();
}).catch(done);
});
it('cannot edit others post', function (done) {
testUtils.fixtures.insertOne('users', 'createUser', 4)
.then(function (result) {
PostAPI.edit({posts: [{status: 'test'}]}, {context: {user: result[0]}, id: 1}).catch(function (err) {
should.exist(err);
(err instanceof errors.NoPermissionError).should.eql(true);
done();
});
});
PostAPI.edit(
{posts: [{status: 'test'}]},
{context: {user: testUtils.DataGenerator.Content.users[3].id}, id: testUtils.DataGenerator.Content.posts[0].id}
).then(function () {
done(new Error('expected permission error'));
}).catch(function (err) {
should.exist(err);
(err instanceof errors.NoPermissionError).should.eql(true);
done();
});
});
// These tests are for #6920
it('should update post & not delete tags with `tags` not included', function (done) {
var options = {context: {user: 1}, id: 1},
var options = {context: {user: testUtils.DataGenerator.Content.users[1].id}, id: testUtils.DataGenerator.Content.posts[0].id},
includeOptions = {include: 'tags'},
startTags;
@ -725,7 +724,7 @@ describe('Post API', function () {
});
it('should update post & not delete tags with `tags` set to undefined', function (done) {
var options = {context: {user: 1}, id: 1},
var options = {context: {user: testUtils.DataGenerator.Content.users[1].id}, id: testUtils.DataGenerator.Content.posts[0].id},
includeOptions = {include: 'tags'},
startTags;
@ -765,7 +764,7 @@ describe('Post API', function () {
});
it('should update post & not delete tags with `tags` set to null', function (done) {
var options = {context: {user: 1}, id: 1},
var options = {context: {user: testUtils.DataGenerator.Content.users[1].id}, id: testUtils.DataGenerator.Content.posts[0].id},
includeOptions = {include: 'tags'},
startTags;
@ -805,7 +804,7 @@ describe('Post API', function () {
});
it('should update post & should delete tags with `tags` set to []', function (done) {
var options = {context: {user: 1}, id: 1},
var options = {context: {user: testUtils.DataGenerator.Content.users[1].id}, id: testUtils.DataGenerator.Content.posts[0].id},
includeOptions = {include: 'tags'};
// Step 1, fetch a post from the API with tags

View File

@ -1,7 +1,7 @@
var should = require('should'),
moment = require('moment'),
Promise = require('bluebird'),
ObjectId = require('bson-objectid'),
testUtils = require('../../utils'),
config = require(__dirname + '/../../../server/config'),
sequence = require(config.get('paths').corePath + '/server/utils/sequence'),
@ -243,9 +243,9 @@ describe('Schedules API', function () {
});
it('client with specific perms has access to publish post', function (done) {
api.schedules.publishPost({id: 1, context: {client: 'ghost-scheduler'}})
api.schedules.publishPost({id: scope.posts[0].id, context: {client: 'ghost-scheduler'}})
.then(function (result) {
result.posts[0].id.should.eql(1);
result.posts[0].id.should.eql(scope.posts[0].id);
result.posts[0].status.should.eql('published');
done();
})
@ -253,9 +253,9 @@ describe('Schedules API', function () {
});
it('can publish with tolerance (30 seconds in the future)', function (done) {
api.schedules.publishPost({id: 2, context: {client: 'ghost-scheduler'}})
api.schedules.publishPost({id: scope.posts[1].id, context: {client: 'ghost-scheduler'}})
.then(function (result) {
result.posts[0].id.should.eql(2);
result.posts[0].id.should.eql(scope.posts[1].id);
result.posts[0].status.should.eql('published');
done();
})
@ -263,9 +263,9 @@ describe('Schedules API', function () {
});
it('can publish with tolerance (30seconds in the past)', function (done) {
api.schedules.publishPost({id: 3, context: {client: 'ghost-scheduler'}})
api.schedules.publishPost({id: scope.posts[2].id, context: {client: 'ghost-scheduler'}})
.then(function (result) {
result.posts[0].id.should.eql(3);
result.posts[0].id.should.eql(scope.posts[2].id);
result.posts[0].status.should.eql('published');
done();
})
@ -273,9 +273,9 @@ describe('Schedules API', function () {
});
it('can publish a post in the past with force flag', function (done) {
api.schedules.publishPost({force: true}, {id: 4, context: {client: 'ghost-scheduler'}})
api.schedules.publishPost({force: true}, {id: scope.posts[3].id, context: {client: 'ghost-scheduler'}})
.then(function (result) {
result.posts[0].id.should.eql(4);
result.posts[0].id.should.eql(scope.posts[3].id);
result.posts[0].status.should.eql('published');
done();
})
@ -334,7 +334,7 @@ describe('Schedules API', function () {
});
it('ghost admin has no access', function (done) {
api.schedules.publishPost({id: 1, context: {client: 'ghost-admin'}})
api.schedules.publishPost({id: scope.posts[0].id, context: {client: 'ghost-admin'}})
.then(function () {
done(new Error('expected NoPermissionError'));
})
@ -346,7 +346,7 @@ describe('Schedules API', function () {
});
it('owner has no access (this is how it is right now!)', function (done) {
api.schedules.publishPost({id: 2, context: {user: testUtils.users.ids.author}})
api.schedules.publishPost({id: scope.posts[1].id, context: {user: testUtils.users.ids.author}})
.then(function () {
done(new Error('expected NoPermissionError'));
})
@ -360,7 +360,7 @@ describe('Schedules API', function () {
it('other user has no access', function (done) {
testUtils.fixtures.insertOne('users', 'createUser', 4)
.then(function (result) {
api.schedules.publishPost({id: 1, context: {user: result[0]}})
api.schedules.publishPost({id: scope.posts[0].id, context: {user: result[0]}})
.then(function () {
done(new Error('expected NoPermissionError'));
})
@ -373,8 +373,8 @@ describe('Schedules API', function () {
.catch(done);
});
it('invalid params', function (done) {
api.schedules.publishPost({id: 'bla', context: {client: 'ghost-scheduler'}})
it('invalid params: id is integer', function (done) {
api.schedules.publishPost({id: 100, context: {client: 'ghost-scheduler'}})
.then(function () {
done(new Error('expected ValidationError'));
})
@ -386,7 +386,7 @@ describe('Schedules API', function () {
});
it('post does not exist', function (done) {
api.schedules.publishPost({id: 10, context: {client: 'ghost-scheduler'}})
api.schedules.publishPost({id: ObjectId.generate(), context: {client: 'ghost-scheduler'}})
.then(function () {
done(new Error('expected ValidationError'));
})
@ -398,7 +398,7 @@ describe('Schedules API', function () {
});
it('publish at a wrong time', function (done) {
api.schedules.publishPost({id: 1, context: {client: 'ghost-scheduler'}})
api.schedules.publishPost({id: scope.posts[0].id, context: {client: 'ghost-scheduler'}})
.then(function () {
done(new Error('expected ValidationError'));
})
@ -410,7 +410,7 @@ describe('Schedules API', function () {
});
it('publish at a wrong time', function (done) {
api.schedules.publishPost({id: 3, context: {client: 'ghost-scheduler'}})
api.schedules.publishPost({id: scope.posts[2].id, context: {client: 'ghost-scheduler'}})
.then(function () {
done(new Error('expected ValidationError'));
})
@ -422,7 +422,7 @@ describe('Schedules API', function () {
});
it('publish at a wrong time', function (done) {
api.schedules.publishPost({id: 4, context: {client: 'ghost-scheduler'}})
api.schedules.publishPost({id: scope.posts[3].id, context: {client: 'ghost-scheduler'}})
.then(function () {
done(new Error('expected ValidationError'));
})
@ -434,7 +434,7 @@ describe('Schedules API', function () {
});
it('publish, but status is draft', function (done) {
api.schedules.publishPost({id: 2, context: {client: 'ghost-scheduler'}})
api.schedules.publishPost({id: scope.posts[1].id, context: {client: 'ghost-scheduler'}})
.then(function () {
done(new Error('expected ValidationError'));
})

View File

@ -2,6 +2,7 @@ var testUtils = require('../../utils'),
should = require('should'),
sinon = require('sinon'),
Promise = require('bluebird'),
ObjectId = require('bson-objectid'),
fs = require('fs'),
_ = require('lodash'),
context = testUtils.context,
@ -92,7 +93,7 @@ describe('Subscribers API', function () {
describe('Edit', function () {
var newSubscriberEmail = 'subscriber@updated.com',
firstSubscriber = 1;
firstSubscriber = testUtils.DataGenerator.Content.subscribers[0].id;
it('can edit a subscriber (admin)', function (done) {
SubscribersAPI.edit({subscribers: [{email: newSubscriberEmail}]}, _.extend({}, context.admin, {id: firstSubscriber}))
@ -137,7 +138,7 @@ describe('Subscribers API', function () {
});
it('CANNOT edit subscriber that doesn\'t exit', function (done) {
SubscribersAPI.edit({subscribers: [{email: newSubscriberEmail}]}, _.extend({}, context.internal, {id: 999}))
SubscribersAPI.edit({subscribers: [{email: newSubscriberEmail}]}, _.extend({}, context.internal, {id: ObjectId.generate()}))
.then(function () {
done(new Error('Edit non-existent subscriber is possible.'));
}, function (err) {
@ -149,7 +150,7 @@ describe('Subscribers API', function () {
});
describe('Destroy', function () {
var firstSubscriber = 1;
var firstSubscriber = testUtils.DataGenerator.Content.subscribers[0].id;
it('can destroy subscriber as admin', function (done) {
SubscribersAPI.destroy(_.extend({}, testUtils.context.admin, {id: firstSubscriber}))
@ -205,18 +206,13 @@ describe('Subscribers API', function () {
});
describe('Read', function () {
function extractFirstSubscriber(subscribers) {
return _.filter(subscribers, {id: 1})[0];
}
it('with id', function (done) {
SubscribersAPI.browse({context: {user: 1}}).then(function (results) {
should.exist(results);
should.exist(results.subscribers);
results.subscribers.length.should.be.above(0);
var firstSubscriber = extractFirstSubscriber(results.subscribers);
var firstSubscriber = _.find(results.subscribers, {id: testUtils.DataGenerator.Content.subscribers[0].id});
return SubscribersAPI.read({context: {user: 1}, id: firstSubscriber.id});
}).then(function (found) {
should.exist(found);

View File

@ -27,7 +27,7 @@ describe('Tags API', function () {
var newTag;
beforeEach(function () {
newTag = _.clone(testUtils.DataGenerator.forKnex.createTag(testUtils.DataGenerator.Content.tags[0]));
newTag = _.clone(_.omit(testUtils.DataGenerator.forKnex.createTag(testUtils.DataGenerator.Content.tags[0]), 'id'));
Promise.resolve(newTag);
});
@ -76,7 +76,7 @@ describe('Tags API', function () {
describe('Edit', function () {
var newTagName = 'tagNameUpdated',
firstTag = 1;
firstTag = testUtils.DataGenerator.Content.tags[0].id;
it('can edit a tag (admin)', function (done) {
TagAPI.edit({tags: [{name: newTagName}]}, _.extend({}, context.admin, {id: firstTag}))
@ -121,7 +121,8 @@ describe('Tags API', function () {
});
describe('Destroy', function () {
var firstTag = 1;
var firstTag = testUtils.DataGenerator.Content.tags[0].id;
it('can destroy Tag', function (done) {
TagAPI.destroy(_.extend({}, testUtils.context.admin, {id: firstTag}))
.then(function (results) {
@ -313,10 +314,6 @@ describe('Tags API', function () {
});
describe('Read', function () {
function extractFirstTag(tags) {
return _.filter(tags, {id: 1})[0];
}
it('returns count.posts with include count.posts', function (done) {
TagAPI.read({context: {user: 1}, include: 'count.posts', slug: 'kitchen-sink'}).then(function (results) {
should.exist(results);
@ -337,7 +334,7 @@ describe('Tags API', function () {
should.exist(results.tags);
results.tags.length.should.be.above(0);
var firstTag = extractFirstTag(results.tags);
var firstTag = _.find(results.tags, {id: testUtils.DataGenerator.Content.tags[0].id});
return TagAPI.read({context: {user: 1}, slug: firstTag.slug});
}).then(function (found) {

View File

@ -199,7 +199,7 @@ describe('Users API', function () {
should.exist(response);
should.not.exist(response.meta);
should.exist(response.users);
response.users[0].id.should.eql(1);
response.users[0].id.should.eql(testUtils.DataGenerator.Content.users[0].id);
if (noEmail) {
// Email should be missing
@ -330,18 +330,17 @@ describe('Users API', function () {
it('Admin can edit Admin, Editor and Author roles with roles in payload', function (done) {
UserAPI.edit({users: [{name: newName, roles: [roleIdFor.admin]}]}, _.extend({}, context.admin, {id: userIdFor.admin})).then(function (response) {
checkEditResponse(response);
checkEditResponse(response);
return UserAPI.edit({users: [{name: newName, roles: [roleIdFor.editor]}]}, _.extend({}, context.admin, {id: userIdFor.editor}));
}).then(function (response) {
checkEditResponse(response);
return UserAPI.edit({users: [{name: newName, roles: [roleIdFor.editor]}]}, _.extend({}, context.admin, {id: userIdFor.editor}));
}).then(function (response) {
checkEditResponse(response);
return UserAPI.edit({users: [{name: newName, roles: [roleIdFor.author]}]}, _.extend({}, context.admin, {id: userIdFor.author}));
}).then(function (response) {
checkEditResponse(response);
return UserAPI.edit({users: [{name: newName, roles: [roleIdFor.author]}]}, _.extend({}, context.admin, {id: userIdFor.author}));
}).then(function (response) {
checkEditResponse(response);
done();
}).catch(done);
done();
}).catch(done);
});
it('Editor CANNOT edit Owner, Admin or Editor roles', function (done) {
@ -363,7 +362,7 @@ describe('Users API', function () {
}).finally(function () {
// Cannot edit Editor
UserAPI.edit(
{users: [{name: newName}]}, _.extend({}, context.editor, {id: userIdFor.editor2})
{users: [{name: newName}]}, _.extend({}, context.editor, {id: testUtils.DataGenerator.Content.extraUsers[1].id})
).then(function () {
done(new Error('Editor should not be able to edit other editor account'));
}).catch(function (error) {
@ -408,7 +407,7 @@ describe('Users API', function () {
error.errorType.should.eql('NoPermissionError');
}).finally(function () {
UserAPI.edit(
{users: [{name: newName}]}, _.extend({}, context.author, {id: userIdFor.author2})
{users: [{name: newName}]}, _.extend({}, context.author, {id: testUtils.DataGenerator.Content.extraUsers[2].id})
).then(function () {
done(new Error('Author should not be able to edit author account which is not their own'));
}).catch(function (error) {
@ -469,6 +468,7 @@ describe('Users API', function () {
Promise.mapSeries(testUtils.DataGenerator.forKnex.posts, function (post, i) {
post = _.cloneDeep(post);
delete post.id;
if (i % 2) {
post.author_id = userIdFor.editor;
@ -591,17 +591,17 @@ describe('Users API', function () {
it('Can destroy admin, editor, author', function (done) {
// Admin
UserAPI.destroy(_.extend({}, context.admin, {id: userIdFor.admin2}))
UserAPI.destroy(_.extend({}, context.admin, {id: testUtils.DataGenerator.Content.extraUsers[0].id}))
.then(function (response) {
should.not.exist(response);
// Editor
return UserAPI.destroy(_.extend({}, context.admin, {id: userIdFor.editor2}));
return UserAPI.destroy(_.extend({}, context.admin, {id: testUtils.DataGenerator.Content.extraUsers[1].id}));
}).then(function (response) {
should.not.exist(response);
// Author
return UserAPI.destroy(_.extend({}, context.admin, {id: userIdFor.author2}));
return UserAPI.destroy(_.extend({}, context.admin, {id: testUtils.DataGenerator.Content.extraUsers[2].id}));
}).then(function (response) {
should.not.exist(response);
@ -626,7 +626,7 @@ describe('Users API', function () {
});
it('CANNOT destroy other editor', function (done) {
UserAPI.destroy(_.extend({}, context.editor, {id: userIdFor.editor2}))
UserAPI.destroy(_.extend({}, context.editor, {id: testUtils.DataGenerator.Content.extraUsers[1].id}))
.then(function () {
done(new Error('Editor should not be able to delete other editor'));
}).catch(checkForErrorType('NoPermissionError', done));
@ -672,7 +672,7 @@ describe('Users API', function () {
});
it('CANNOT destroy other author', function (done) {
UserAPI.destroy(_.extend({}, context.author, {id: userIdFor.author2}))
UserAPI.destroy(_.extend({}, context.author, {id: testUtils.DataGenerator.Content.extraUsers[2].id}))
.then(function () {
done(new Error('Author should not be able to delete other author'));
}).catch(checkForErrorType('NoPermissionError', done));
@ -850,10 +850,10 @@ describe('Users API', function () {
it('Can assign author role to author', function (done) {
UserAPI.edit(
{users: [{name: newName, roles: [roleIdFor.author]}]},
_.extend({}, context.editor, {id: userIdFor.author2}, {include: 'roles'})
_.extend({}, context.editor, {id: testUtils.DataGenerator.Content.extraUsers[2].id}, {include: 'roles'})
).then(function (response) {
checkEditResponse(response);
response.users[0].id.should.equal(userIdFor.author2);
response.users[0].id.should.equal(testUtils.DataGenerator.Content.extraUsers[2].id);
response.users[0].roles[0].name.should.equal('Author');
done();
@ -872,7 +872,7 @@ describe('Users API', function () {
it('CANNOT assign author role to other Editor', function (done) {
UserAPI.edit(
{users: [{name: newName, roles: [roleIdFor.author]}]},
_.extend({}, context.editor, {id: userIdFor.editor2}, {include: 'roles'})
_.extend({}, context.editor, {id: testUtils.DataGenerator.Content.extraUsers[1].id}, {include: 'roles'})
).then(function () {
done(new Error('Editor should not be able to change the roles of other editors'));
}).catch(checkForErrorType('NoPermissionError', done));
@ -919,8 +919,8 @@ describe('Users API', function () {
response.users.should.have.length(2);
testUtils.API.checkResponse(response.users[0], 'user', ['roles']);
testUtils.API.checkResponse(response.users[1], 'user', ['roles']);
response.users[0].roles[0].id.should.equal(1);
response.users[1].roles[0].id.should.equal(4);
response.users[0].roles[0].id.should.equal(testUtils.DataGenerator.Content.roles[0].id);
response.users[1].roles[0].id.should.equal(testUtils.DataGenerator.Content.roles[3].id);
done();
}).catch(done);
});

View File

@ -614,7 +614,7 @@ describe('Import', function () {
return importer.doImport(exportData);
}).then(function () {
done(new Error('Allowed import of invalid tags data'));
done(new Error('Allowed import of invalid post data'));
}).catch(function (response) {
response.length.should.equal(5, response);
done();
@ -776,13 +776,13 @@ describe('Import (new test structure)', function () {
_.each(rolesUsers, function (roleUser) {
if (roleUser.user_id === user1.id) {
roleUser.role_id.should.equal(4, 'Original user should be an owner');
roleUser.role_id.should.equal(testUtils.DataGenerator.Content.roles[3].id, 'Original user should be an owner');
}
if (roleUser.user_id === user2.id) {
roleUser.role_id.should.equal(1, 'Josephine should be an admin');
roleUser.role_id.should.equal(testUtils.DataGenerator.Content.roles[0].id, 'Josephine should be an admin');
}
if (roleUser.user_id === user3.id) {
roleUser.role_id.should.equal(3, 'Smith should be an author by default');
roleUser.role_id.should.equal(testUtils.DataGenerator.Content.roles[2].id, 'Smith should be an author by default');
}
});
@ -1001,13 +1001,13 @@ describe('Import (new test structure)', function () {
_.each(rolesUsers, function (roleUser) {
if (roleUser.user_id === user1.id) {
roleUser.role_id.should.equal(4, 'Original user should be an owner');
roleUser.role_id.should.equal(testUtils.DataGenerator.Content.roles[3].id, 'Original user should be an owner');
}
if (roleUser.user_id === user2.id) {
roleUser.role_id.should.equal(1, 'Josephine should be an admin');
roleUser.role_id.should.equal(testUtils.DataGenerator.Content.roles[0].id, 'Josephine should be an admin');
}
if (roleUser.user_id === user3.id) {
roleUser.role_id.should.equal(3, 'Smith should be an author by default');
roleUser.role_id.should.equal(testUtils.DataGenerator.Content.roles[2].id, 'Smith should be an author by default');
}
});
@ -1233,13 +1233,13 @@ describe('Import (new test structure)', function () {
_.each(rolesUsers, function (roleUser) {
if (roleUser.user_id === ownerUser.id) {
roleUser.role_id.should.equal(4, 'Original user should be an owner');
roleUser.role_id.should.equal(testUtils.DataGenerator.Content.roles[3].id, 'Original user should be an owner');
}
if (roleUser.user_id === newUser.id) {
roleUser.role_id.should.equal(1, 'New user should be an admin');
roleUser.role_id.should.equal(testUtils.DataGenerator.Content.roles[0].id, 'New user should be an admin');
}
if (roleUser.user_id === existingUser.id) {
roleUser.role_id.should.equal(1, 'Existing user was an admin');
roleUser.role_id.should.equal(testUtils.DataGenerator.Content.roles[0].id, 'Existing user was an admin');
}
});
@ -1387,13 +1387,13 @@ describe('Import (new test structure)', function () {
_.each(rolesUsers, function (roleUser) {
if (roleUser.user_id === ownerUser.id) {
roleUser.role_id.should.equal(4, 'Original user should be an owner');
roleUser.role_id.should.equal(testUtils.DataGenerator.Content.roles[3].id, 'Original user should be an owner');
}
if (roleUser.user_id === newUser.id) {
roleUser.role_id.should.equal(1, 'New user should be downgraded from owner to admin');
roleUser.role_id.should.equal(testUtils.DataGenerator.Content.roles[0].id, 'New user should be downgraded from owner to admin');
}
if (roleUser.user_id === existingUser.id) {
roleUser.role_id.should.equal(1, 'Existing user was an admin');
roleUser.role_id.should.equal(testUtils.DataGenerator.Content.roles[0].id, 'Existing user was an admin');
}
});

View File

@ -17,17 +17,18 @@ describe('Accesstoken Model', function () {
sandbox.restore();
});
beforeEach(testUtils.setup('users', 'clients'));
beforeEach(testUtils.setup('users:roles', 'clients'));
it('on creation emits token.added event', function (done) {
// Setup
var eventSpy = sandbox.spy(events, 'emit');
// Test
// Stub refreshtoken
AccesstokenModel.add({
token: 'foobartoken',
user_id: 1,
client_id: 1,
user_id: testUtils.DataGenerator.Content.users[0].id,
client_id: testUtils.DataGenerator.forKnex.clients[0].id,
expires: Date.now() + utils.ONE_HOUR_MS
})
.then(function (token) {

View File

@ -26,7 +26,7 @@ describe('App Fields Model', function () {
});
it('can findOne', function (done) {
AppFieldsModel.findOne({id: 1}).then(function (foundAppField) {
AppFieldsModel.findOne({id: testUtils.DataGenerator.Content.app_fields[0].id}).then(function (foundAppField) {
should.exist(foundAppField);
foundAppField.get('created_at').should.be.an.instanceof(Date);
@ -36,12 +36,12 @@ describe('App Fields Model', function () {
});
it('can edit', function (done) {
AppFieldsModel.findOne({id: 1}).then(function (foundAppField) {
AppFieldsModel.findOne({id: testUtils.DataGenerator.Content.app_fields[0].id}).then(function (foundAppField) {
should.exist(foundAppField);
return foundAppField.set({value: '350'}).save(null, context);
}).then(function () {
return AppFieldsModel.findOne({id: 1});
return AppFieldsModel.findOne({id: testUtils.DataGenerator.Content.app_fields[0].id});
}).then(function (updatedAppField) {
should.exist(updatedAppField);

View File

@ -26,7 +26,7 @@ describe('App Setting Model', function () {
});
it('can findOne', function (done) {
AppSettingModel.findOne({id: 1}).then(function (foundAppSetting) {
AppSettingModel.findOne({id: testUtils.DataGenerator.Content.app_settings[0].id}).then(function (foundAppSetting) {
should.exist(foundAppSetting);
foundAppSetting.get('created_at').should.be.an.instanceof(Date);
@ -36,12 +36,12 @@ describe('App Setting Model', function () {
});
it('can edit', function (done) {
AppSettingModel.findOne({id: 1}).then(function (foundAppSetting) {
AppSettingModel.findOne({id: testUtils.DataGenerator.Content.app_settings[0].id}).then(function (foundAppSetting) {
should.exist(foundAppSetting);
return foundAppSetting.set({value: '350'}).save(null, context);
}).then(function () {
return AppSettingModel.findOne({id: 1});
return AppSettingModel.findOne({id: testUtils.DataGenerator.Content.app_settings[0].id});
}).then(function (updatedAppSetting) {
should.exist(updatedAppSetting);

View File

@ -28,7 +28,7 @@ describe('App Model', function () {
});
it('can findOne', function (done) {
AppModel.findOne({id: 1}).then(function (foundApp) {
AppModel.findOne({id: testUtils.DataGenerator.Content.apps[0].id}).then(function (foundApp) {
should.exist(foundApp);
foundApp.get('created_at').should.be.an.instanceof(Date);
@ -38,12 +38,12 @@ describe('App Model', function () {
});
it('can edit', function (done) {
AppModel.findOne({id: 1}).then(function (foundApp) {
AppModel.findOne({id: testUtils.DataGenerator.Content.apps[0].id}).then(function (foundApp) {
should.exist(foundApp);
return foundApp.set({name: 'New App'}).save(null, context);
}).then(function () {
return AppModel.findOne({id: 1});
return AppModel.findOne({id: testUtils.DataGenerator.Content.apps[0].id});
}).then(function (updatedApp) {
should.exist(updatedApp);
@ -66,7 +66,7 @@ describe('App Model', function () {
});
it('can destroy', function (done) {
var firstApp = {id: 1};
var firstApp = {id: testUtils.DataGenerator.Content.apps[0].id};
AppModel.findOne(firstApp).then(function (foundApp) {
should.exist(foundApp);

View File

@ -26,7 +26,7 @@ describe('Permission Model', function () {
});
it('can findOne', function (done) {
PermissionModel.findOne({id: 1}).then(function (foundPermission) {
PermissionModel.findOne({id: testUtils.DataGenerator.Content.permissions[0].id}).then(function (foundPermission) {
should.exist(foundPermission);
foundPermission.get('created_at').should.be.an.instanceof(Date);
@ -35,12 +35,12 @@ describe('Permission Model', function () {
});
it('can edit', function (done) {
PermissionModel.findOne({id: 1}).then(function (foundPermission) {
PermissionModel.findOne({id: testUtils.DataGenerator.Content.permissions[0].id}).then(function (foundPermission) {
should.exist(foundPermission);
return foundPermission.set({name: 'updated'}).save(null, context);
}).then(function () {
return PermissionModel.findOne({id: 1});
return PermissionModel.findOne({id: testUtils.DataGenerator.Content.permissions[0].id});
}).then(function (updatedPermission) {
should.exist(updatedPermission);
@ -67,7 +67,7 @@ describe('Permission Model', function () {
});
it('can destroy', function (done) {
var firstPermission = {id: 1};
var firstPermission = {id: testUtils.DataGenerator.Content.permissions[0].id};
PermissionModel.findOne(firstPermission).then(function (foundPermission) {
should.exist(foundPermission);

View File

@ -54,10 +54,6 @@ describe('Post Model', function () {
beforeEach(testUtils.setup('owner', 'posts', 'apps'));
function extractFirstPost(posts) {
return _.filter(posts, {id: 1})[0];
}
function checkFirstPostData(firstPost) {
should.not.exist(firstPost.author_id);
firstPost.author.should.be.an.Object();
@ -94,14 +90,12 @@ describe('Post Model', function () {
.then(function (results) {
should.exist(results);
results.length.should.be.above(0);
var posts = results.models.map(function (model) {
return model.toJSON();
});
}), firstPost = _.find(posts, {title: testUtils.DataGenerator.Content.posts[0].title});
// the first post in the result is not always the post at
// position 0 in the fixture data so we need to use extractFirstPost
// to get the post with id: 1
checkFirstPostData(extractFirstPost(posts));
checkFirstPostData(firstPost);
done();
}).catch(done);
@ -138,10 +132,8 @@ describe('Post Model', function () {
results.meta.pagination.pages.should.equal(1);
results.posts.length.should.equal(4);
// the first post in the result is not always the post at
// position 0 in the fixture data so we need to use extractFirstPost
// to get the post with id: 1
checkFirstPostData(extractFirstPost(results.posts));
var firstPost = _.find(results.posts, {title: testUtils.DataGenerator.Content.posts[0].title});
checkFirstPostData(firstPost);
done();
}).catch(done);
@ -151,8 +143,7 @@ describe('Post Model', function () {
PostModel.findPage({columns: ['id', 'slug', 'url', 'markdown']}).then(function (results) {
should.exist(results);
var post = extractFirstPost(results.posts);
var post = _.find(results.posts, {slug: testUtils.DataGenerator.Content.posts[0].slug});
post.url.should.equal('/html-ipsum/');
// If a computed property is inadvertently passed into a "fetch" operation,
@ -168,9 +159,8 @@ describe('Post Model', function () {
PostModel.findPage({columns: ['id', 'slug', 'doesnotexist']}).then(function (results) {
should.exist(results);
var post = extractFirstPost(results.posts);
post.id.should.equal(1);
var post = _.find(results.posts, {slug: testUtils.DataGenerator.Content.posts[0].slug});
post.id.should.equal(testUtils.DataGenerator.Content.posts[0].id);
post.slug.should.equal('html-ipsum');
should.not.exist(post.doesnotexist);
@ -337,12 +327,10 @@ describe('Post Model', function () {
});
it('can findOne, returning a slug only permalink', function (done) {
var firstPost = 1;
PostModel.findOne({id: firstPost})
PostModel.findOne({id: testUtils.DataGenerator.Content.posts[0].id})
.then(function (result) {
should.exist(result);
firstPost = result.toJSON();
var firstPost = result.toJSON();
firstPost.url.should.equal('/html-ipsum/');
done();
@ -350,14 +338,12 @@ describe('Post Model', function () {
});
it('can findOne, returning a dated permalink', function (done) {
var firstPost = 1;
configUtils.set('theme:permalinks', '/:year/:month/:day/:slug/');
PostModel.findOne({id: firstPost})
PostModel.findOne({id: testUtils.DataGenerator.Content.posts[0].id})
.then(function (result) {
should.exist(result);
firstPost = result.toJSON();
var firstPost = result.toJSON();
// published_at of post 1 is 2015-01-01 00:00:00
// default blog TZ is UTC
@ -370,7 +356,7 @@ describe('Post Model', function () {
describe('edit', function () {
it('can change title', function (done) {
var postId = 1;
var postId = testUtils.DataGenerator.Content.posts[0].id;
PostModel.findOne({id: postId}).then(function (results) {
var post;
@ -392,7 +378,7 @@ describe('Post Model', function () {
});
it('can change title to number', function (done) {
var postId = 1;
var postId = testUtils.DataGenerator.Content.posts[0].id;
PostModel.findOne({id: postId}).then(function (results) {
should.exist(results);
@ -407,7 +393,7 @@ describe('Post Model', function () {
});
it('can change markdown to number', function (done) {
var postId = 1;
var postId = testUtils.DataGenerator.Content.posts[0].id;
PostModel.findOne({id: postId}).then(function (results) {
should.exist(results);
@ -422,7 +408,7 @@ describe('Post Model', function () {
});
it('can publish draft post', function (done) {
var postId = 4;
var postId = testUtils.DataGenerator.Content.posts[3].id;
PostModel.findOne({id: postId, status: 'draft'}).then(function (results) {
var post;
@ -444,7 +430,7 @@ describe('Post Model', function () {
});
it('can unpublish published post', function (done) {
var postId = 1;
var postId = testUtils.DataGenerator.Content.posts[0].id;
PostModel.findOne({id: postId}).then(function (results) {
var post;
@ -597,7 +583,7 @@ describe('Post Model', function () {
});
it('published -> scheduled and expect update of published_at', function (done) {
var postId = 1;
var postId = testUtils.DataGenerator.Content.posts[0].id;
PostModel.findOne({id: postId}).then(function (results) {
var post;
@ -620,7 +606,7 @@ describe('Post Model', function () {
});
it('can convert draft post to page and back', function (done) {
var postId = 4;
var postId = testUtils.DataGenerator.Content.posts[3].id;
PostModel.findOne({id: postId, status: 'draft'}).then(function (results) {
var post;
@ -686,7 +672,7 @@ describe('Post Model', function () {
});
it('can convert published post to page and back', function (done) {
var postId = 1;
var postId = testUtils.DataGenerator.Content.posts[0].id;
PostModel.findOne({id: postId}).then(function (results) {
var post;
@ -723,7 +709,7 @@ describe('Post Model', function () {
});
it('can change type and status at the same time', function (done) {
var postId = 4;
var postId = testUtils.DataGenerator.Content.posts[3].id;
PostModel.findOne({id: postId, status: 'draft'}).then(function (results) {
var post;
@ -790,7 +776,7 @@ describe('Post Model', function () {
});
it('cannot override the published_by setting', function (done) {
var postId = 4;
var postId = testUtils.DataGenerator.Content.posts[3].id;
PostModel.findOne({id: postId, status: 'draft'}).then(function (results) {
var post;
@ -844,12 +830,12 @@ describe('Post Model', function () {
(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.get('created_by').should.equal(testUtils.DataGenerator.Content.users[0].id);
createdPost.get('author_id').should.equal(testUtils.DataGenerator.Content.users[0].id);
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);
createdPost.get('updated_by').should.equal(testUtils.DataGenerator.Content.users[0].id);
should.equal(createdPost.get('published_at'), null);
should.equal(createdPost.get('published_by'), null);
@ -862,9 +848,9 @@ describe('Post Model', function () {
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('published_by').should.equal(testUtils.DataGenerator.Content.users[0].id);
publishedPost.get('updated_at').should.be.instanceOf(Date);
publishedPost.get('updated_by').should.equal(1);
publishedPost.get('updated_by').should.equal(testUtils.DataGenerator.Content.users[0].id);
publishedPost.get('updated_at').should.not.equal(createdPostUpdatedDate);
eventSpy.calledThrice.should.be.true();
@ -1187,8 +1173,8 @@ describe('Post Model', function () {
describe('destroy', function () {
it('published post', function (done) {
// We're going to try deleting post id 1 which also has tag id 1
var firstItemData = {id: 1};
// We're going to try deleting post id 1 which has tag id 1
var firstItemData = {id: testUtils.DataGenerator.Content.posts[0].id};
// Test that we have the post we expect, with exactly one tag
PostModel.findOne(firstItemData, {include: ['tags']}).then(function (results) {
@ -1198,7 +1184,7 @@ describe('Post Model', function () {
post.id.should.equal(firstItemData.id);
post.status.should.equal('published');
post.tags.should.have.length(2);
post.tags[0].id.should.equal(firstItemData.id);
post.tags[0].id.should.equal(testUtils.DataGenerator.Content.tags[0].id);
// Destroy the post
return results.destroy();
@ -1226,8 +1212,8 @@ describe('Post Model', function () {
});
it('draft post', function (done) {
// We're going to try deleting post id 4 which also has tag id 4
var firstItemData = {id: 4, status: 'draft'};
// We're going to try deleting post 4 which also has tag 4
var firstItemData = {id: testUtils.DataGenerator.Content.posts[3].id, status: 'draft'};
// Test that we have the post we expect, with exactly one tag
PostModel.findOne(firstItemData, {include: ['tags']}).then(function (results) {
@ -1236,7 +1222,7 @@ describe('Post Model', function () {
post = results.toJSON();
post.id.should.equal(firstItemData.id);
post.tags.should.have.length(1);
post.tags[0].id.should.equal(firstItemData.id);
post.tags[0].id.should.equal(testUtils.DataGenerator.Content.tags[3].id);
// Destroy the post
return results.destroy(firstItemData);
@ -1263,8 +1249,8 @@ describe('Post Model', function () {
});
it('published page', function (done) {
// We're going to try deleting page id 6 which also has tag id 1
var firstItemData = {id: 6};
// We're going to try deleting page 6 which has tag 1
var firstItemData = {id: testUtils.DataGenerator.Content.posts[5].id};
// Test that we have the post we expect, with exactly one tag
PostModel.findOne(firstItemData, {include: ['tags']}).then(function (results) {
@ -1301,8 +1287,8 @@ describe('Post Model', function () {
});
it('draft page', function (done) {
// We're going to try deleting post id 4 which also has tag id 4
var firstItemData = {id: 7, status: 'draft'};
// We're going to try deleting post 7 which has tag 4
var firstItemData = {id: testUtils.DataGenerator.Content.posts[6].id, status: 'draft'};
// Test that we have the post we expect, with exactly one tag
PostModel.findOne(firstItemData, {include: ['tags']}).then(function (results) {
@ -1344,7 +1330,7 @@ describe('Post Model', function () {
it('can destroy multiple posts by author', function (done) {
// We're going to delete all posts by user 1
var authorData = {id: 1};
var authorData = {id: testUtils.DataGenerator.Content.users[0].id};
PostModel.findAll({context:{internal:true}}).then(function (found) {
// There are 50 posts to begin with

View File

@ -27,7 +27,7 @@ describe('Role Model', function () {
});
it('can findOne', function (done) {
RoleModel.findOne({id: 1}).then(function (foundRole) {
RoleModel.findOne({id: testUtils.DataGenerator.Content.roles[0].id}).then(function (foundRole) {
should.exist(foundRole);
foundRole.get('created_at').should.be.an.instanceof(Date);
@ -36,12 +36,12 @@ describe('Role Model', function () {
});
it('can edit', function (done) {
RoleModel.findOne({id: 1}).then(function (foundRole) {
RoleModel.findOne({id: testUtils.DataGenerator.Content.roles[0].id}).then(function (foundRole) {
should.exist(foundRole);
return foundRole.set({name: 'updated'}).save(null, context);
}).then(function () {
return RoleModel.findOne({id: 1});
return RoleModel.findOne({id: testUtils.DataGenerator.Content.roles[0].id});
}).then(function (updatedRole) {
should.exist(updatedRole);
@ -68,7 +68,7 @@ describe('Role Model', function () {
});
it('can destroy', function (done) {
var firstRole = {id: 1};
var firstRole = {id: testUtils.DataGenerator.Content.roles[0].id};
RoleModel.findOne(firstRole).then(function (foundRole) {
should.exist(foundRole);

View File

@ -138,7 +138,7 @@ describe('Settings Model', function () {
SettingsModel.add(newSetting, context).then(function (createdSetting) {
should.exist(createdSetting);
createdSetting.has('uuid').should.equal(true);
createdSetting.has('uuid').should.equal(false);
createdSetting.attributes.key.should.equal(newSetting.key, 'key is correct');
createdSetting.attributes.value.should.equal(newSetting.value, 'value is correct');
createdSetting.attributes.type.should.equal('core');
@ -152,22 +152,20 @@ describe('Settings Model', function () {
});
it('can destroy', function (done) {
// dont't use id 1, since it will delete databaseversion
var settingToDestroy = {id: 2};
SettingsModel.findAll().then(function (allSettings) {
// dont't use index 0, since it will delete databaseversion
SettingsModel.findOne({id: allSettings.models[1].id}).then(function (results) {
should.exist(results);
results.attributes.id.should.equal(allSettings.models[1].id);
return SettingsModel.destroy({id: allSettings.models[1].id});
}).then(function (response) {
response.toJSON().should.be.empty();
return SettingsModel.findOne({id: allSettings.models[1].id});
}).then(function (newResults) {
should.equal(newResults, null);
SettingsModel.findOne(settingToDestroy).then(function (results) {
should.exist(results);
results.attributes.id.should.equal(settingToDestroy.id);
return SettingsModel.destroy(settingToDestroy);
}).then(function (response) {
response.toJSON().should.be.empty();
return SettingsModel.findOne(settingToDestroy);
}).then(function (newResults) {
should.equal(newResults, null);
done();
done();
}).catch(done);
}).catch(done);
});
});

View File

@ -50,7 +50,6 @@ describe('User Model', function run() {
UserModel.add(userData, context).then(function (createdUser) {
should.exist(createdUser);
createdUser.has('uuid').should.equal(true);
createdUser.attributes.password.should.not.equal(userData.password, 'password was hashed');
createdUser.attributes.email.should.eql(userData.email, 'email address correct');
@ -99,7 +98,6 @@ describe('User Model', function run() {
UserModel.add(userData, context).then(function (createdUser) {
should.exist(createdUser);
createdUser.has('uuid').should.equal(true);
createdUser.attributes.email.should.eql(userData.email, 'email address correct');
done();
}).catch(done);
@ -115,7 +113,6 @@ describe('User Model', function run() {
UserModel.add(userData, context).then(function (createdUser) {
should.exist(createdUser);
createdUser.has('uuid').should.equal(true);
createdUser.attributes.image.should.eql(
'http://www.gravatar.com/avatar/2fab21a4c4ed88e76add10650c73bae1?d=404', 'Gravatar found'
);
@ -132,7 +129,6 @@ describe('User Model', function run() {
UserModel.add(userData, context).then(function (createdUser) {
should.exist(createdUser);
createdUser.has('uuid').should.equal(true);
should.not.exist(createdUser.image);
done();
}).catch(done);
@ -342,7 +338,6 @@ describe('User Model', function run() {
UserModel.add(_.extend({}, userData, {status: 'invited'}), context).then(function (createdUser) {
should.exist(createdUser);
createdUser.has('uuid').should.equal(true);
createdUser.attributes.password.should.not.equal(userData.password, 'password was hashed');
createdUser.attributes.email.should.eql(userData.email, 'email address correct');
@ -362,7 +357,6 @@ describe('User Model', function run() {
return UserModel.add(userData, _.extend({}, context, {include: ['roles']}));
}).then(function (createdUser) {
should.exist(createdUser);
createdUser.has('uuid').should.equal(true);
createdUser.get('password').should.not.equal(userData.password, 'password was hashed');
createdUser.get('email').should.eql(userData.email, 'email address correct');
createdUser.related('roles').toJSON()[0].name.should.eql('Administrator', 'role set correctly');
@ -392,7 +386,7 @@ describe('User Model', function run() {
});
it('can edit active user', function (done) {
var firstUser = 1;
var firstUser = testUtils.DataGenerator.Content.users[0].id;
UserModel.findOne({id: firstUser}).then(function (results) {
var user;
@ -415,7 +409,7 @@ describe('User Model', function run() {
});
it('can NOT set an invalid email address', function (done) {
var firstUser = 1;
var firstUser = testUtils.DataGenerator.Content.users[0].id;
UserModel.findOne({id: firstUser}).then(function (user) {
return user.edit({email: 'notanemailaddress'});
@ -432,7 +426,6 @@ describe('User Model', function run() {
UserModel.add(_.extend({}, userData, {status: 'invited'}), context).then(function (createdUser) {
should.exist(createdUser);
createdUser.has('uuid').should.equal(true);
createdUser.attributes.password.should.not.equal(userData.password, 'password was hashed');
createdUser.attributes.email.should.eql(userData.email, 'email address correct');
createdUser.attributes.status.should.equal('invited');
@ -458,7 +451,6 @@ describe('User Model', function run() {
UserModel.add(_.extend({}, userData, {status: 'invited'}), context).then(function (createdUser) {
should.exist(createdUser);
createdUser.has('uuid').should.equal(true);
createdUser.attributes.password.should.not.equal(userData.password, 'password was hashed');
createdUser.attributes.email.should.eql(userData.email, 'email address correct');
createdUser.attributes.status.should.equal('invited');
@ -480,7 +472,7 @@ describe('User Model', function run() {
});
it('can destroy active user', function (done) {
var firstUser = {id: 1};
var firstUser = {id: testUtils.DataGenerator.Content.users[0].id};
// Test that we have the user we expect
UserModel.findOne(firstUser).then(function (results) {
@ -513,7 +505,6 @@ describe('User Model', function run() {
UserModel.add(_.extend({}, userData, {status: 'invited'}), context).then(function (createdUser) {
should.exist(createdUser);
createdUser.has('uuid').should.equal(true);
createdUser.attributes.password.should.not.equal(userData.password, 'password was hashed');
createdUser.attributes.email.should.eql(userData.email, 'email address correct');
createdUser.attributes.status.should.equal('invited');
@ -550,7 +541,7 @@ describe('User Model', function run() {
newPassword: '12345678',
ne2Password: '12345678',
oldPassword: '123456789',
user_id: 1
user_id: testUtils.DataGenerator.Content.users[0].id
}, testUtils.context.owner).then(function () {
done(new Error('expected error!'));
}).catch(function (err) {
@ -564,7 +555,7 @@ describe('User Model', function run() {
newPassword: '12345678',
ne2Password: '12345678',
oldPassword: '123456789',
user_id: '1'
user_id: testUtils.DataGenerator.Content.users[0].id
}, testUtils.context.owner).then(function () {
done(new Error('expected error!'));
}).catch(function (err) {
@ -580,7 +571,7 @@ describe('User Model', function run() {
newPassword: '12345678',
ne2Password: '12345678',
oldPassword: 'Sl1m3rson',
user_id: 1
user_id: testUtils.DataGenerator.Content.users[0].id
}, testUtils.context.owner).then(function (user) {
user.get('password').should.not.eql('12345678');
done();

View File

@ -2,6 +2,7 @@ var should = require('should'),
sinon = require('sinon'),
_ = require('lodash'),
Promise = require('bluebird'),
ObjectId = require('bson-objectid'),
permissions = require('../../server/permissions'),
errors = require('../../server/errors'),
apiUtils = require('../../server/api/utils'),
@ -136,9 +137,10 @@ describe('API Utils', function () {
});
it('should allow idDefaultOptions when passed', function (done) {
// test read
var id = ObjectId.generate();
apiUtils.validate('test', {opts: apiUtils.idDefaultOptions})(
{id: 5, context: 'stuff'}
{id: id, context: 'stuff'}
).then(function (options) {
options.should.not.have.ownProperty('data');
options.should.not.have.ownProperty('include');
@ -148,7 +150,7 @@ describe('API Utils', function () {
options.should.have.ownProperty('context');
options.context.should.eql('stuff');
options.should.have.ownProperty('id');
options.id.should.eql(5);
options.id.should.eql(id);
done();
}).catch(done);
@ -198,8 +200,8 @@ describe('API Utils', function () {
}
it('can validate `id`', function () {
valid = [1, '1', 304, '304'];
invalid = ['test', 'de305d54'];
valid = [ObjectId.generate(), '1', 1];
invalid = ['test', 'de305d54', 300, '304'];
check('id', valid, invalid);
});

View File

@ -4,12 +4,12 @@ var should = require('should'),
rewire = require('rewire'),
models = require('../../server/models'),
baseUtils = require('../../server/models/base/utils'),
fixtureUtils = rewire('../../server/data/migration/fixtures/utils'),
fixtures = require('../../server/data/migration/fixtures/fixtures'),
sandbox = sinon.sandbox.create();
describe('Utils', function () {
describe('Migration Fixture Utils', function () {
var loggerStub;
beforeEach(function () {
@ -136,15 +136,14 @@ describe('Utils', function () {
it('should call attach for permissions-roles', function (done) {
var fromItem = {
related: sandbox.stub().returnsThis(),
findWhere: sandbox.stub().returns(),
permissions: sandbox.stub().returnsThis(),
attach: sandbox.stub().returns(Promise.resolve([{}]))
findWhere: sandbox.stub().returns()
},
toItem = [{get: sandbox.stub()}],
dataMethodStub = {
filter: sandbox.stub().returns(toItem),
find: sandbox.stub().returns(fromItem)
},
baseUtilAttachStub = sandbox.stub(baseUtils, 'attach').returns(Promise.resolve([{}])),
permsAllStub = sandbox.stub(models.Permission, 'findAll').returns(Promise.resolve(dataMethodStub)),
rolesAllStub = sandbox.stub(models.Role, 'findAll').returns(Promise.resolve(dataMethodStub));
@ -159,15 +158,12 @@ describe('Utils', function () {
rolesAllStub.calledOnce.should.be.true();
dataMethodStub.filter.callCount.should.eql(29);
dataMethodStub.find.callCount.should.eql(3);
baseUtilAttachStub.callCount.should.eql(29);
fromItem.related.callCount.should.eql(29);
fromItem.findWhere.callCount.should.eql(29);
toItem[0].get.callCount.should.eql(58);
fromItem.permissions.callCount.should.eql(29);
fromItem.attach.callCount.should.eql(29);
fromItem.attach.calledWith(toItem).should.be.true();
done();
}).catch(done);
});
@ -175,16 +171,14 @@ describe('Utils', function () {
it('should call attach for posts-tags', function (done) {
var fromItem = {
related: sandbox.stub().returnsThis(),
findWhere: sandbox.stub().returns(),
tags: sandbox.stub().returnsThis(),
attach: sandbox.stub().returns(Promise.resolve([{}]))
findWhere: sandbox.stub().returns()
},
toItem = [{get: sandbox.stub()}],
dataMethodStub = {
filter: sandbox.stub().returns(toItem),
find: sandbox.stub().returns(fromItem)
},
baseUtilAttachStub = sandbox.stub(baseUtils, 'attach').returns(Promise.resolve([{}])),
postsAllStub = sandbox.stub(models.Post, 'findAll').returns(Promise.resolve(dataMethodStub)),
tagsAllStub = sandbox.stub(models.Tag, 'findAll').returns(Promise.resolve(dataMethodStub));
@ -199,14 +193,10 @@ describe('Utils', function () {
tagsAllStub.calledOnce.should.be.true();
dataMethodStub.filter.calledOnce.should.be.true();
dataMethodStub.find.calledOnce.should.be.true();
fromItem.related.calledOnce.should.be.true();
fromItem.findWhere.calledOnce.should.be.true();
toItem[0].get.calledOnce.should.be.true();
fromItem.tags.calledOnce.should.be.true();
fromItem.attach.calledOnce.should.be.true();
fromItem.attach.calledWith(toItem).should.be.true();
baseUtilAttachStub.callCount.should.eql(1);
done();
}).catch(done);

View File

@ -31,7 +31,8 @@ describe('RSS', function () {
return post.status === 'published' && post.page === false;
});
_.each(posts, function (post) {
_.each(posts, function (post, i) {
post.id = i;
post.url = '/' + post.slug + '/';
post.author = {name: 'Joe Bloggs'};
});

View File

@ -1,5 +1,6 @@
var _ = require('lodash'),
uuid = require('node-uuid'),
ObjectId = require('bson-objectid'),
moment = require('moment'),
globalUtils = require('../../../server/utils'),
DataGenerator = {};
@ -9,18 +10,21 @@ var _ = require('lodash'),
DataGenerator.Content = {
posts: [
{
id: ObjectId.generate(),
title: "HTML Ipsum",
slug: "html-ipsum",
markdown: "<h1>HTML Ipsum Presents</h1><p><strong>Pellentesque habitant morbi tristique</strong> senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. <em>Aenean ultricies mi vitae est.</em> Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, <code>commodo vitae</code>, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. <a href=\"#\">Donec non enim</a> in turpis pulvinar facilisis. Ut felis.</p><h2>Header Level 2</h2><ol><li>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</li><li>Aliquam tincidunt mauris eu risus.</li></ol><blockquote><p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est.</p></blockquote><h3>Header Level 3</h3><ul><li>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</li><li>Aliquam tincidunt mauris eu risus.</li></ul><pre><code>#header h1 a{display: block;width: 300px;height: 80px;}</code></pre>",
published_at: new Date("2015-01-01")
},
{
id: ObjectId.generate(),
title: "Ghostly Kitchen Sink",
slug: "ghostly-kitchen-sink",
markdown: "<h1>HTML Ipsum Presents</h1><p><strong>Pellentesque habitant morbi tristique</strong> senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. <em>Aenean ultricies mi vitae est.</em> Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, <code>commodo vitae</code>, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. <a href=\"#\">Donec non enim</a> in turpis pulvinar facilisis. Ut felis.</p><h2>Header Level 2</h2><ol><li>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</li><li>Aliquam tincidunt mauris eu risus.</li></ol><blockquote><p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est.</p></blockquote><h3>Header Level 3</h3><ul><li>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</li><li>Aliquam tincidunt mauris eu risus.</li></ul><pre><code>#header h1 a{display: block;width: 300px;height: 80px;}</code></pre>",
published_at: new Date("2015-01-02")
},
{
id: ObjectId.generate(),
title: "Short and Sweet",
slug: "short-and-sweet",
markdown: "## testing\n\nmctesters\n\n- test\n- line\n- items",
@ -31,6 +35,7 @@ DataGenerator.Content = {
uuid: "2ac6b4f6-e1f3-406c-9247-c94a0496d39d"
},
{
id: ObjectId.generate(),
title: "Not finished yet",
slug: "unfinished",
markdown: "<h1>HTML Ipsum Presents</h1><p><strong>Pellentesque habitant morbi tristique</strong> senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. <em>Aenean ultricies mi vitae est.</em> Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, <code>commodo vitae</code>, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. <a href=\"#\">Donec non enim</a> in turpis pulvinar facilisis. Ut felis.</p><h2>Header Level 2</h2><ol><li>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</li><li>Aliquam tincidunt mauris eu risus.</li></ol><blockquote><p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est.</p></blockquote><h3>Header Level 3</h3><ul><li>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</li><li>Aliquam tincidunt mauris eu risus.</li></ul><pre><code>#header h1 a{display: block;width: 300px;height: 80px;}</code></pre>",
@ -39,17 +44,20 @@ DataGenerator.Content = {
featured: false
},
{
id: ObjectId.generate(),
title: "Not so short, bit complex",
slug: "not-so-short-bit-complex",
markdown: "<p><nav><ul><li><a href=\"#nowhere\" title=\"Anchor URL\">Lorem</a></li><li><a href=\"/about#nowhere\" title=\"Relative URL\">Aliquam</a></li><li><a href=\"//somewhere.com/link#nowhere\" title=\"Protocol Relative URL\">Tortor</a></li><li><a href=\"http://somewhere.com/link#nowhere\" title=\"Absolute URL\">Morbi</a></li><li><a href=\"#nowhere\" title=\"Praesent dapibus, neque id cursus faucibus\">Praesent</a></li><li><a href=\"#nowhere\" title=\"Pellentesque fermentum dolor\">Pellentesque</a></li></ul></nav><p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p><table><thead><tr><th>1</th><th>2</th><th>3</th><th>4</th></tr></thead><tbody><tr><td>a</td><td>b</td><td>c</td><td>d</td></tr><tr><td>e</td><td>f</td><td>g</td><td>h</td></tr><tr><td>i</td><td>j</td><td>k</td><td>l</td></tr></tbody></table><dl><dt>Definition list</dt><dd>Consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</dd><dt>Lorem ipsum dolor sit amet</dt><dd>Consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</dd></dl><ul><li>Morbi in sem quis dui placerat ornare. Pellentesque odio nisi, euismod in, pharetra a, ultricies in, diam. Sed arcu. Cras consequat.</li><li>Praesent dapibus, neque id cursus faucibus, tortor neque egestas augue, eu vulputate magna eros eu erat. Aliquam erat volutpat. Nam dui mi, tincidunt quis, accumsan porttitor, facilisis luctus, metus.</li><li>Phasellus ultrices nulla quis nibh. Quisque a lectus. Donec consectetuer ligula vulputate sem tristique cursus. Nam nulla quam, gravida non, commodo a, sodales sit amet, nisi.</li><li>Pellentesque fermentum dolor. Aliquam quam lectus, facilisis auctor, ultrices ut, elementum vulputate, nunc.</li></ul></p>"
},
{
id: ObjectId.generate(),
title: "This is a static page",
slug: "static-page-test",
markdown: "<h1>Static page test is what this is for.</h1><p>Hopefully you don't find it a bore.</p>",
page: 1
},
{
id: ObjectId.generate(),
title: "This is a draft static page",
slug: "static-page-draft",
markdown: "<h1>Static page test is what this is for.</h1><p>Hopefully you don't find it a bore.</p>",
@ -57,6 +65,7 @@ DataGenerator.Content = {
status: "draft"
},
{
id: ObjectId.generate(),
title: "This is a scheduled post!!",
slug: "scheduled-post",
markdown: "<h1>Welcome to my invisible post!</h1>",
@ -67,22 +76,27 @@ DataGenerator.Content = {
tags: [
{
id: ObjectId.generate(),
name: "kitchen sink",
slug: "kitchen-sink"
},
{
id: ObjectId.generate(),
name: "bacon",
slug: "bacon"
},
{
id: ObjectId.generate(),
name: "chorizo",
slug: "chorizo"
},
{
id: ObjectId.generate(),
name: "pollo",
slug: "pollo"
},
{
id: ObjectId.generate(),
name: "injection",
slug: "injection"
}
@ -91,88 +105,120 @@ DataGenerator.Content = {
// Password = Sl1m3rson
users: [
{
// owner
// owner (owner is still id 1 because of permissions)
id: '1',
name: 'Joe Bloggs',
slug: 'joe-bloggs',
email: 'jbloggs@example.com',
password: '$2a$10$.pZeeBE0gHXd0PTnbT/ph.GEKgd0Wd3q2pWna3ynTGBkPKnGIKZL6'
},
{
// editor
// admin
id: ObjectId.generate(),
name: 'Smith Wellingsworth',
slug: 'smith-wellingsworth',
email: 'swellingsworth@example.com',
password: '$2a$10$.pZeeBE0gHXd0PTnbT/ph.GEKgd0Wd3q2pWna3ynTGBkPKnGIKZL6'
},
{
// author
// editor
id: ObjectId.generate(),
name: 'Jimothy Bogendath',
slug: 'jimothy-bogendath',
email: 'jbOgendAth@example.com',
password: '$2a$10$.pZeeBE0gHXd0PTnbT/ph.GEKgd0Wd3q2pWna3ynTGBkPKnGIKZL6'
},
{
// administrator
// author
id: ObjectId.generate(),
name: 'Slimer McEctoplasm',
slug: 'slimer-mcectoplasm',
email: 'smcectoplasm@example.com',
password: '$2a$10$.pZeeBE0gHXd0PTnbT/ph.GEKgd0Wd3q2pWna3ynTGBkPKnGIKZL6'
},
{
// editor 2
id: ObjectId.generate(),
name: 'Ivan Email',
slug: 'ivan-email',
email: 'info@ghost.org',
email: 'info1@ghost.org',
password: '$2a$10$.pZeeBE0gHXd0PTnbT/ph.GEKgd0Wd3q2pWna3ynTGBkPKnGIKZL6'
},
{
// author 2
id: ObjectId.generate(),
name: 'Author2',
slug: 'a-2',
email: 'info2@ghost.org',
password: '$2a$10$.pZeeBE0gHXd0PTnbT/ph.GEKgd0Wd3q2pWna3ynTGBkPKnGIKZL6'
},
{
// admin 2
id: ObjectId.generate(),
name: 'admin2',
slug: 'ad-2',
email: 'info3@ghost.org',
password: '$2a$10$.pZeeBE0gHXd0PTnbT/ph.GEKgd0Wd3q2pWna3ynTGBkPKnGIKZL6'
}
],
permissions: [
{
id: ObjectId.generate(),
name: 'Browse posts',
action_type: 'browse',
object_type: 'post'
},
{
id: ObjectId.generate(),
name: 'test',
action_type: 'edit',
object_type: 'post'
},
{
id: ObjectId.generate(),
name: 'test',
action_type: 'edit',
object_type: 'tag'
},
{
id: ObjectId.generate(),
name: 'test',
action_type: 'edit',
object_type: 'user'
},
{
id: ObjectId.generate(),
name: 'test',
action_type: 'edit',
object_type: 'page'
},
{
id: ObjectId.generate(),
name: 'test',
action_type: 'add',
object_type: 'post'
},
{
id: ObjectId.generate(),
name: 'test',
action_type: 'add',
object_type: 'user'
},
{
id: ObjectId.generate(),
name: 'test',
action_type: 'add',
object_type: 'page'
},
{
id: ObjectId.generate(),
name: 'test',
action_type: 'destroy',
object_type: 'post'
},
{
id: ObjectId.generate(),
name: 'test',
action_type: 'destroy',
object_type: 'user'
@ -181,18 +227,22 @@ DataGenerator.Content = {
roles: [
{
id: ObjectId.generate(),
name: 'Administrator',
description: 'Administrators'
},
{
id: ObjectId.generate(),
name: 'Editor',
description: 'Editors'
},
{
id: ObjectId.generate(),
name: 'Author',
description: 'Authors'
},
{
id: ObjectId.generate(),
name: 'Owner',
description: 'Blog Owner'
}
@ -200,18 +250,21 @@ DataGenerator.Content = {
apps: [
{
id: ObjectId.generate(),
name: 'Kudos',
slug: 'kudos',
version: '0.0.1',
status: 'installed'
},
{
id: ObjectId.generate(),
name: 'Importer',
slug: 'importer',
version: '0.1.0',
status: 'inactive'
},
{
id: ObjectId.generate(),
name: 'Hemingway',
slug: 'hemingway',
version: '1.0.0',
@ -221,12 +274,14 @@ DataGenerator.Content = {
app_fields: [
{
id: ObjectId.generate(),
key: 'count',
value: '120',
type: 'number',
active: true
},
{
id: ObjectId.generate(),
key: 'words',
value: '512',
type: 'number',
@ -236,10 +291,12 @@ DataGenerator.Content = {
app_settings: [
{
id: ObjectId.generate(),
key: 'color',
value: 'ghosty'
},
{
id: ObjectId.generate(),
key: 'setting',
value: 'value'
}
@ -247,9 +304,11 @@ DataGenerator.Content = {
subscribers: [
{
id: ObjectId.generate(),
email: 'subscriber1@test.com'
},
{
id: ObjectId.generate(),
email: 'subscriber2@test.com'
}
]
@ -271,10 +330,10 @@ DataGenerator.forKnex = (function () {
var newObj = _.cloneDeep(overrides);
return _.defaults(newObj, {
uuid: uuid.v4(),
created_by: 1,
id: ObjectId.generate(),
created_by: DataGenerator.Content.users[0].id,
created_at: new Date(),
updated_by: 1,
updated_by: DataGenerator.Content.users[0].id,
updated_at: new Date()
});
}
@ -283,6 +342,7 @@ DataGenerator.forKnex = (function () {
var newObj = _.cloneDeep(overrides);
return _.defaults(newObj, {
id: ObjectId.generate(),
uuid: uuid.v4(),
title: 'title',
status: 'published',
@ -290,23 +350,23 @@ DataGenerator.forKnex = (function () {
language: 'en_US',
featured: true,
page: false,
author_id: 1,
author_id: DataGenerator.Content.users[0].id,
updated_at: new Date(),
updated_by: 1,
updated_by: DataGenerator.Content.users[0].id,
created_at: new Date(),
created_by: 1,
created_by: DataGenerator.Content.users[0].id,
published_at: new Date(),
published_by: 1
published_by: DataGenerator.Content.users[0].id
});
}
function createGenericPost(uniqueInteger, status, language, author_id) {
status = status || 'draft';
language = language || 'en_US';
author_id = author_id || 1;
author_id = author_id || DataGenerator.Content.users[0].id;
return createPost({
uuid: uuid.v4(),
id: ObjectId.generate(),
title: 'Test Post ' + uniqueInteger,
slug: 'ghost-from-fiction-to-function-' + uniqueInteger,
author_id: author_id,
@ -322,9 +382,12 @@ DataGenerator.forKnex = (function () {
var newObj = _.cloneDeep(overrides);
return _.defaults(newObj, {
uuid: uuid.v4(),
id: ObjectId.generate(),
name: 'name',
slug: 'slug_' + Date.now(),
status: 'active',
created_by: 1,
password: '$2a$10$.pZeeBE0gHXd0PTnbT/ph.GEKgd0Wd3q2pWna3ynTGBkPKnGIKZL6',
created_by: DataGenerator.Content.users[0].id,
created_at: new Date()
});
}
@ -336,6 +399,8 @@ DataGenerator.forKnex = (function () {
basics = createBasic(newObj);
return _.defaults(newObj, {
id: ObjectId.generate(),
uuid: uuid.v4(),
secret: 'not_available',
redirection_uri: 'http://localhost:9999',
client_uri: 'http://localhost:9000',
@ -356,6 +421,7 @@ DataGenerator.forKnex = (function () {
function createPostsTags(postId, tagId) {
return {
id: ObjectId.generate(),
post_id: postId,
tag_id: tagId
};
@ -365,12 +431,12 @@ DataGenerator.forKnex = (function () {
var newObj = _.cloneDeep(overrides);
return _.defaults(newObj, {
uuid: uuid.v4(),
created_by: 1,
id: ObjectId.generate(),
created_by: DataGenerator.Content.users[0].id,
created_at: new Date(),
active: true,
app_id: 1,
relatable_id: 1,
app_id: DataGenerator.Content.apps[0].id,
relatable_id: DataGenerator.Content.posts[0].id,
relatable_type: 'posts'
});
}
@ -379,9 +445,9 @@ DataGenerator.forKnex = (function () {
var newObj = _.cloneDeep(overrides);
return _.defaults(newObj, {
uuid: uuid.v4(),
app_id: 1,
created_by: 1,
id: ObjectId.generate(),
app_id: DataGenerator.Content.apps[0].id,
created_by: DataGenerator.Content.users[0].id,
created_at: new Date()
});
}
@ -390,8 +456,9 @@ DataGenerator.forKnex = (function () {
var newObj = _.cloneDeep(overrides);
return _.defaults(newObj, {
id: ObjectId.generate(),
token: uuid.v4(),
client_id: 1,
client_id: clients[0].id,
expires: Date.now() + globalUtils.ONE_DAY_MS
});
}
@ -400,11 +467,12 @@ DataGenerator.forKnex = (function () {
var newObj = _.cloneDeep(overrides);
return _.defaults(newObj, {
id: ObjectId.generate(),
token: uuid.v4(),
email: 'test@ghost.org',
role_id: 1,
role_id: DataGenerator.Content.roles[0].id,
expires: Date.now() + (60 * 1000),
created_by: 1,
created_by: DataGenerator.Content.users[0].id,
created_at: new Date(),
status: 'sent'
});
@ -450,19 +518,21 @@ DataGenerator.forKnex = (function () {
];
roles_users = [
{user_id: 1, role_id: 4},
{user_id: 2, role_id: 1},
{user_id: 3, role_id: 2},
{user_id: 4, role_id: 3}
{id: ObjectId.generate(), user_id: DataGenerator.Content.users[0].id, role_id: DataGenerator.Content.roles[3].id},
{id: ObjectId.generate(), user_id: DataGenerator.Content.users[1].id, role_id: DataGenerator.Content.roles[0].id},
{id: ObjectId.generate(), user_id: DataGenerator.Content.users[2].id, role_id: DataGenerator.Content.roles[1].id},
{id: ObjectId.generate(), user_id: DataGenerator.Content.users[3].id, role_id: DataGenerator.Content.roles[2].id}
];
// this is not pretty, but the fastest
// it relies on the created posts/tags
posts_tags = [
{post_id: 1, tag_id: 1},
{post_id: 1, tag_id: 2},
{post_id: 2, tag_id: 1},
{post_id: 2, tag_id: 2},
{post_id: 3, tag_id: 3},
{post_id: 4, tag_id: 4}
{id: ObjectId.generate(), post_id: DataGenerator.Content.posts[0].id, tag_id: DataGenerator.Content.tags[0].id},
{id: ObjectId.generate(), post_id: DataGenerator.Content.posts[0].id, tag_id: DataGenerator.Content.tags[1].id},
{id: ObjectId.generate(), post_id: DataGenerator.Content.posts[1].id, tag_id: DataGenerator.Content.tags[0].id},
{id: ObjectId.generate(), post_id: DataGenerator.Content.posts[1].id, tag_id: DataGenerator.Content.tags[1].id},
{id: ObjectId.generate(), post_id: DataGenerator.Content.posts[2].id, tag_id: DataGenerator.Content.tags[2].id},
{id: ObjectId.generate(), post_id: DataGenerator.Content.posts[3].id, tag_id: DataGenerator.Content.tags[3].id}
];
apps = [
@ -477,8 +547,8 @@ DataGenerator.forKnex = (function () {
];
invites = [
createInvite({email: 'test1@ghost.org', role_id: 1}),
createInvite({email: 'test2@ghost.org', role_id: 3})
createInvite({email: 'test1@ghost.org', role_id: DataGenerator.Content.roles[0].id}),
createInvite({email: 'test2@ghost.org', role_id: DataGenerator.Content.roles[2].id})
];
return {
@ -512,6 +582,7 @@ DataGenerator.forKnex = (function () {
};
}());
// @TODO: this logic only exists because we are now using our models :/
DataGenerator.forModel = (function () {
var posts,
tags,
@ -532,8 +603,8 @@ DataGenerator.forModel = (function () {
}, user);
});
roles = _.map(DataGenerator.Content.roles, function (role, id) {
return _.extend({}, role, {id: id + 1});
roles = _.map(DataGenerator.Content.roles, function (role) {
return _.extend({}, role, {id: ObjectId.generate()});
});
return {

View File

@ -2,51 +2,14 @@
* These fixtures are just for testing the filter spec
*/
var _ = require('lodash'),
ObjectId = require('bson-objectid'),
db = require('../../../../server/data/db'),
data = {};
data.tags = [
{
name: 'Getting Started',
slug: 'getting-started',
created_by: 1
},
{
name: 'photo',
slug: 'photo',
image: 'some/image/path.jpg',
description: 'Photo posts',
created_by: 2
},
{
name: 'Video',
slug: 'video',
image: 'some/image/path.jpg',
description: 'Video posts',
created_by: 1
},
{
name: 'Audio',
slug: 'audio',
image: 'some/image/path.jpg',
description: 'Audio posts',
created_by: 1
},
{
name: 'No Posts',
slug: 'no-posts',
created_by: 2
},
{
name: 'Special',
slug: 'special',
created_by: 2
}
];
// Password = Sl1m3rson
data.users = [
{
id: ObjectId.generate(),
name: 'Leslie Jones',
slug: 'leslie',
email: 'ljones@nothere.com',
@ -54,6 +17,7 @@ data.users = [
website: 'http://twitter.com/ljonestestuser'
},
{
id: ObjectId.generate(),
name: 'Pat Smith',
slug: 'pat-smith',
email: 'pat-smith@nothere.com',
@ -61,6 +25,7 @@ data.users = [
website: 'http://github.com/patsmithtestuser'
},
{
id: ObjectId.generate(),
name: 'Cameron Howe',
slug: 'camhowe',
email: 'camhowe@c-e-is-real.com',
@ -68,180 +33,246 @@ data.users = [
}
];
data.tags = [
{
id: ObjectId.generate(),
name: 'Getting Started',
slug: 'getting-started',
created_by: data.users[0].id
},
{
id: ObjectId.generate(),
name: 'photo',
slug: 'photo',
image: 'some/image/path.jpg',
description: 'Photo posts',
created_by: data.users[1].id
},
{
id: ObjectId.generate(),
name: 'Video',
slug: 'video',
image: 'some/image/path.jpg',
description: 'Video posts',
created_by: data.users[0].id
},
{
id: ObjectId.generate(),
name: 'Audio',
slug: 'audio',
image: 'some/image/path.jpg',
description: 'Audio posts',
created_by: data.users[0].id
},
{
id: ObjectId.generate(),
name: 'No Posts',
slug: 'no-posts',
created_by: data.users[1].id
},
{
id: ObjectId.generate(),
name: 'Special',
slug: 'special',
created_by: data.users[1].id
}
];
data.posts = [
{
id: ObjectId.generate(),
title: 'First Post',
slug: 'first-post',
markdown: 'Hello World!',
featured: false,
author_id: 1,
tags: [1]
author_id: data.users[0].id,
tags: [data.tags[0].id]
},
{
id: ObjectId.generate(),
title: 'Second Post',
slug: 'second-post',
markdown: 'Hello World!',
featured: false,
author_id: 2,
tags: [2, 3, 4, 6]
author_id: data.users[1].id,
tags: [data.tags[1].id, data.tags[2].id, data.tags[3].id, data.tags[5].id]
},
{
id: ObjectId.generate(),
title: 'Third Post',
slug: 'third-post',
markdown: 'Hello World!',
featured: false,
author_id: 1,
tags: [2]
author_id: data.users[0].id,
tags: [data.tags[1].id]
},
{
id: ObjectId.generate(),
title: 'Fourth Post',
slug: 'fourth-post',
markdown: 'Hello World!',
featured: false,
author_id: 1,
tags: [3]
author_id: data.users[0].id,
tags: [data.tags[2].id]
},
{
id: ObjectId.generate(),
title: 'Fifth Post',
slug: 'fifth-post',
markdown: 'Hello World!',
featured: true,
author_id: 2,
tags: [6]
author_id: data.users[1].id,
tags: [data.tags[5].id]
},
{
id: ObjectId.generate(),
title: 'Sixth Post',
slug: 'sixth-post',
markdown: 'Hello World!',
featured: false,
author_id: 2,
author_id: data.users[1].id,
image: 'some/image/path.jpg',
tags: [1, 4, 6]
tags: [data.tags[0].id, data.tags[3].id, data.tags[5].id]
},
{
id: ObjectId.generate(),
title: 'Seventh Post',
slug: 'seventh-post',
markdown: 'Hello World!',
featured: false,
author_id: 1,
author_id: data.users[0].id,
image: 'some/image/path.jpg',
tags: [1, 3]
tags: [data.tags[0].id, data.tags[2].id]
},
{
id: ObjectId.generate(),
title: 'Eighth Post',
slug: 'eighth-post',
markdown: 'Hello World!',
featured: true,
author_id: 1,
tags: [1, 3, 4]
author_id: data.users[0].id,
tags: [data.tags[0].id, data.tags[2].id, data.tags[3].id]
},
{
id: ObjectId.generate(),
title: 'Ninth Post',
slug: 'ninth-post',
markdown: 'Hello World!',
featured: false,
author_id: 1,
tags: [2, 4]
author_id: data.users[0].id,
tags: [data.tags[1].id, data.tags[3].id]
},
{
id: ObjectId.generate(),
title: 'Tenth Post',
slug: 'tenth-post',
markdown: 'Hello World!',
featured: false,
author_id: 1,
tags: [3]
author_id: data.users[0].id,
tags: [data.tags[2].id]
},
{
id: ObjectId.generate(),
title: 'Eleventh Post',
slug: 'eleventh-post',
markdown: 'Hello World!',
featured: false,
author_id: 1,
author_id: data.users[0].id,
image: 'some/image/path.jpg',
tags: [2]
tags: [data.tags[1].id]
},
{
id: ObjectId.generate(),
title: 'Twelfth Post',
slug: 'twelfth-post',
markdown: 'Hello World!',
featured: false,
author_id: 1,
tags: [4]
author_id: data.users[0].id,
tags: [data.tags[3].id]
},
{
id: ObjectId.generate(),
title: 'Thirteenth Post',
slug: 'thirteenth-post',
markdown: 'Hello World!',
featured: false,
author_id: 1,
author_id: data.users[0].id,
tags: []
},
{
id: ObjectId.generate(),
title: 'Fourteenth Post',
slug: 'fourteenth-post',
markdown: 'Hello World!',
featured: true,
author_id: 1,
tags: [4]
author_id: data.users[0].id,
tags: [data.tags[3].id]
},
{
id: ObjectId.generate(),
title: 'Fifteenth Post',
slug: 'fifteenth-post',
markdown: 'Hello World! I am a featured page',
featured: true,
page: 1,
author_id: 1,
author_id: data.users[0].id,
tags: []
},
{
id: ObjectId.generate(),
title: 'Sixteenth Post',
slug: 'sixteenth-post',
markdown: 'Hello World!',
featured: false,
author_id: 1,
author_id: data.users[0].id,
tags: []
},
{
id: ObjectId.generate(),
title: 'Seventeenth Post',
slug: 'seventeenth-post',
markdown: 'Hello World!',
featured: false,
author_id: 1,
author_id: data.users[0].id,
tags: []
},
{
id: ObjectId.generate(),
title: 'Eighteenth Post',
slug: 'eighteenth-post',
markdown: 'Hello World!',
featured: false,
author_id: 1,
author_id: data.users[0].id,
tags: []
},
{
id: ObjectId.generate(),
title: 'Nineteenth Post',
slug: 'nineteenth-post',
markdown: 'Hello World!',
featured: false,
status: 'draft',
author_id: 1,
tags: [1, 2, 3, 4]
author_id: data.users[0].id,
tags: [data.tags[0].id, data.tags[1].id, data.tags[2].id, data.tags[3].id]
},
{
id: ObjectId.generate(),
title: 'Twentieth Post',
slug: 'twentieth-post',
markdown: 'Hello World!',
featured: false,
author_id: 1,
author_id: data.users[0].id,
tags: []
},
{
id: ObjectId.generate(),
title: 'About Page',
slug: 'about',
markdown: 'About Me!',
featured: false,
page: 1,
author_id: 1,
tags: [1, 2, 3, 4]
author_id: data.users[0].id,
tags: [data.tags[0].id, data.tags[1].id, data.tags[2].id, data.tags[3].id]
}
];
@ -274,38 +305,34 @@ function createUsers(knex, DataGenerator) {
return writeFetchFix(knex, 'users');
}
function createTags(knex, DataGenerator, created) {
function createTags(knex, DataGenerator) {
data.tags = _.map(data.tags, function (tag) {
tag = DataGenerator.forKnex.createBasic(tag);
tag.created_by = created.users[tag.created_by].id;
return tag;
return DataGenerator.forKnex.createBasic(tag);
});
// Next, insert it into the database & return the correctly indexed data
return writeFetchFix(knex, 'tags');
}
function createPosts(knex, DataGenerator, created) {
function createPosts(knex, DataGenerator) {
var postsTags = [];
data.posts = _.map(data.posts, function (post, index) {
data.posts = _.map(data.posts, function (post) {
post = DataGenerator.forKnex.createPost(post);
post.created_by = created.users[post.author_id].id;
post.author_id = created.users[post.author_id].id;
_.each(post.tags, function (tagId) {
postsTags.push({post_id: index + 1, tag_id: created.tags[tagId].id});
postsTags.push({
id: ObjectId.generate(),
post_id: post.id,
tag_id: tagId
});
});
delete post.tags;
return post;
});
// Next, insert it into the database & return the correctly indexed data
return writeFetchFix(knex, 'posts').then(function (createdPosts) {
// Handle post tags
postsTags = _.map(postsTags, function (postTag) {
postTag.post_id = createdPosts[postTag.post_id].id;
return postTag;
});
return knex('posts_tags').insert(postsTags).then(function () {
return createdPosts;
});
@ -315,16 +342,16 @@ function createPosts(knex, DataGenerator, created) {
module.exports = function (DataGenerator) {
var created = {};
// Create users first
return createUsers(db.knex, DataGenerator).then(function (createdUsers) {
created.users = createdUsers;
return createUsers(db.knex, DataGenerator).then(function () {
// Next create tags
return createTags(db.knex, DataGenerator, created);
}).then(function (createdTags) {
created.tags = createdTags;
return createTags(db.knex, DataGenerator);
}).then(function () {
// Finally, setup posts with the right authors and tags
return createPosts(db.knex, DataGenerator, created);
return createPosts(db.knex, DataGenerator);
}).then(function (createdPosts) {
created.posts = createdPosts;
return created;
});
};
module.exports.data = data;

View File

@ -4,9 +4,11 @@ var Promise = require('bluebird'),
path = require('path'),
Module = require('module'),
debug = require('debug')('ghost:test'),
ObjectId = require('bson-objectid'),
uuid = require('node-uuid'),
KnexMigrator = require('knex-migrator'),
ghost = require('../../server'),
errors = require('../../server/errors'),
db = require('../../server/data/db'),
fixtureUtils = require('../../server/data/migration/fixtures/utils'),
models = require('../../server/models'),
@ -31,6 +33,7 @@ var Promise = require('bluebird'),
teardown,
setup,
doAuth,
createUser,
login,
togglePermalinks,
startGhost,
@ -205,10 +208,11 @@ fixtures = {
user = DataGenerator.forKnex.createBasic(user);
user = _.extend({}, user, {status: 'inactive'});
return db.knex('users').insert(user)
.then(function () {
return db.knex('roles_users').insert(DataGenerator.forKnex.roles_users[0]);
});
return db.knex('roles').insert(DataGenerator.forKnex.roles).then(function () {
return db.knex('users').insert(user);
}).then(function () {
return db.knex('roles_users').insert(DataGenerator.forKnex.roles_users[0]);
});
},
insertOwnerUser: function insertOwnerUser() {
@ -230,7 +234,7 @@ fixtures = {
}
return db.knex('users')
.where('id', '=', '1')
.where('id', '=', models.User.ownerUser)
.update(user);
},
@ -257,16 +261,22 @@ fixtures = {
extraUsers = _.map(extraUsers, function (user) {
return DataGenerator.forKnex.createUser(_.extend({}, user, {
id: ObjectId.generate(),
email: 'a' + user.email,
slug: 'a' + user.slug
}));
});
// @TODO: remove when overhauling test env
// tests need access to the extra created users (especially to the created id)
// replacement for admin2, editor2 etc
DataGenerator.Content.extraUsers = extraUsers;
return db.knex('users').insert(extraUsers).then(function () {
return db.knex('roles_users').insert([
{user_id: 5, role_id: 1},
{user_id: 6, role_id: 2},
{user_id: 7, role_id: 3}
{id: ObjectId.generate(), user_id: extraUsers[0].id, role_id: DataGenerator.Content.roles[0].id},
{id: ObjectId.generate(), user_id: extraUsers[1].id, role_id: DataGenerator.Content.roles[1].id},
{id: ObjectId.generate(), user_id: extraUsers[2].id, role_id: DataGenerator.Content.roles[2].id}
]);
});
},
@ -274,30 +284,9 @@ fixtures = {
// Creates a client, and access and refresh tokens for user 3 (author)
createTokensForUser: function createTokensForUser() {
return db.knex('clients').insert(DataGenerator.forKnex.clients).then(function () {
return db.knex('accesstokens').insert(DataGenerator.forKnex.createToken({user_id: 3}));
return db.knex('accesstokens').insert(DataGenerator.forKnex.createToken({user_id: DataGenerator.Content.users[2].id}));
}).then(function () {
return db.knex('refreshtokens').insert(DataGenerator.forKnex.createToken({user_id: 3}));
});
},
createInvitedUsers: function createInvitedUser() {
// grab 3 more users
var extraUsers = DataGenerator.Content.users.slice(2, 5);
extraUsers = _.map(extraUsers, function (user) {
return DataGenerator.forKnex.createUser(_.extend({}, user, {
email: 'inv' + user.email,
slug: 'inv' + user.slug,
status: 'invited-pending'
}));
});
return db.knex('users').insert(extraUsers).then(function () {
return db.knex('roles_users').insert([
{user_id: 8, role_id: 1},
{user_id: 9, role_id: 2},
{user_id: 10, role_id: 3}
]);
return db.knex('refreshtokens').insert(DataGenerator.forKnex.createToken({user_id: DataGenerator.Content.users[2].id}));
});
},
@ -344,10 +333,10 @@ fixtures = {
actions = [],
permissionsRoles = [],
roles = {
Administrator: 1,
Editor: 2,
Author: 3,
Owner: 4
Administrator: DataGenerator.Content.roles[0].id,
Editor: DataGenerator.Content.roles[1].id,
Author: DataGenerator.Content.roles[2].id,
Owner: DataGenerator.Content.roles[3].id
};
// CASE: if empty db will throw SQLITE_MISUSE, hard to debug
@ -356,19 +345,29 @@ fixtures = {
}
permsToInsert = _.map(permsToInsert, function (perms) {
actions.push(perms.action_type);
perms.id = ObjectId.generate();
actions.push({type: perms.action_type, permissionId: perms.id});
return DataGenerator.forKnex.createBasic(perms);
});
_.each(permsRolesToInsert, function (perms, role) {
if (perms[obj]) {
if (perms[obj] === 'all') {
_.each(actions, function (action, i) {
permissionsRoles.push({permission_id: (i + 1), role_id: roles[role]});
_.each(actions, function (action) {
permissionsRoles.push({
id: ObjectId.generate(),
permission_id: action.permissionId,
role_id: roles[role]
});
});
} else {
_.each(perms[obj], function (action) {
permissionsRoles.push({permission_id: (_.indexOf(actions, action) + 1), role_id: roles[role]});
permissionsRoles.push({
id: ObjectId.generate(),
permission_id: _.find(actions, {type: action}).permissionId,
role_id: roles[role]
});
});
}
}
@ -498,7 +497,7 @@ getFixtureOps = function getFixtureOps(toDos) {
fixtureOps.push(toDoList[tmp[0]](tmp[1]));
} else {
if (!toDoList[toDo]) {
throw new Error('setup todo does not exist - spell mistake? --> ' + toDo);
throw new Error('setup todo does not exist - spell mistake?');
}
fixtureOps.push(toDoList[toDo]);
@ -542,9 +541,9 @@ setup = function setup() {
};
};
// ## Functions for Route Tests (!!)
/**
* ## DoAuth For Route Tests
*
* This function manages the work of ensuring we have an overridden owner user, and grabbing an access token
* @returns {deferred.promise<AccessToken>}
*/
@ -571,19 +570,48 @@ doAuth = function doAuth() {
});
};
createUser = function createUser(options) {
var user = options.user,
role = options.role;
return db.knex('users').insert(user)
.then(function () {
return db.knex('roles');
})
.then(function (roles) {
return db.knex('roles_users').insert({
id: ObjectId.generate(),
role_id: _.find(roles, {name: role.name}).id,
user_id: user.id
});
})
.then(function () {
return user;
});
};
login = function login(request) {
var user = DataGenerator.forModel.users[request.userIndex || 0];
// CASE: by default we use the owner to login
if (!request.user) {
request.user = DataGenerator.Content.users[0];
}
return new Promise(function (resolve, reject) {
request.post('/ghost/api/v0.1/authentication/token/')
.set('Origin', config.get('url'))
.send({
grant_type: 'password',
username: user.email,
password: user.password,
username: request.user.email,
password: 'Sl1m3rson',
client_id: 'ghost-admin',
client_secret: 'not_available'
}).then(function then(res) {
if (res.statusCode !== 200) {
return reject(new errors.GhostError({
message: res.body.errors[0].message
}));
}
resolve(res.body.access_token);
}, reject);
});
@ -661,8 +689,11 @@ unmockNotExistingModule = function unmockNotExistingModule() {
* 2. start ghost
*/
startGhost = function startGhost() {
return knexMigrator.init()
.then(function () {
return knexMigrator.reset()
.then(function initialiseDatabase() {
return knexMigrator.init();
})
.then(function startGhost() {
return ghost();
});
};
@ -672,6 +703,7 @@ module.exports = {
teardown: teardown,
setup: setup,
doAuth: doAuth,
createUser: createUser,
login: login,
togglePermalinks: togglePermalinks,
@ -688,6 +720,7 @@ module.exports = {
fixtures: fixtures,
DataGenerator: DataGenerator,
filterData: filterData,
API: API,
fork: fork,
@ -696,28 +729,25 @@ module.exports = {
context: {
internal: {context: {internal: true}},
external: {context: {external: true}},
owner: {context: {user: 1}},
admin: {context: {user: 2}},
editor: {context: {user: 3}},
author: {context: {user: 4}}
owner: {context: {user: DataGenerator.Content.users[0].id}},
admin: {context: {user: DataGenerator.Content.users[1].id}},
editor: {context: {user: DataGenerator.Content.users[2].id}},
author: {context: {user: DataGenerator.Content.users[3].id}}
},
users: {
ids: {
owner: 1,
admin: 2,
editor: 3,
author: 4,
admin2: 5,
editor2: 6,
author2: 7
owner: DataGenerator.Content.users[0].id,
admin: DataGenerator.Content.users[1].id,
editor: DataGenerator.Content.users[2].id,
author: DataGenerator.Content.users[3].id
}
},
roles: {
ids: {
owner: 4,
admin: 1,
editor: 2,
author: 3
owner: DataGenerator.Content.roles[3].id,
admin: DataGenerator.Content.roles[0].id,
editor: DataGenerator.Content.roles[1].id,
author: DataGenerator.Content.roles[2].id
}
},

View File

@ -30,9 +30,10 @@
"bcryptjs": "2.3.0",
"bluebird": "3.4.6",
"body-parser": "1.15.2",
"bookshelf": "0.10.2",
"brute-knex": "https://github.com/cobbspur/brute-knex/tarball/0cb28fa8e3230dcbf6bca8b991dbb340b9fff6cc",
"bookshelf": "https://github.com/kirrg001/bookshelf/tarball/feature/creating-event-attached-models",
"bunyan": "1.8.1",
"bson-objectid": "1.1.4",
"chalk": "1.1.3",
"cheerio": "0.22.0",
"commander": "2.9.0",