From d50354dde3f8c635d12f04fd925c48800979ad8f Mon Sep 17 00:00:00 2001 From: Hannah Wolfe Date: Sat, 28 Dec 2013 20:13:47 +0000 Subject: [PATCH] Update import tool to be safe closes #1681 - import doesn't override user credentials - import doesn't override theme - import doesn't kill session - import does refresh the settings cache - updated tests, they now use a fixture instead of a generated export - tests check to ensure import is safe --- core/server/api/db.js | 53 ++-- core/server/data/import/000.js | 25 +- core/test/unit/import_spec.js | 166 +++++++----- core/test/utils/fixtures/export-000.json | 169 ++++++++++++ core/test/utils/fixtures/export-001.json | 331 +++++++++++++++++++++++ core/test/utils/index.js | 56 ++-- 6 files changed, 679 insertions(+), 121 deletions(-) create mode 100644 core/test/utils/fixtures/export-000.json create mode 100644 core/test/utils/fixtures/export-001.json diff --git a/core/server/api/db.js b/core/server/api/db.js index e28423c0ee..9062d48268 100644 --- a/core/server/api/db.js +++ b/core/server/api/db.js @@ -1,8 +1,8 @@ var dataExport = require('../data/export'), dataImport = require('../data/import'), dataProvider = require('../models'), - apiNotifications = require('./notifications'), - apiSettings = require('./settings'), + notifications = require('./notifications'), + settings = require('./settings'), fs = require('fs-extra'), path = require('path'), when = require('when'), @@ -29,7 +29,7 @@ db = { res.download(exportedFilePath, 'GhostData.json'); }).otherwise(function (error) { // Notify of an error if it occurs - return apiNotifications.browse().then(function (notifications) { + return notifications.browse().then(function (notifications) { var notification = { type: 'error', message: error.message || error, @@ -37,7 +37,7 @@ db = { id: 'per-' + (notifications.length + 1) }; - return apiNotifications.add(notification).then(function () { + return notifications.add(notification).then(function () { res.redirect(debugPath); }); }); @@ -56,7 +56,7 @@ db = { * - If there is no path * - If the name doesn't have json in it */ - return apiNotifications.browse().then(function (notifications) { + return notifications.browse().then(function (notifications) { notification = { type: 'error', message: "Must select a .json file to import", @@ -64,13 +64,13 @@ db = { id: 'per-' + (notifications.length + 1) }; - return apiNotifications.add(notification).then(function () { + return notifications.add(notification).then(function () { res.redirect(debugPath); }); }); } - apiSettings.read({ key: 'databaseVersion' }).then(function (setting) { + settings.read({ key: 'databaseVersion' }).then(function (setting) { return when(setting.value); }, function () { return when('001'); @@ -100,17 +100,15 @@ db = { for (prop in elem) { if (elem.hasOwnProperty(prop)) { if (schema[constkey].hasOwnProperty(prop)) { - if (elem.hasOwnProperty(prop)) { - if (!_.isNull(elem[prop])) { - if (elem[prop].length > schema[constkey][prop].maxlength) { - error += error !== "" ? "
" : ""; - error += "Property '" + prop + "' exceeds maximum length of " + schema[constkey][prop].maxlength + " (element:" + constkey + " / id:" + elem.id + ")"; - } - } else { - if (!schema[constkey][prop].nullable) { - error += error !== "" ? "
" : ""; - error += "Property '" + prop + "' is not nullable (element:" + constkey + " / id:" + elem.id + ")"; - } + if (!_.isNull(elem[prop])) { + if (elem[prop].length > schema[constkey][prop].maxlength) { + error += error !== "" ? "
" : ""; + error += "Property '" + prop + "' exceeds maximum length of " + schema[constkey][prop].maxlength + " (element:" + constkey + " / id:" + elem.id + ")"; + } + } else { + if (!schema[constkey][prop].nullable) { + error += error !== "" ? "
" : ""; + error += "Property '" + prop + "' is not nullable (element:" + constkey + " / id:" + elem.id + ")"; } } } else { @@ -127,25 +125,24 @@ db = { } // Import for the current version return dataImport(databaseVersion, importData); + }).then(function importSuccess() { - return apiNotifications.browse(); + return settings.updateSettingsCache(); + }).then(function () { + return notifications.browse(); }).then(function (notifications) { notification = { type: 'success', - message: "Data imported. Log in with the user details you imported", + message: "Posts, tags and other data successfully imported", status: 'persistent', id: 'per-' + (notifications.length + 1) }; - return apiNotifications.add(notification).then(function () { - req.session.destroy(); - res.set({ - "X-Cache-Invalidate": "/*" - }); - res.redirect(config.paths().subdir + '/ghost/signin/'); + return notifications.add(notification).then(function () { + res.redirect(debugPath); }); }).otherwise(function importFailure(error) { - return apiNotifications.browse().then(function (notifications) { + return notifications.browse().then(function (notifications) { // Notify of an error if it occurs notification = { type: 'error', @@ -154,7 +151,7 @@ db = { id: 'per-' + (notifications.length + 1) }; - return apiNotifications.add(notification).then(function () { + return notifications.add(notification).then(function () { res.redirect(debugPath); }); }); diff --git a/core/server/data/import/000.js b/core/server/data/import/000.js index 72f9a2c09a..b6ea5e4a55 100644 --- a/core/server/data/import/000.js +++ b/core/server/data/import/000.js @@ -82,7 +82,9 @@ function importTags(ops, tableData, transaction) { _.each(tableData, function (tag) { ops.push(models.Tag.findOne({name: tag.name}, {transacting: transaction}).then(function (_tag) { if (!_tag) { - return models.Tag.add(tag, {transacting: transaction}); + return models.Tag.add(tag, {transacting: transaction}) + // add pass-through error handling so that bluebird doesn't think we've dropped it + .otherwise(function (error) { return when.reject(error); }); } return when.resolve(_tag); })); @@ -92,26 +94,35 @@ function importTags(ops, tableData, transaction) { function importPosts(ops, tableData, transaction) { tableData = stripProperties(['id'], tableData); _.each(tableData, function (post) { - ops.push(models.Post.add(post, {transacting: transaction, importing: true})); + ops.push(models.Post.add(post, {transacting: transaction, importing: true}) + // add pass-through error handling so that bluebird doesn't think we've dropped it + .otherwise(function (error) { return when.reject(error); })); }); } function importUsers(ops, tableData, transaction) { - tableData = stripProperties(['id'], tableData); + // don't override the users credentials + tableData = stripProperties(['id', 'email', 'password'], tableData); tableData[0].id = 1; - ops.push(models.User.edit(tableData[0], {transacting: transaction})); + ops.push(models.User.edit(tableData[0], {transacting: transaction}) + // add pass-through error handling so that bluebird doesn't think we've dropped it + .otherwise(function (error) { return when.reject(error); })); } function importSettings(ops, tableData, transaction) { // for settings we need to update individual settings, and insert any missing ones - // the one setting we MUST NOT update is the databaseVersion settings - var blackList = ['databaseVersion']; + // settings we MUST NOT update are the databaseVersion, dbHash, and activeTheme + // as all of these will cause side effects which don't make sense for an import + + var blackList = ['databaseVersion', 'dbHash', 'activeTheme']; tableData = stripProperties(['id'], tableData); tableData = _.filter(tableData, function (data) { return blackList.indexOf(data.key) === -1; }); - ops.push(models.Settings.edit(tableData, transaction)); + ops.push(models.Settings.edit(tableData, transaction) + // add pass-through error handling so that bluebird doesn't think we've dropped it + .otherwise(function (error) { return when.reject(error); })); } // No data needs modifying, we just import whatever tables are available diff --git a/core/test/unit/import_spec.js b/core/test/unit/import_spec.js index d41fd59621..f1fa41e560 100644 --- a/core/test/unit/import_spec.js +++ b/core/test/unit/import_spec.js @@ -62,12 +62,7 @@ describe("Import", function () { describe("000", function () { should.exist(Importer000); - it("imports data from 001", function (done) { - var exportData; - var migrationStub = sinon.stub(migration, "getDatabaseVersion", function () { - return when.resolve("000"); - }); - + beforeEach(function (done) { // migrate to current version migration.migrateUp().then(function () { // Load the fixtures @@ -76,10 +71,20 @@ describe("Import", function () { // Initialise the default settings return Settings.populateDefaults(); }).then(function () { - // export the version 000 data ready to import - // TODO: Should have static test data here? - return exporter(); - }).then(function (exported) { + return testUtils.insertDefaultUser(); + }).then(function () { + done(); + }).then(null, done); + }); + + + it("imports data from 000", function (done) { + var exportData, + migrationStub = sinon.stub(migration, "getDatabaseVersion", function () { + return when.resolve("000"); + }); + + testUtils.loadExportFixture('export-000').then(function (exported) { exportData = exported; return importer("000", exportData); @@ -92,21 +97,28 @@ describe("Import", function () { knex("tags").select() ]); }).then(function (importedData) { - should.exist(importedData); + importedData.length.should.equal(4, 'Did not get data successfully'); - // we always have 0 users as there isn't one in fixtures - importedData[0].length.should.equal(0, 'There should not be a user'); + var users = importedData[0], + posts = importedData[1], + settings = importedData[2], + tags = importedData[3]; + + // we always have 1 user, the default user we added + users.length.should.equal(1, 'There should only be one user'); // import no longer requires all data to be dropped, and adds posts - importedData[1].length.should.equal(exportData.data.posts.length + 1, 'Wrong number of posts'); + posts.length.should.equal(exportData.data.posts.length + 1, 'Wrong number of posts'); // test settings - importedData[2].length.should.be.above(0, 'Wrong number of settings'); - _.findWhere(importedData[2], {key: "databaseVersion"}).value.should.equal("001", 'Wrong database version'); + settings.length.should.be.above(0, 'Wrong number of settings'); + _.findWhere(settings, {key: "databaseVersion"}).value.should.equal("001", 'Wrong database version'); // test tags - importedData[3].length.should.equal(exportData.data.tags.length, 'no new tags'); + tags.length.should.equal(exportData.data.tags.length, 'no new tags'); + + migrationStub.restore(); done(); }).then(null, done); @@ -116,22 +128,26 @@ describe("Import", function () { describe("001", function () { should.exist(Importer001); - it("imports data from 001", function (done) { - var exportData, - timestamp = 1349928000000; - - // Migrate to version 001 + beforeEach(function (done) { + // migrate to current version migration.migrateUp().then(function () { // Load the fixtures return fixtures.populateFixtures(); }).then(function () { - // Initialise the default settings + // Initialise the default settings return Settings.populateDefaults(); }).then(function () { - // export the version 000 data ready to import - // TODO: Should have static test data here? - return exporter(); - }).then(function (exported) { + return testUtils.insertDefaultUser(); + }).then(function () { + done(); + }).then(null, done); + }); + + it("safely imports data from 001", function (done) { + var exportData, + timestamp = 1349928000000; + + testUtils.loadExportFixture('export-001').then(function (exported) { exportData = exported; // Modify timestamp data for testing @@ -156,10 +172,20 @@ describe("Import", function () { var users = importedData[0], posts = importedData[1], settings = importedData[2], - tags = importedData[3]; + tags = importedData[3], + exportEmail; + + // we always have 1 user, the default user we added + users.length.should.equal(1, 'There should only be one user'); + + // user should still have the credentials from the original insert, not the import + users[0].email.should.equal(testUtils.DataGenerator.Content.users[0].email); + users[0].password.should.equal(testUtils.DataGenerator.Content.users[0].password); + // but the name, slug, and bio should have been overridden + users[0].name.should.equal(exportData.data.users[0].name); + users[0].slug.should.equal(exportData.data.users[0].slug); + users[0].bio.should.equal(exportData.data.users[0].bio); - // we always have 0 users as there isn't one in fixtures - users.length.should.equal(0, 'There should not be a user'); // import no longer requires all data to be dropped, and adds posts posts.length.should.equal(exportData.data.posts.length + 1, 'Wrong number of posts'); @@ -167,6 +193,13 @@ describe("Import", function () { settings.length.should.be.above(0, 'Wrong number of settings'); _.findWhere(settings, {key: "databaseVersion"}).value.should.equal("001", 'Wrong database version'); + // activeTheme should NOT have been overridden + _.findWhere(settings, {key: "activeTheme"}).value.should.equal("casper", 'Wrong theme'); + + // email address should have been overridden + exportEmail = _.findWhere(exportData.data.settings, {key: "email"}).value; + _.findWhere(settings, {key: "email"}).value.should.equal(exportEmail, 'Wrong email in settings'); + // test tags tags.length.should.equal(exportData.data.tags.length, 'no new tags'); @@ -175,9 +208,9 @@ describe("Import", function () { // in MySQL we're returned a date object. // We pass the returned post always through the date object // to ensure the return is consistant for all DBs. - assert.equal( new Date(posts[1].created_at).getTime(), timestamp); - assert.equal( new Date(posts[1].updated_at).getTime(), timestamp); - assert.equal( new Date(posts[1].published_at).getTime(), timestamp); + assert.equal(new Date(posts[1].created_at).getTime(), timestamp); + assert.equal(new Date(posts[1].updated_at).getTime(), timestamp); + assert.equal(new Date(posts[1].published_at).getTime(), timestamp); done(); }).then(null, done); @@ -185,19 +218,11 @@ describe("Import", function () { it("doesn't import invalid post data from 001", function (done) { var exportData; - // migrate to current version - migration.migrateUp().then(function () { - // Load the fixtures - return fixtures.populateFixtures(); - }).then(function () { - // Initialise the default settings - return Settings.populateDefaults(); - }).then(function () { - // export the version 000 data ready to import - // TODO: Should have static test data here? - return exporter(); - }).then(function (exported) { + + + testUtils.loadExportFixture('export-001').then(function (exported) { exportData = exported; + //change title to 151 characters exportData.data.posts[0].title = new Array(152).join('a'); exportData.data.posts[0].tags = 'Tag'; @@ -214,40 +239,36 @@ describe("Import", function () { knex("tags").select() ]).then(function (importedData) { should.exist(importedData); + importedData.length.should.equal(4, 'Did not get data successfully'); - // we always have 0 users as there isn't one in fixtures - importedData[0].length.should.equal(0, 'There should not be a user'); + var users = importedData[0], + posts = importedData[1], + settings = importedData[2], + tags = importedData[3]; + + // we always have 1 user, the default user we added + users.length.should.equal(1, 'There should only be one user'); // import no longer requires all data to be dropped, and adds posts - importedData[1].length.should.equal(exportData.data.posts.length, 'Wrong number of posts'); + posts.length.should.equal(exportData.data.posts.length, 'Wrong number of posts'); // test settings - importedData[2].length.should.be.above(0, 'Wrong number of settings'); - _.findWhere(importedData[2], {key: "databaseVersion"}).value.should.equal("001", 'Wrong database version'); + settings.length.should.be.above(0, 'Wrong number of settings'); + _.findWhere(settings, {key: "databaseVersion"}).value.should.equal("001", 'Wrong database version'); // test tags - importedData[3].length.should.equal(exportData.data.tags.length, 'no new tags'); + tags.length.should.equal(exportData.data.tags.length, 'no new tags'); done(); }); }).then(null, done); }); + it("doesn't import invalid settings data from 001", function (done) { var exportData; - // migrate to current version - migration.migrateUp().then(function () { - // Load the fixtures - return fixtures.populateFixtures(); - }).then(function () { - // Initialise the default settings - return Settings.populateDefaults(); - }).then(function () { - // export the version 000 data ready to import - // TODO: Should have static test data here? - return exporter(); - }).then(function (exported) { + testUtils.loadExportFixture('export-001').then(function (exported) { exportData = exported; //change to blank settings key exportData.data.settings[3].key = null; @@ -256,6 +277,7 @@ describe("Import", function () { (1).should.eql(0, 'Data import should not resolve promise.'); }, function (error) { error.should.eql('Error importing data: Setting key cannot be blank'); + when.all([ knex("users").select(), knex("posts").select(), @@ -263,19 +285,25 @@ describe("Import", function () { knex("tags").select() ]).then(function (importedData) { should.exist(importedData); + importedData.length.should.equal(4, 'Did not get data successfully'); - // we always have 0 users as there isn't one in fixtures - importedData[0].length.should.equal(0, 'There should not be a user'); + var users = importedData[0], + posts = importedData[1], + settings = importedData[2], + tags = importedData[3]; + + // we always have 1 user, the default user we added + users.length.should.equal(1, 'There should only be one user'); // import no longer requires all data to be dropped, and adds posts - importedData[1].length.should.equal(exportData.data.posts.length, 'Wrong number of posts'); + posts.length.should.equal(exportData.data.posts.length, 'Wrong number of posts'); // test settings - importedData[2].length.should.be.above(0, 'Wrong number of settings'); - _.findWhere(importedData[2], {key: "databaseVersion"}).value.should.equal("001", 'Wrong database version'); + settings.length.should.be.above(0, 'Wrong number of settings'); + _.findWhere(settings, {key: "databaseVersion"}).value.should.equal("001", 'Wrong database version'); // test tags - importedData[3].length.should.equal(exportData.data.tags.length, 'no new tags'); + tags.length.should.equal(exportData.data.tags.length, 'no new tags'); done(); }); diff --git a/core/test/utils/fixtures/export-000.json b/core/test/utils/fixtures/export-000.json new file mode 100644 index 0000000000..429464c2d7 --- /dev/null +++ b/core/test/utils/fixtures/export-000.json @@ -0,0 +1,169 @@ +{ + "meta": { + "exported_on": 1388318311015, + "version": "000" + }, + "data": { + "posts": [ + { + "id": 1, + "uuid": "8492fbba-1102-4b53-8e3e-abe207952f0c", + "title": "Welcome to Ghost", + "slug": "welcome-to-ghost", + "markdown": "You're live! Nice.", + "html": "

You're live! Nice.

", + "image": null, + "featured": 0, + "page": 0, + "status": "published", + "language": "en_US", + "meta_title": null, + "meta_description": null, + "author_id": 1, + "created_at": 1388318310782, + "created_by": 1, + "updated_at": 1388318310782, + "updated_by": 1, + "published_at": 1388318310783, + "published_by": 1 + } + ], + "users": [], + "roles": [ + { + "id": 1, + "uuid": "d2ea9c7f-7e6b-4cae-b009-35c298206852", + "name": "Administrator", + "description": "Administrators", + "created_at": 1388318310794, + "created_by": 1, + "updated_at": 1388318310794, + "updated_by": 1 + }, + { + "id": 2, + "uuid": "b0d7d6b0-5b88-45b5-b0e5-a487741b843d", + "name": "Editor", + "description": "Editors", + "created_at": 1388318310796, + "created_by": 1, + "updated_at": 1388318310796, + "updated_by": 1 + }, + { + "id": 3, + "uuid": "9f72e817-5490-4ccf-bc78-c557dc9613ca", + "name": "Author", + "description": "Authors", + "created_at": 1388318310799, + "created_by": 1, + "updated_at": 1388318310799, + "updated_by": 1 + } + ], + "roles_users": [], + "permissions": [ + { + "id": 1, + "uuid": "bdfbd261-e0fb-4c8e-abab-aece7a9e8e34", + "name": "Edit posts", + "object_type": "post", + "action_type": "edit", + "object_id": null, + "created_at": 1388318310803, + "created_by": 1, + "updated_at": 1388318310803, + "updated_by": 1 + }, + { + "id": 2, + "uuid": "580d31c4-e3db-40f3-969d-9a1caea9d1bb", + "name": "Remove posts", + "object_type": "post", + "action_type": "remove", + "object_id": null, + "created_at": 1388318310814, + "created_by": 1, + "updated_at": 1388318310814, + "updated_by": 1 + }, + { + "id": 3, + "uuid": "c1f8b024-e383-494a-835d-6fb673f143db", + "name": "Create posts", + "object_type": "post", + "action_type": "create", + "object_id": null, + "created_at": 1388318310818, + "created_by": 1, + "updated_at": 1388318310818, + "updated_by": 1 + } + ], + "permissions_users": [], + "permissions_roles": [ + { + "id": 1, + "role_id": 1, + "permission_id": 1 + }, + { + "id": 2, + "role_id": 1, + "permission_id": 2 + }, + { + "id": 3, + "role_id": 1, + "permission_id": 3 + } + ], + "settings": [ + { + "id": 1, + "uuid": "f90aa810-4fa2-49fe-a39b-7c0d2ebb473e", + "key": "databaseVersion", + "value": "000", + "type": "core", + "created_at": 1388318310829, + "created_by": 1, + "updated_at": 1388318310829, + "updated_by": 1 + }, + { + "id": 3, + "uuid": "c356fbde-0bc5-4fe1-9309-2510291aa34d", + "key": "title", + "value": "Ghost", + "type": "blog", + "created_at": 1388318310830, + "created_by": 1, + "updated_at": 1388318310830, + "updated_by": 1 + } + ], + "tags": [ + { + "id": 1, + "uuid": "a950117a-9735-4584-931d-25a28015a80d", + "name": "Getting Started", + "slug": "getting-started", + "description": null, + "parent_id": null, + "meta_title": null, + "meta_description": null, + "created_at": 1388318310790, + "created_by": 1, + "updated_at": 1388318310790, + "updated_by": 1 + } + ], + "posts_tags": [ + { + "id": 1, + "post_id": 1, + "tag_id": 1 + } + ] + } +} \ No newline at end of file diff --git a/core/test/utils/fixtures/export-001.json b/core/test/utils/fixtures/export-001.json new file mode 100644 index 0000000000..82732f0021 --- /dev/null +++ b/core/test/utils/fixtures/export-001.json @@ -0,0 +1,331 @@ +{ + "meta": { + "exported_on": 1388318311015, + "version": "001" + }, + "data": { + "posts": [ + { + "id": 1, + "uuid": "8492fbba-1102-4b53-8e3e-abe207952f0c", + "title": "Welcome to Ghost", + "slug": "welcome-to-ghost", + "markdown": "You're live! Nice.", + "html": "

You're live! Nice.

", + "image": null, + "featured": 0, + "page": 0, + "status": "published", + "language": "en_US", + "meta_title": null, + "meta_description": null, + "author_id": 1, + "created_at": 1388318310782, + "created_by": 1, + "updated_at": 1388318310782, + "updated_by": 1, + "published_at": 1388318310783, + "published_by": 1 + } + ], + "users": [ + { + "id": 1, + "uuid": "e5188224-4742-4c32-a2d6-e9c5c5d4c123", + "name": "Josephine Bloggs", + "slug": "josephine-blogs", + "password": "$2a$10$.pZeeBE0gHXd0PTnbT/ph.GEKgd0Wd3q2pWna3ynTGBkPKnGIKABC", + "email": "josephinebloggs@example.com", + "image": null, + "cover": null, + "bio": "A blogger", + "website": null, + "location": null, + "accessibility": null, + "status": "active", + "language": "en_US", + "meta_title": null, + "meta_description": null, + "last_login": null, + "created_at": 1388319501897, + "created_by": 1, + "updated_at": null, + "updated_by": null + } + ], + "roles": [ + { + "id": 1, + "uuid": "d2ea9c7f-7e6b-4cae-b009-35c298206852", + "name": "Administrator", + "description": "Administrators", + "created_at": 1388318310794, + "created_by": 1, + "updated_at": 1388318310794, + "updated_by": 1 + }, + { + "id": 2, + "uuid": "b0d7d6b0-5b88-45b5-b0e5-a487741b843d", + "name": "Editor", + "description": "Editors", + "created_at": 1388318310796, + "created_by": 1, + "updated_at": 1388318310796, + "updated_by": 1 + }, + { + "id": 3, + "uuid": "9f72e817-5490-4ccf-bc78-c557dc9613ca", + "name": "Author", + "description": "Authors", + "created_at": 1388318310799, + "created_by": 1, + "updated_at": 1388318310799, + "updated_by": 1 + } + ], + "roles_users": [ + { + "id": 1, + "role_id": 1, + "user_id": 1 + } + ], + "permissions": [ + { + "id": 1, + "uuid": "bdfbd261-e0fb-4c8e-abab-aece7a9e8e34", + "name": "Edit posts", + "object_type": "post", + "action_type": "edit", + "object_id": null, + "created_at": 1388318310803, + "created_by": 1, + "updated_at": 1388318310803, + "updated_by": 1 + }, + { + "id": 2, + "uuid": "580d31c4-e3db-40f3-969d-9a1caea9d1bb", + "name": "Remove posts", + "object_type": "post", + "action_type": "remove", + "object_id": null, + "created_at": 1388318310814, + "created_by": 1, + "updated_at": 1388318310814, + "updated_by": 1 + }, + { + "id": 3, + "uuid": "c1f8b024-e383-494a-835d-6fb673f143db", + "name": "Create posts", + "object_type": "post", + "action_type": "create", + "object_id": null, + "created_at": 1388318310818, + "created_by": 1, + "updated_at": 1388318310818, + "updated_by": 1 + } + ], + "permissions_users": [], + "permissions_roles": [ + { + "id": 1, + "role_id": 1, + "permission_id": 1 + }, + { + "id": 2, + "role_id": 1, + "permission_id": 2 + }, + { + "id": 3, + "role_id": 1, + "permission_id": 3 + } + ], + "settings": [ + { + "id": 1, + "uuid": "f90aa810-4fa2-49fe-a39b-7c0d2ebb473e", + "key": "databaseVersion", + "value": "001", + "type": "core", + "created_at": 1388318310829, + "created_by": 1, + "updated_at": 1388318310829, + "updated_by": 1 + }, + { + "id": 2, + "uuid": "95ce1c53-69b0-4f5f-be91-d3aeb39046b5", + "key": "dbHash", + "value": null, + "type": "core", + "created_at": 1388318310829, + "created_by": 1, + "updated_at": 1388318310829, + "updated_by": 1 + }, + { + "id": 3, + "uuid": "c356fbde-0bc5-4fe1-9309-2510291aa34d", + "key": "title", + "value": "Ghost", + "type": "blog", + "created_at": 1388318310830, + "created_by": 1, + "updated_at": 1388318310830, + "updated_by": 1 + }, + { + "id": 4, + "uuid": "858dc11f-8f9e-4011-99ee-d94c48d5a2ce", + "key": "description", + "value": "Just a blogging platform.", + "type": "blog", + "created_at": 1388318310830, + "created_by": 1, + "updated_at": 1388318310830, + "updated_by": 1 + }, + { + "id": 5, + "uuid": "37ca5ae7-bca6-4dd5-8021-4ef6c6dcb097", + "key": "email", + "value": "josephinebloggs@example.com", + "type": "blog", + "created_at": 1388318310830, + "created_by": 1, + "updated_at": 1388318310830, + "updated_by": 1 + }, + { + "id": 6, + "uuid": "1672d62c-fab7-4f22-b333-8cf760189f67", + "key": "logo", + "value": "", + "type": "blog", + "created_at": 1388318310830, + "created_by": 1, + "updated_at": 1388318310830, + "updated_by": 1 + }, + { + "id": 7, + "uuid": "cd8b0456-578b-467a-857e-551bad17a14d", + "key": "cover", + "value": "", + "type": "blog", + "created_at": 1388318310830, + "created_by": 1, + "updated_at": 1388318310830, + "updated_by": 1 + }, + { + "id": 8, + "uuid": "c4a074a4-05c7-49f7-83eb-068302c15d82", + "key": "defaultLang", + "value": "en_US", + "type": "blog", + "created_at": 1388318310830, + "created_by": 1, + "updated_at": 1388318310830, + "updated_by": 1 + }, + { + "id": 9, + "uuid": "21f2f5da-9bee-4dae-b3b7-b8d7baf8be33", + "key": "postsPerPage", + "value": "6", + "type": "blog", + "created_at": 1388318310830, + "created_by": 1, + "updated_at": 1388318310830, + "updated_by": 1 + }, + { + "id": 10, + "uuid": "2d21b736-f85a-4119-a0e3-5fc898b1bf47", + "key": "forceI18n", + "value": "true", + "type": "blog", + "created_at": 1388318310831, + "created_by": 1, + "updated_at": 1388318310831, + "updated_by": 1 + }, + { + "id": 11, + "uuid": "5c5b91b8-6062-4104-b855-9e121f72b0f0", + "key": "permalinks", + "value": "/:slug/", + "type": "blog", + "created_at": 1388318310831, + "created_by": 1, + "updated_at": 1388318310831, + "updated_by": 1 + }, + { + "id": 12, + "uuid": "795cb328-3e38-4906-81a8-fcdff19d914f", + "key": "activeTheme", + "value": "notcasper", + "type": "theme", + "created_at": 1388318310831, + "created_by": 1, + "updated_at": 1388318310831, + "updated_by": 1 + }, + { + "id": 13, + "uuid": "f3afce35-5166-453e-86c3-50dfff74dca7", + "key": "activePlugins", + "value": "[]", + "type": "plugin", + "created_at": 1388318310831, + "created_by": 1, + "updated_at": 1388318310831, + "updated_by": 1 + }, + { + "id": 14, + "uuid": "2ea560a3-2304-449d-a62b-f7b622987510", + "key": "installedPlugins", + "value": "[]", + "type": "plugin", + "created_at": 1388318310831, + "created_by": 1, + "updated_at": 1388318310831, + "updated_by": 1 + } + ], + "tags": [ + { + "id": 1, + "uuid": "a950117a-9735-4584-931d-25a28015a80d", + "name": "Getting Started", + "slug": "getting-started", + "description": null, + "parent_id": null, + "meta_title": null, + "meta_description": null, + "created_at": 1388318310790, + "created_by": 1, + "updated_at": 1388318310790, + "updated_by": 1 + } + ], + "posts_tags": [ + { + "id": 1, + "post_id": 1, + "tag_id": 1 + } + ] + } +} \ No newline at end of file diff --git a/core/test/utils/index.js b/core/test/utils/index.js index bd385702b1..5d8084edb4 100644 --- a/core/test/utils/index.js +++ b/core/test/utils/index.js @@ -1,9 +1,12 @@ -var knex = require('../../server/models/base').knex, - when = require('when'), - migration = require("../../server/data/migration/"), - Settings = require('../../server/models/settings').Settings, +var knex = require('../../server/models/base').knex, + when = require('when'), + nodefn = require('when/node/function'), + fs = require('fs-extra'), + path = require('path'), + migration = require("../../server/data/migration/"), + Settings = require('../../server/models/settings').Settings, DataGenerator = require('./fixtures/data-generator'), - API = require('./api'); + API = require('./api'); function initData() { return migration.init(); @@ -14,18 +17,12 @@ function clearData() { return migration.reset(); } -function insertDefaultFixtures() { - return when(insertDefaultUser().then(function(){ - return insertPosts(); - })); -} - function insertPosts() { return when(knex('posts').insert(DataGenerator.forKnex.posts).then(function () { - return knex('tags').insert(DataGenerator.forKnex.tags).then(function () { - return knex('posts_tags').insert(DataGenerator.forKnex.posts_tags); - }); - })); + return knex('tags').insert(DataGenerator.forKnex.tags).then(function () { + return knex('posts_tags').insert(DataGenerator.forKnex.posts_tags); + }); + })); } function insertMorePosts() { @@ -58,8 +55,31 @@ function insertDefaultUser() { users.push(DataGenerator.forKnex.createUser(DataGenerator.Content.users[0])); userRoles.push(DataGenerator.forKnex.createUserRole(1, 1)); return when(knex('users').insert(users).then(function () { - return knex('roles_users').insert(userRoles); - })); + return knex('roles_users').insert(userRoles); + })); +} + +function insertDefaultFixtures() { + return when(insertDefaultUser().then(function () { + return insertPosts(); + })); +} + +function loadExportFixture(filename) { + var filepath = path.resolve(__dirname + '/fixtures/' + filename + '.json'); + + return nodefn.call(fs.readFile, filepath).then(function (fileContents) { + var data; + + // Parse the json data + try { + data = JSON.parse(fileContents); + } catch (e) { + return when.reject(new Error("Failed to parse the file")); + } + + return data; + }); } module.exports = { @@ -70,6 +90,8 @@ module.exports = { insertMorePosts: insertMorePosts, insertDefaultUser: insertDefaultUser, + loadExportFixture: loadExportFixture, + DataGenerator: DataGenerator, API: API };