mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-21 01:41:46 +03:00
a96a74c5a1
refs #6301, #4176 - always check existence of items before attempting to create them, in order to prevent duplicates - provide stats on how many object creations are expected vs done - split out and improve fixture utils tests (100% covers utils)
617 lines
20 KiB
JavaScript
617 lines
20 KiB
JavaScript
var Promise = require('bluebird'),
|
|
_ = require('lodash'),
|
|
fs = require('fs-extra'),
|
|
path = require('path'),
|
|
uuid = require('node-uuid'),
|
|
db = require('../../server/data/db'),
|
|
migration = require('../../server/data/migration/'),
|
|
fixtureUtils = require('../../server/data/migration/fixtures/utils'),
|
|
Models = require('../../server/models'),
|
|
SettingsAPI = require('../../server/api/settings'),
|
|
permissions = require('../../server/permissions'),
|
|
sequence = require('../../server/utils/sequence'),
|
|
DataGenerator = require('./fixtures/data-generator'),
|
|
filterData = require('./fixtures/filter-param'),
|
|
API = require('./api'),
|
|
fork = require('./fork'),
|
|
config = require('../../server/config'),
|
|
|
|
fixtures,
|
|
getFixtureOps,
|
|
toDoList,
|
|
postsInserted = 0,
|
|
|
|
teardown,
|
|
setup,
|
|
doAuth,
|
|
login,
|
|
togglePermalinks,
|
|
|
|
initFixtures,
|
|
initData,
|
|
clearData;
|
|
|
|
/** TEST FIXTURES **/
|
|
fixtures = {
|
|
insertPosts: function insertPosts() {
|
|
return Promise.resolve(db.knex('posts').insert(DataGenerator.forKnex.posts)).then(function () {
|
|
return db.knex('tags').insert(DataGenerator.forKnex.tags);
|
|
}).then(function () {
|
|
return db.knex('posts_tags').insert(DataGenerator.forKnex.posts_tags);
|
|
});
|
|
},
|
|
|
|
insertMultiAuthorPosts: function insertMultiAuthorPosts(max) {
|
|
/*jshint unused:false*/
|
|
var author,
|
|
authors,
|
|
i, j, k = postsInserted,
|
|
posts = [];
|
|
|
|
max = max || 50;
|
|
// insert users of different roles
|
|
return Promise.resolve(fixtures.createUsersWithRoles()).then(function () {
|
|
// create the tags
|
|
return db.knex('tags').insert(DataGenerator.forKnex.tags);
|
|
}).then(function () {
|
|
return db.knex('users').select('id');
|
|
}).then(function (results) {
|
|
authors = _.pluck(results, 'id');
|
|
|
|
// Let's insert posts with random authors
|
|
for (i = 0; i < max; i += 1) {
|
|
author = authors[i % authors.length];
|
|
posts.push(DataGenerator.forKnex.createGenericPost(k, null, null, author));
|
|
k = k + 1;
|
|
}
|
|
|
|
// Keep track so we can run this function again safely
|
|
postsInserted = k;
|
|
|
|
return sequence(_.times(posts.length, function (index) {
|
|
return function () {
|
|
return db.knex('posts').insert(posts[index]);
|
|
};
|
|
}));
|
|
}).then(function () {
|
|
return Promise.all([
|
|
// PostgreSQL can return results in any order
|
|
db.knex('posts').orderBy('id', 'asc').select('id'),
|
|
db.knex('tags').select('id')
|
|
]);
|
|
}).then(function (results) {
|
|
var posts = _.pluck(results[0], 'id'),
|
|
tags = _.pluck(results[1], 'id'),
|
|
promises = [],
|
|
i;
|
|
|
|
if (max > posts.length) {
|
|
throw new Error('Trying to add more posts_tags than the number of posts. ' + max + ' ' + posts.length);
|
|
}
|
|
|
|
for (i = 0; i < max; i += 1) {
|
|
promises.push(DataGenerator.forKnex.createPostsTags(posts[i], tags[i % tags.length]));
|
|
}
|
|
|
|
return sequence(_.times(promises.length, function (index) {
|
|
return function () {
|
|
return db.knex('posts_tags').insert(promises[index]);
|
|
};
|
|
}));
|
|
});
|
|
},
|
|
|
|
insertMorePosts: function insertMorePosts(max) {
|
|
var lang,
|
|
status,
|
|
posts = [],
|
|
i, j, k = postsInserted;
|
|
|
|
max = max || 50;
|
|
|
|
for (i = 0; i < 2; i += 1) {
|
|
lang = i % 2 ? 'en' : 'fr';
|
|
posts.push(DataGenerator.forKnex.createGenericPost(k, null, lang));
|
|
k = k + 1;
|
|
|
|
for (j = 0; j < max; j += 1) {
|
|
status = j % 2 ? 'draft' : 'published';
|
|
posts.push(DataGenerator.forKnex.createGenericPost(k, status, lang));
|
|
k = k + 1;
|
|
}
|
|
}
|
|
|
|
// Keep track so we can run this function again safely
|
|
postsInserted = k;
|
|
|
|
return sequence(_.times(posts.length, function (index) {
|
|
return function () {
|
|
return db.knex('posts').insert(posts[index]);
|
|
};
|
|
}));
|
|
},
|
|
|
|
insertMoreTags: function insertMoreTags(max) {
|
|
max = max || 50;
|
|
var tags = [],
|
|
tagName,
|
|
i;
|
|
|
|
for (i = 0; i < max; i += 1) {
|
|
tagName = uuid.v4().split('-')[0];
|
|
tags.push(DataGenerator.forKnex.createBasic({name: tagName, slug: tagName}));
|
|
}
|
|
|
|
return sequence(_.times(tags.length, function (index) {
|
|
return function () {
|
|
return db.knex('tags').insert(tags[index]);
|
|
};
|
|
}));
|
|
},
|
|
|
|
insertMorePostsTags: function insertMorePostsTags(max) {
|
|
max = max || 50;
|
|
|
|
return Promise.all([
|
|
// PostgreSQL can return results in any order
|
|
db.knex('posts').orderBy('id', 'asc').select('id'),
|
|
db.knex('tags').select('id', 'name')
|
|
]).then(function (results) {
|
|
var posts = _.pluck(results[0], 'id'),
|
|
injectionTagId = _.chain(results[1])
|
|
.where({name: 'injection'})
|
|
.pluck('id')
|
|
.value()[0],
|
|
promises = [],
|
|
i;
|
|
|
|
if (max > posts.length) {
|
|
throw new Error('Trying to add more posts_tags than the number of posts.');
|
|
}
|
|
|
|
for (i = 0; i < max; i += 1) {
|
|
promises.push(DataGenerator.forKnex.createPostsTags(posts[i], injectionTagId));
|
|
}
|
|
|
|
return sequence(_.times(promises.length, function (index) {
|
|
return function () {
|
|
return db.knex('posts_tags').insert(promises[index]);
|
|
};
|
|
}));
|
|
});
|
|
},
|
|
insertRoles: function insertRoles() {
|
|
return db.knex('roles').insert(DataGenerator.forKnex.roles);
|
|
},
|
|
|
|
initOwnerUser: function initOwnerUser() {
|
|
var user = DataGenerator.Content.users[0];
|
|
|
|
user = DataGenerator.forKnex.createBasic(user);
|
|
user = _.extend({}, user, {status: 'inactive'});
|
|
|
|
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() {
|
|
var user;
|
|
|
|
user = DataGenerator.forKnex.createUser(DataGenerator.Content.users[0]);
|
|
|
|
return db.knex('users').insert(user).then(function () {
|
|
return db.knex('roles_users').insert(DataGenerator.forKnex.roles_users[0]);
|
|
});
|
|
},
|
|
|
|
overrideOwnerUser: function overrideOwnerUser(slug) {
|
|
var user;
|
|
|
|
user = DataGenerator.forKnex.createUser(DataGenerator.Content.users[0]);
|
|
if (slug) {
|
|
user.slug = slug;
|
|
}
|
|
|
|
return db.knex('users')
|
|
.where('id', '=', '1')
|
|
.update(user);
|
|
},
|
|
|
|
createUsersWithRoles: function createUsersWithRoles() {
|
|
return db.knex('roles').insert(DataGenerator.forKnex.roles).then(function () {
|
|
return db.knex('users').insert(DataGenerator.forKnex.users);
|
|
}).then(function () {
|
|
return db.knex('roles_users').insert(DataGenerator.forKnex.roles_users);
|
|
});
|
|
},
|
|
|
|
createExtraUsers: function createExtraUsers() {
|
|
// grab 3 more users
|
|
var extraUsers = DataGenerator.Content.users.slice(2, 5);
|
|
|
|
extraUsers = _.map(extraUsers, function (user) {
|
|
return DataGenerator.forKnex.createUser(_.extend({}, user, {
|
|
email: 'a' + user.email,
|
|
slug: 'a' + user.slug
|
|
}));
|
|
});
|
|
|
|
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}
|
|
]);
|
|
});
|
|
},
|
|
|
|
// 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}));
|
|
}).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}
|
|
]);
|
|
});
|
|
},
|
|
|
|
insertOne: function insertOne(obj, fn) {
|
|
return db.knex(obj)
|
|
.insert(DataGenerator.forKnex[fn](DataGenerator.Content[obj][0]));
|
|
},
|
|
|
|
insertApps: function insertApps() {
|
|
return db.knex('apps').insert(DataGenerator.forKnex.apps).then(function () {
|
|
return db.knex('app_fields').insert(DataGenerator.forKnex.app_fields);
|
|
});
|
|
},
|
|
|
|
getImportFixturePath: function (filename) {
|
|
return path.resolve(__dirname + '/fixtures/import/' + filename);
|
|
},
|
|
|
|
getExportFixturePath: function (filename) {
|
|
return path.resolve(__dirname + '/fixtures/export/' + filename + '.json');
|
|
},
|
|
|
|
loadExportFixture: function loadExportFixture(filename) {
|
|
var filePath = this.getExportFixturePath(filename),
|
|
readFile = Promise.promisify(fs.readFile);
|
|
|
|
return readFile(filePath).then(function (fileContents) {
|
|
var data;
|
|
|
|
// Parse the json data
|
|
try {
|
|
data = JSON.parse(fileContents);
|
|
} catch (e) {
|
|
return new Error('Failed to parse the file');
|
|
}
|
|
|
|
return data;
|
|
});
|
|
},
|
|
|
|
permissionsFor: function permissionsFor(obj) {
|
|
var permsToInsert = fixtureUtils.findModelFixtures('Permission', {object_type: obj}).entries,
|
|
permsRolesToInsert = fixtureUtils.findPermissionRelationsForObject(obj).entries,
|
|
actions = [],
|
|
permissionsRoles = [],
|
|
roles = {
|
|
Administrator: 1,
|
|
Editor: 2,
|
|
Author: 3,
|
|
Owner: 4
|
|
};
|
|
|
|
permsToInsert = _.map(permsToInsert, function (perms) {
|
|
actions.push(perms.action_type);
|
|
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]});
|
|
});
|
|
} else {
|
|
_.each(perms[obj], function (action) {
|
|
permissionsRoles.push({permission_id: (_.indexOf(actions, action) + 1), role_id: roles[role]});
|
|
});
|
|
}
|
|
}
|
|
});
|
|
|
|
return db.knex('permissions').insert(permsToInsert).then(function () {
|
|
return db.knex('permissions_roles').insert(permissionsRoles);
|
|
});
|
|
},
|
|
insertClients: function insertClients() {
|
|
return db.knex('clients').insert(DataGenerator.forKnex.clients);
|
|
},
|
|
insertAccessToken: function insertAccessToken(override) {
|
|
return db.knex('accesstokens').insert(DataGenerator.forKnex.createToken(override));
|
|
}
|
|
};
|
|
|
|
/** Test Utility Functions **/
|
|
initData = function initData() {
|
|
return migration.init();
|
|
};
|
|
|
|
clearData = function clearData() {
|
|
// we must always try to delete all tables
|
|
return migration.reset();
|
|
};
|
|
|
|
toDoList = {
|
|
app: function insertApp() { return fixtures.insertOne('apps', 'createApp'); },
|
|
app_field: function insertAppField() {
|
|
// TODO: use the actual app ID to create the field
|
|
return fixtures.insertOne('apps', 'createApp').then(function () {
|
|
return fixtures.insertOne('app_fields', 'createAppField');
|
|
});
|
|
},
|
|
app_setting: function insertAppSetting() {
|
|
// TODO: use the actual app ID to create the field
|
|
return fixtures.insertOne('apps', 'createApp').then(function () {
|
|
return fixtures.insertOne('app_settings', 'createAppSetting');
|
|
});
|
|
},
|
|
permission: function insertPermission() { return fixtures.insertOne('permissions', 'createPermission'); },
|
|
role: function insertRole() { return fixtures.insertOne('roles', 'createRole'); },
|
|
roles: function insertRoles() { return fixtures.insertRoles(); },
|
|
tag: function insertTag() { return fixtures.insertOne('tags', 'createTag'); },
|
|
|
|
posts: function insertPosts() { return fixtures.insertPosts(); },
|
|
'posts:mu': function insertMultiAuthorPosts() { return fixtures.insertMultiAuthorPosts(); },
|
|
tags: function insertMoreTags() { return fixtures.insertMoreTags(); },
|
|
apps: function insertApps() { return fixtures.insertApps(); },
|
|
settings: function populateSettings() {
|
|
return Models.Settings.populateDefaults().then(function () { return SettingsAPI.updateSettingsCache(); });
|
|
},
|
|
'users:roles': function createUsersWithRoles() { return fixtures.createUsersWithRoles(); },
|
|
users: function createExtraUsers() { return fixtures.createExtraUsers(); },
|
|
'user:token': function createTokensForUser() { return fixtures.createTokensForUser(); },
|
|
owner: function insertOwnerUser() { return fixtures.insertOwnerUser(); },
|
|
'owner:pre': function initOwnerUser() { return fixtures.initOwnerUser(); },
|
|
'owner:post': function overrideOwnerUser() { return fixtures.overrideOwnerUser(); },
|
|
'perms:init': function initPermissions() { return permissions.init(); },
|
|
perms: function permissionsFor(obj) {
|
|
return function permissionsForObj() { return fixtures.permissionsFor(obj); };
|
|
},
|
|
clients: function insertClients() { return fixtures.insertClients(); },
|
|
filter: function createFilterParamFixtures() { return filterData(DataGenerator); }
|
|
};
|
|
|
|
/**
|
|
* ## getFixtureOps
|
|
*
|
|
* Takes the arguments from a setup function and turns them into an array of promises to fullfil
|
|
*
|
|
* This is effectively a list of instructions with regard to which fixtures should be setup for this test.
|
|
* * `default` - a special option which will cause the full suite of normal fixtures to be initialised
|
|
* * `perms:init` - initialise the permissions object after having added permissions
|
|
* * `perms:obj` - initialise permissions for a particular object type
|
|
* * `users:roles` - create a full suite of users, one per role
|
|
* @param {Object} toDos
|
|
*/
|
|
getFixtureOps = function getFixtureOps(toDos) {
|
|
// default = default fixtures, if it isn't present, init with tables only
|
|
var tablesOnly = !toDos.default,
|
|
fixtureOps = [];
|
|
|
|
// Database initialisation
|
|
if (toDos.init || toDos.default) {
|
|
fixtureOps.push(function initDB() {
|
|
return migration.init(tablesOnly);
|
|
});
|
|
delete toDos.default;
|
|
delete toDos.init;
|
|
}
|
|
|
|
// Go through our list of things to do, and add them to an array
|
|
_.each(toDos, function (value, toDo) {
|
|
var tmp;
|
|
if (toDo !== 'perms:init' && toDo.indexOf('perms:') !== -1) {
|
|
tmp = toDo.split(':');
|
|
fixtureOps.push(toDoList[tmp[0]](tmp[1]));
|
|
} else {
|
|
fixtureOps.push(toDoList[toDo]);
|
|
}
|
|
});
|
|
|
|
return fixtureOps;
|
|
};
|
|
|
|
// ## Test Setup and Teardown
|
|
|
|
initFixtures = function initFixtures() {
|
|
var options = _.merge({init: true}, _.transform(arguments, function (result, val) {
|
|
result[val] = true;
|
|
})),
|
|
fixtureOps = getFixtureOps(options);
|
|
|
|
return sequence(fixtureOps);
|
|
};
|
|
|
|
/**
|
|
* ## Setup Integration Tests
|
|
* Setup takes a list of arguments like: 'default', 'tag', 'perms:tag', 'perms:init'
|
|
* Setup does 'init' (DB) by default
|
|
* @returns {Function}
|
|
*/
|
|
setup = function setup() {
|
|
var self = this,
|
|
args = arguments;
|
|
|
|
return function (done) {
|
|
Models.init();
|
|
|
|
return initFixtures.apply(self, args).then(function () {
|
|
done();
|
|
}).catch(done);
|
|
};
|
|
};
|
|
|
|
/**
|
|
* ## 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>}
|
|
*/
|
|
// TODO make this do the DB init as well
|
|
doAuth = function doAuth() {
|
|
var options = arguments,
|
|
request = arguments[0],
|
|
fixtureOps;
|
|
|
|
// Remove request from this list
|
|
delete options[0];
|
|
// No DB setup, but override the owner
|
|
options = _.merge({'owner:post': true}, _.transform(options, function (result, val) {
|
|
if (val) {
|
|
result[val] = true;
|
|
}
|
|
}));
|
|
|
|
fixtureOps = getFixtureOps(options);
|
|
|
|
return sequence(fixtureOps).then(function () {
|
|
return login(request);
|
|
});
|
|
};
|
|
|
|
login = function login(request) {
|
|
var user = DataGenerator.forModel.users[0];
|
|
|
|
return new Promise(function (resolve, reject) {
|
|
request.post('/ghost/api/v0.1/authentication/token/')
|
|
.set('Origin', config.url)
|
|
.send({
|
|
grant_type: 'password',
|
|
username: user.email,
|
|
password: user.password,
|
|
client_id: 'ghost-admin',
|
|
client_secret: 'not_available'
|
|
}).then(function then(res) {
|
|
resolve(res.body.access_token);
|
|
}, reject);
|
|
});
|
|
};
|
|
|
|
togglePermalinks = function togglePermalinks(request, toggle) {
|
|
var permalinkString = toggle === 'date' ? '/:year/:month/:day/:slug/' : '/:slug/';
|
|
|
|
return new Promise(function (resolve, reject) {
|
|
doAuth(request).then(function (token) {
|
|
request.put('/ghost/api/v0.1/settings/')
|
|
.set('Authorization', 'Bearer ' + token)
|
|
.send({settings: [
|
|
{
|
|
uuid: '75e994ae-490e-45e6-9207-0eab409c1c04',
|
|
key: 'permalinks',
|
|
value: permalinkString,
|
|
type: 'blog',
|
|
created_at: '2014-10-16T17:39:16.005Z',
|
|
created_by: 1,
|
|
updated_at: '2014-10-20T19:44:18.077Z',
|
|
updated_by: 1
|
|
}
|
|
]})
|
|
.end(function (err, res) {
|
|
if (err) {
|
|
return reject(err);
|
|
}
|
|
|
|
resolve(res.body);
|
|
});
|
|
});
|
|
});
|
|
};
|
|
|
|
teardown = function teardown(done) {
|
|
migration.reset().then(function () {
|
|
done();
|
|
}).catch(done);
|
|
};
|
|
|
|
module.exports = {
|
|
teardown: teardown,
|
|
setup: setup,
|
|
doAuth: doAuth,
|
|
login: login,
|
|
togglePermalinks: togglePermalinks,
|
|
|
|
initFixtures: initFixtures,
|
|
initData: initData,
|
|
clearData: clearData,
|
|
|
|
fixtures: fixtures,
|
|
|
|
DataGenerator: DataGenerator,
|
|
API: API,
|
|
|
|
fork: fork,
|
|
|
|
// Helpers to make it easier to write tests which are easy to read
|
|
context: {
|
|
internal: {context: {internal: true}},
|
|
owner: {context: {user: 1}},
|
|
admin: {context: {user: 2}},
|
|
editor: {context: {user: 3}},
|
|
author: {context: {user: 4}}
|
|
},
|
|
users: {
|
|
ids: {
|
|
owner: 1,
|
|
admin: 2,
|
|
editor: 3,
|
|
author: 4,
|
|
admin2: 5,
|
|
editor2: 6,
|
|
author2: 7
|
|
}
|
|
},
|
|
roles: {
|
|
ids: {
|
|
owner: 4,
|
|
admin: 1,
|
|
editor: 2,
|
|
author: 3
|
|
}
|
|
},
|
|
|
|
cacheRules: {
|
|
public: 'public, max-age=0',
|
|
hour: 'public, max-age=' + 3600,
|
|
day: 'public, max-age=' + 86400,
|
|
year: 'public, max-age=' + 31536000,
|
|
private: 'no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0'
|
|
}
|
|
};
|