0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-03-11 02:12:21 -05:00

😱 🚀 🎨 tests: use truncate instead of database deletion (#8119)

* 😱  🚀  🎨  tests: use truncate instead of delete the database

refs #7718, refs #7470

- should bring massive speed improvement
- could also fix random test failures (e.g. sqlite database is busy)

* gruntfile: add knex-migrator command in test-setup
This commit is contained in:
Katharina Irrgang 2017-03-09 20:38:20 +01:00 committed by Hannah Wolfe
parent feaa25dad2
commit f8c51ac7e5
4 changed files with 205 additions and 95 deletions

View file

@ -8,9 +8,15 @@
// jshint unused: false // jshint unused: false
var overrides = require('./core/server/overrides'), var overrides = require('./core/server/overrides'),
config = require('./core/server/config'),
_ = require('lodash'), _ = require('lodash'),
chalk = require('chalk'), chalk = require('chalk'),
fs = require('fs-extra'), fs = require('fs-extra'),
KnexMigrator = require('knex-migrator'),
knexMigrator = new KnexMigrator({
knexMigratorFilePath: config.get('paths:appRoot')
}),
path = require('path'), path = require('path'),
escapeChar = process.platform.match(/^win/) ? '^' : '\\', escapeChar = process.platform.match(/^win/) ? '^' : '\\',
@ -446,6 +452,13 @@ var overrides = require('./core/server/overrides'),
}); });
}); });
/**
* Ensures the target database get's automatically created.
*/
grunt.registerTask('knex-migrator', function () {
return knexMigrator.init({noScripts: true});
});
// ### Validate // ### Validate
// **Main testing task** // **Main testing task**
// //
@ -499,7 +512,7 @@ var overrides = require('./core/server/overrides'),
// ### test-setup *(utility)( // ### test-setup *(utility)(
// `grunt test-setup` will run all the setup tasks required for running tests // `grunt test-setup` will run all the setup tasks required for running tests
grunt.registerTask('test-setup', 'Setup ready to run tests', grunt.registerTask('test-setup', 'Setup ready to run tests',
['clean:test', 'setTestEnv'] ['knex-migrator', 'clean:test', 'setTestEnv']
); );
// ### Unit Tests *(sub task)* // ### Unit Tests *(sub task)*

View file

