From f8c51ac7e54f868ec271d206db78a99f9ee6efab Mon Sep 17 00:00:00 2001 From: Katharina Irrgang Date: Thu, 9 Mar 2017 20:38:20 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=98=B1=20=20=F0=9F=9A=80=20=20?= =?UTF-8?q?=F0=9F=8E=A8=20=20tests:=20use=20truncate=20instead=20of=20data?= =?UTF-8?q?base=20deletion=20(#8119)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 😱 🚀 🎨 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 --- Gruntfile.js | 15 +- core/test/functional/routes/frontend_spec.js | 10 +- core/test/utils/fork.js | 3 + core/test/utils/index.js | 272 +++++++++++++------ 4 files changed, 205 insertions(+), 95 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 790ce75b7c..91a371d02f 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -8,9 +8,15 @@ // jshint unused: false var overrides = require('./core/server/overrides'), + config = require('./core/server/config'), _ = require('lodash'), chalk = require('chalk'), fs = require('fs-extra'), + KnexMigrator = require('knex-migrator'), + knexMigrator = new KnexMigrator({ + knexMigratorFilePath: config.get('paths:appRoot') + }), + path = require('path'), 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 // **Main testing task** // @@ -499,7 +512,7 @@ var overrides = require('./core/server/overrides'), // ### test-setup *(utility)( // `grunt test-setup` will run all the setup tasks required for running tests grunt.registerTask('test-setup', 'Setup ready to run tests', - ['clean:test', 'setTestEnv'] + ['knex-migrator', 'clean:test', 'setTestEnv'] ); // ### Unit Tests *(sub task)* diff --git a/core/test/functional/routes/frontend_spec.js b/core/test/functional/routes/frontend_spec.js index b526b25dd9..2127d7d05c 100644 --- a/core/test/functional/routes/frontend_spec.js +++ b/core/test/functional/routes/frontend_spec.js @@ -35,7 +35,9 @@ describe('Frontend Routing', function () { } function addPosts(done) { - testUtils.initData().then(function () { + testUtils.clearData().then(function () { + return testUtils.initData(); + }).then(function () { return testUtils.fixtures.insertPostsAndTags(); }).then(function () { done(); @@ -493,10 +495,10 @@ describe('Frontend Routing', function () { }); describe('Site Map', function () { - before(testUtils.teardown); - before(function (done) { - testUtils.initData().then(function () { + testUtils.clearData().then(function () { + return testUtils.initData(); + }).then(function () { return testUtils.fixtures.insertPostsAndTags(); }).then(function () { done(); diff --git a/core/test/utils/fork.js b/core/test/utils/fork.js index 7921447657..81b8411701 100644 --- a/core/test/utils/fork.js +++ b/core/test/utils/fork.js @@ -49,6 +49,9 @@ function forkGhost(newConfig) { .then(function (_port) { port = _port; + return knexMigrator.reset(); + }) + .then(function () { return knexMigrator.init(); }) .then(function () { diff --git a/core/test/utils/index.js b/core/test/utils/index.js index 3a4d02d5eb..ba79ec584d 100644 --- a/core/test/utils/index.js +++ b/core/test/utils/index.js @@ -1,28 +1,31 @@ -var Promise = require('bluebird'), - _ = require('lodash'), - fs = require('fs-extra'), - path = require('path'), - Module = require('module'), - debug = require('debug')('ghost:test'), - ObjectId = require('bson-objectid'), - uuid = require('uuid'), - KnexMigrator = require('knex-migrator'), - ghost = require('../../server'), - errors = require('../../server/errors'), - db = require('../../server/data/db'), - fixtureUtils = require('../../server/data/schema/fixtures/utils'), - models = require('../../server/models'), - SettingsLib = require('../../server/settings'), - permissions = require('../../server/permissions'), - sequence = require('../../server/utils/sequence'), - themes = require('../../server/themes'), +/*jshint expr:true*/ +var Promise = require('bluebird'), + _ = require('lodash'), + fs = require('fs-extra'), + path = require('path'), + Module = require('module'), + debug = require('debug')('ghost:test'), + ObjectId = require('bson-objectid'), + uuid = require('uuid'), + KnexMigrator = require('knex-migrator'), + ghost = require('../../server'), + errors = require('../../server/errors'), + db = require('../../server/data/db'), + fixtureUtils = require('../../server/data/schema/fixtures/utils'), + schema = require('../../server/data/schema').tables, + schemaTables = Object.keys(schema), + models = require('../../server/models'), + SettingsLib = require('../../server/settings'), + permissions = require('../../server/permissions'), + sequence = require('../../server/utils/sequence'), + themes = require('../../server/themes'), DataGenerator = require('./fixtures/data-generator'), - filterData = require('./fixtures/filter-param'), - API = require('./api'), - fork = require('./fork'), - mocks = require('./mocks'), - config = require('../../server/config'), - knexMigrator = new KnexMigrator(), + filterData = require('./fixtures/filter-param'), + API = require('./api'), + fork = require('./fork'), + mocks = require('./mocks'), + config = require('../../server/config'), + knexMigrator = new KnexMigrator(), fixtures, getFixtureOps, toDoList, @@ -293,7 +296,7 @@ fixtures = { insertOne: function insertOne(obj, fn, index) { 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() { @@ -423,7 +426,9 @@ clearData = function clearData() { }; toDoList = { - app: function insertApp() { return fixtures.insertOne('apps', 'createApp'); }, + 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 () { @@ -436,32 +441,80 @@ toDoList = { 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'); }, - 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); }; + permission: function insertPermission() { + return fixtures.insertOne('permissions', 'createPermission'); }, - 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(); } + role: function insertRole() { + return fixtures.insertOne('roles', 'createRole'); + }, + 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(); + }, + '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 * * 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 + * * `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) { @@ -614,14 +667,14 @@ login = function login(request) { 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 - })); - } + if (res.statusCode !== 200) { + return reject(new errors.GhostError({ + message: res.body.errors[0].message + })); + } - resolve(res.body.access_token); - }, reject); + resolve(res.body.access_token); + }, reject); }); }; @@ -632,18 +685,20 @@ togglePermalinks = function togglePermalinks(request, toggle) { 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 - } - ]}) + .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); @@ -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) { - debug('Database reset'); + debug('Database teardown'); + var tables = schemaTables.concat(['migrations']); - if (done) { - knexMigrator.reset() - .then(function () { - done(); + if (config.get('database:client') === 'sqlite3') { + return Promise + .mapSeries(tables, function createTable(table) { + return db.knex.raw('DELETE FROM ' + table + ';'); }) - .catch(done); - } else { - return knexMigrator.reset(); + .then(function () { + done && done(); + }) + .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 context: { - internal: {context: {internal: true}}, - external: {context: {external: true}}, - 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}} + internal: {context: {internal: true}}, + external: {context: {external: true}}, + 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: { @@ -761,9 +853,9 @@ module.exports = { cacheRules: { public: 'public, max-age=0', - hour: 'public, max-age=' + 3600, + hour: 'public, max-age=' + 3600, 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' } };