@ -35,7 +35,9 @@ describe('Frontend Routing', function () {
} }
function addPosts(done) { function addPosts(done) {
testUtils.initData().then(function () { testUtils.clearData().then(function () {
return testUtils.initData();
}).then(function () {
return testUtils.fixtures.insertPostsAndTags(); return testUtils.fixtures.insertPostsAndTags();
}).then(function () { }).then(function () {
done(); done();
@ -493,10 +495,10 @@ describe('Frontend Routing', function () {
}); });
describe('Site Map', function () { describe('Site Map', function () {
before(testUtils.teardown);
before(function (done) { before(function (done) {
testUtils.initData().then(function () { testUtils.clearData().then(function () {
return testUtils.initData();
}).then(function () {
return testUtils.fixtures.insertPostsAndTags(); return testUtils.fixtures.insertPostsAndTags();
}).then(function () { }).then(function () {
done(); done();

View file

@ -49,6 +49,9 @@ function forkGhost(newConfig) {
.then(function (_port) { .then(function (_port) {
port = _port; port = _port;
return knexMigrator.reset();
})
.then(function () {
return knexMigrator.init(); return knexMigrator.init();
}) })
.then(function () { .then(function () {

View file

@ -1,28 +1,31 @@
var Promise = require('bluebird'), /*jshint expr:true*/
_ = require('lodash'), var Promise = require('bluebird'),
fs = require('fs-extra'), _ = require('lodash'),
path = require('path'), fs = require('fs-extra'),
Module = require('module'), path = require('path'),
debug = require('debug')('ghost:test'), Module = require('module'),
ObjectId = require('bson-objectid'), debug = require('debug')('ghost:test'),
uuid = require('uuid'), ObjectId = require('bson-objectid'),
KnexMigrator = require('knex-migrator'), uuid = require('uuid'),
ghost = require('../../server'), KnexMigrator = require('knex-migrator'),
errors = require('../../server/errors'), ghost = require('../../server'),
db = require('../../server/data/db'), errors = require('../../server/errors'),
fixtureUtils = require('../../server/data/schema/fixtures/utils'), db = require('../../server/data/db'),
models = require('../../server/models'), fixtureUtils = require('../../server/data/schema/fixtures/utils'),
SettingsLib = require('../../server/settings'), schema = require('../../server/data/schema').tables,
permissions = require('../../server/permissions'), schemaTables = Object.keys(schema),
sequence = require('../../server/utils/sequence'), models = require('../../server/models'),
themes = require('../../server/themes'), SettingsLib = require('../../server/settings'),
permissions = require('../../server/permissions'),
sequence = require('../../server/utils/sequence'),
themes = require('../../server/themes'),
DataGenerator = require('./fixtures/data-generator'), DataGenerator = require('./fixtures/data-generator'),
filterData = require('./fixtures/filter-param'), filterData = require('./fixtures/filter-param'),
API = require('./api'), API = require('./api'),
fork = require('./fork'), fork = require('./fork'),
mocks = require('./mocks'), mocks = require('./mocks'),
config = require('../../server/config'), config = require('../../server/config'),
knexMigrator = new KnexMigrator(), knexMigrator = new KnexMigrator(),
fixtures, fixtures,
getFixtureOps, getFixtureOps,
toDoList, toDoList,
@ -293,7 +296,7 @@ fixtures = {
insertOne: function insertOne(obj, fn, index) { insertOne: function insertOne(obj, fn, index) {
return db.knex(obj) return db.knex(obj)
.insert(DataGenerator.forKnex[fn](DataGenerator.Content[obj][index || 0])); .insert(DataGenerator.forKnex[fn](DataGenerator.Content[obj][index || 0]));
}, },
insertApps: function insertApps() { insertApps: function insertApps() {
@ -423,7 +426,9 @@ clearData = function clearData() {
}; };
toDoList = { toDoList = {
app: function insertApp() { return fixtures.insertOne('apps', 'createApp'); }, app: function insertApp() {
return fixtures.insertOne('apps', 'createApp');
},
app_field: function insertAppField() { app_field: function insertAppField() {
// TODO: use the actual app ID to create the field // TODO: use the actual app ID to create the field
return fixtures.insertOne('apps', 'createApp').then(function () { return fixtures.insertOne('apps', 'createApp').then(function () {
@ -436,32 +441,80 @@ toDoList = {
return fixtures.insertOne('app_settings', 'createAppSetting'); return fixtures.insertOne('app_settings', 'createAppSetting');
}); });
}, },
permission: function insertPermission() { return fixtures.insertOne('permissions', 'createPermission'); }, permission: function insertPermission() {
role: function insertRole() { return fixtures.insertOne('roles', 'createRole'); }, return fixtures.insertOne('permissions', 'createPermission');
roles: function insertRoles() { return fixtures.insertRoles(); },
tag: function insertTag() { return fixtures.insertOne('tags', 'createTag'); },
subscriber: function insertSubscriber() { return fixtures.insertOne('subscribers', 'createSubscriber'); },
posts: function insertPostsAndTags() { return fixtures.insertPostsAndTags(); },
'posts:mu': function insertMultiAuthorPosts() { return fixtures.insertMultiAuthorPosts(); },
tags: function insertMoreTags() { return fixtures.insertMoreTags(); },
apps: function insertApps() { return fixtures.insertApps(); },
settings: function populateSettings() { return SettingsLib.init(); },
'users:roles': function createUsersWithRoles() { return fixtures.createUsersWithRoles(); },
'users:no-owner': function createUsersWithoutOwner() { return fixtures.createUsersWithoutOwner(); },
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(); }, role: function insertRole() {
'client:trusted-domain': function insertClients() { return fixtures.insertClientWithTrustedDomain(); }, return fixtures.insertOne('roles', 'createRole');
filter: function createFilterParamFixtures() { return filterData(DataGenerator); }, },
invites: function insertInvites() { return fixtures.insertInvites(); }, roles: function insertRoles() {
themes: function loadThemes() { return themes.loadAll(); } return fixtures.insertRoles();
},
tag: function insertTag() {
return fixtures.insertOne('tags', 'createTag');
},
subscriber: function insertSubscriber() {
return fixtures.insertOne('subscribers', 'createSubscriber');
},
posts: function insertPostsAndTags() {
return fixtures.insertPostsAndTags();
},
'posts:mu': function insertMultiAuthorPosts() {
return fixtures.insertMultiAuthorPosts();
},
tags: function insertMoreTags() {
return fixtures.insertMoreTags();
},
apps: function insertApps() {
return fixtures.insertApps();
},
settings: function populateSettings() {
return SettingsLib.init();
},
'users:roles': function createUsersWithRoles() {
return fixtures.createUsersWithRoles();
},
'users:no-owner': function createUsersWithoutOwner() {
return fixtures.createUsersWithoutOwner();
},
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();
},
'client:trusted-domain': function insertClients() {
return fixtures.insertClientWithTrustedDomain();
},
filter: function createFilterParamFixtures() {
return filterData(DataGenerator);
},
invites: function insertInvites() {
return fixtures.insertInvites();
},
themes: function loadThemes() {
return themes.loadAll();
}
}; };
/** /**
@ -470,10 +523,10 @@ toDoList = {
* Takes the arguments from a setup function and turns them into an array of promises to fullfil * 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. * 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 * * `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:init` - initialise the permissions object after having added permissions
* * `perms:obj` - initialise permissions for a particular object type * * `perms:obj` - initialise permissions for a particular object type
* * `users:roles` - create a full suite of users, one per role * * `users:roles` - create a full suite of users, one per role
* @param {Object} toDos * @param {Object} toDos
*/ */
getFixtureOps = function getFixtureOps(toDos) { getFixtureOps = function getFixtureOps(toDos) {
@ -614,14 +667,14 @@ login = function login(request) {
client_id: 'ghost-admin', client_id: 'ghost-admin',
client_secret: 'not_available' client_secret: 'not_available'
}).then(function then(res) { }).then(function then(res) {
if (res.statusCode !== 200) { if (res.statusCode !== 200) {
return reject(new errors.GhostError({ return reject(new errors.GhostError({
message: res.body.errors[0].message message: res.body.errors[0].message
})); }));
} }
resolve(res.body.access_token); resolve(res.body.access_token);
}, reject); }, reject);
}); });
}; };
@ -632,18 +685,20 @@ togglePermalinks = function togglePermalinks(request, toggle) {
doAuth(request).then(function (token) { doAuth(request).then(function (token) {
request.put('/ghost/api/v0.1/settings/') request.put('/ghost/api/v0.1/settings/')
.set('Authorization', 'Bearer ' + token) .set('Authorization', 'Bearer ' + token)
.send({settings: [ .send({
{ settings: [
uuid: '75e994ae-490e-45e6-9207-0eab409c1c04', {
key: 'permalinks', uuid: '75e994ae-490e-45e6-9207-0eab409c1c04',
value: permalinkString, key: 'permalinks',
type: 'blog', value: permalinkString,
created_at: '2014-10-16T17:39:16.005Z', type: 'blog',
created_by: 1, created_at: '2014-10-16T17:39:16.005Z',
updated_at: '2014-10-20T19:44:18.077Z', created_by: 1,
updated_by: 1 updated_at: '2014-10-20T19:44:18.077Z',
} updated_by: 1
]}) }
]
})
.end(function (err, res) { .end(function (err, res) {
if (err) { if (err) {
return reject(err); return reject(err);
@ -659,18 +714,55 @@ togglePermalinks = function togglePermalinks(request, toggle) {
}); });
}; };
/**
* Has to run in a transaction for MySQL, otherwise the foreign key check does not work.
* Sqlite3 has no truncate command.
*/
teardown = function teardown(done) { teardown = function teardown(done) {
debug('Database reset'); debug('Database teardown');
var tables = schemaTables.concat(['migrations']);
if (done) { if (config.get('database:client') === 'sqlite3') {
knexMigrator.reset() return Promise
.then(function () { .mapSeries(tables, function createTable(table) {
done(); return db.knex.raw('DELETE FROM ' + table + ';');
}) })
.catch(done); .then(function () {
} else { done && done();
return knexMigrator.reset(); })
.catch(function (err) {
// CASE: table does not exist
if (err.errno === 1) {
return done && done();
}
done && done(err);
});
} }
return db.knex.transaction(function (trx) {
return db.knex.raw('SET FOREIGN_KEY_CHECKS=0;').transacting(trx)
.then(function () {
return Promise
.each(tables, function createTable(table) {
return db.knex.raw('TRUNCATE ' + table + ';').transacting(trx);
});
})
.then(function () {
return db.knex.raw('SET FOREIGN_KEY_CHECKS=1;').transacting(trx);
})
.then(function () {
done && done();
})
.catch(function (err) {
// CASE: table does not exist
if (err.errno === 1146) {
return done && done();
}
return done ? done(err) : Promise.reject(err);
});
});
}; };
/** /**
@ -735,12 +827,12 @@ module.exports = {
// Helpers to make it easier to write tests which are easy to read // Helpers to make it easier to write tests which are easy to read
context: { context: {
internal: {context: {internal: true}}, internal: {context: {internal: true}},
external: {context: {external: true}}, external: {context: {external: true}},
owner: {context: {user: DataGenerator.Content.users[0].id}}, owner: {context: {user: DataGenerator.Content.users[0].id}},
admin: {context: {user: DataGenerator.Content.users[1].id}}, admin: {context: {user: DataGenerator.Content.users[1].id}},
editor: {context: {user: DataGenerator.Content.users[2].id}}, editor: {context: {user: DataGenerator.Content.users[2].id}},
author: {context: {user: DataGenerator.Content.users[3].id}} author: {context: {user: DataGenerator.Content.users[3].id}}
}, },
users: { users: {
ids: { ids: {
@ -761,9 +853,9 @@ module.exports = {
cacheRules: { cacheRules: {
public: 'public, max-age=0', public: 'public, max-age=0',
hour: 'public, max-age=' + 3600, hour: 'public, max-age=' + 3600,
day: 'public, max-age=' + 86400, day: 'public, max-age=' + 86400,
year: 'public, max-age=' + 31536000, year: 'public, max-age=' + 31536000,
private: 'no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0' private: 'no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0'
} }
}; };