diff --git a/core/server/data/import/000.js b/core/server/data/import/000.js index 499556ef9a..b80bdf5805 100644 --- a/core/server/data/import/000.js +++ b/core/server/data/import/000.js @@ -7,15 +7,15 @@ var when = require('when'), Importer000 = function () { - _.bindAll(this, 'basicImport'); + _.bindAll(this, 'doImport'); this.version = '000'; this.importFrom = { - '000': this.basicImport, - '001': this.basicImport, - '002': this.basicImport, - '003': this.basicImport + '000': this.doImport, + '001': this.doImport, + '002': this.doImport, + '003': this.doImport }; }; @@ -36,73 +36,142 @@ Importer000.prototype.canImport = function (data) { return when.reject('Unsupported version of data: ' + data.meta.version); }; -// No data needs modifying, we just import whatever tables are available -Importer000.prototype.basicImport = function (data) { - var ops = [], - tableData = data.data; - return models.Base.transaction(function (t) { - // Do any pre-processing of relationships (we can't depend on ids) - if (tableData.posts_tags && tableData.posts && tableData.tags) { - tableData = utils.preProcessPostTags(tableData); - } +Importer000.prototype.loadUsers = function () { + var users = {all: {}}; - // Import things in the right order: - if (tableData.tags && tableData.tags.length) { - utils.importTags(ops, tableData.tags, t); - } - - if (tableData.posts && tableData.posts.length) { - utils.importPosts(ops, tableData.posts, t); - } - - // If we only have 1 user, behave as we always have done, overwriting properties, - // Else attempt to import users like any other resource, failing if there are clashes - if (tableData.users && tableData.users.length && tableData.users.length > 1) { - if (tableData.roles_users && tableData.roles_users.length) { - tableData = utils.preProcessRolesUsers(tableData); + return models.User.findAll({include: 'roles'}).then(function (_users) { + _users.forEach(function (user) { + users.all[user.get('email')] = {'realId': user.get('id')}; + if (user.related('roles').toJSON()[0] && user.related('roles').toJSON()[0].name === 'Owner') { + users.owner = user.toJSON(); } + }); - utils.importUsers(ops, tableData.users, t); - } else if (tableData.users && tableData.users.length) { - utils.importSingleUser(ops, tableData.users, t); + if (!users.owner) { + return when.reject('Unable to find an owner'); } - if (tableData.settings && tableData.settings.length) { - utils.importSettings(ops, tableData.settings, t); + return when.resolve(users); + }); +}; + +//Importer000.prototype.importerFunction = function (t) { +// +//}; + +Importer000.prototype.doUserImport = function (t, tableData, users, errors) { + var userOps = [], + imported = []; + + if (tableData.users && tableData.users.length) { + if (tableData.roles_users && tableData.roles_users.length) { + tableData = utils.preProcessRolesUsers(tableData); } + // Import users, deduplicating with already present users + userOps = utils.importUsers(tableData.users, users, t); - /** do nothing with these tables, the data shouldn't have changed from the fixtures - * permissions - * roles - * permissions_roles - * permissions_users - * roles_users - */ - - // Write changes to DB, if successful commit, otherwise rollback - // when.all() does not work as expected, when.settle() does. - when.settle(ops).then(function (descriptors) { - var errors = []; - + return when.settle(userOps).then(function (descriptors) { descriptors.forEach(function (d) { if (d.state === 'rejected') { errors = errors.concat(d.reason); + } else { + imported.push(d.value.toJSON()); } }); - if (errors.length === 0) { - t.commit(); - } else { + // If adding the users fails, + if (errors.length > 0) { t.rollback(errors); + } else { + return when.resolve(imported); } }); - }).then(function () { - //TODO: could return statistics of imported items - return when.resolve(); - }, function (errors) { - return when.reject(errors); + } + + return when.resolve({}); +}; + +Importer000.prototype.doImport = function (data) { + var self = this, + ops = [], + errors = [], + tableData = data.data, + imported = {}, + users = {}, + owner = {}; + + return self.loadUsers().then(function (result) { + owner = result.owner; + users = result.all; + + return models.Base.transaction(function (t) { + + // Step 1: Attempt to handle adding new users + self.doUserImport(t, tableData, users, errors).then(function (result) { + imported.users = result; + + _.each(imported.users, function (user) { + users[user.email] = {realId: user.id}; + }); + + // process user data - need to figure out what users we have available for assigning stuff to etc + try { + tableData = utils.processUsers(tableData, owner, users, ['posts', 'tags']); + } catch (error) { + return t.rollback([error]); + } + + // Do any pre-processing of relationships (we can't depend on ids) + if (tableData.posts_tags && tableData.posts && tableData.tags) { + tableData = utils.preProcessPostTags(tableData); + } + + // Import things in the right order: + if (tableData.tags && tableData.tags.length) { + utils.importTags(ops, tableData.tags, t); + } + + if (tableData.posts && tableData.posts.length) { + utils.importPosts(ops, tableData.posts, t); + } + + if (tableData.settings && tableData.settings.length) { + utils.importSettings(ops, tableData.settings, t); + } + + /** do nothing with these tables, the data shouldn't have changed from the fixtures + * permissions + * roles + * permissions_roles + * permissions_users + */ + + // Write changes to DB, if successful commit, otherwise rollback + // when.all() does not work as expected, when.settle() does. + when.settle(ops).then(function (descriptors) { + var errors = []; + + descriptors.forEach(function (d) { + if (d.state === 'rejected') { + errors = errors.concat(d.reason); + } + }); + + if (errors.length === 0) { + t.commit(); + } else { + t.rollback(errors); + } + }); + }); + }).then(function () { + //TODO: could return statistics of imported items + return when.resolve(); + }, function (error) { + return when.reject(error); + }); }); }; diff --git a/core/server/data/import/index.js b/core/server/data/import/index.js index e796995e2e..a3be3734ed 100644 --- a/core/server/data/import/index.js +++ b/core/server/data/import/index.js @@ -42,6 +42,10 @@ cleanError = function cleanError(error) { handleErrors = function handleErrors(errorList) { var processedErrors = []; + if (!_.isArray(errorList)) { + return when.reject(errorList); + } + _.each(errorList, function (error) { if (!error.raw) { // These are validation errors diff --git a/core/server/data/import/utils.js b/core/server/data/import/utils.js index d956eec387..7607115fd3 100644 --- a/core/server/data/import/utils.js +++ b/core/server/data/import/utils.js @@ -1,8 +1,10 @@ -var when = require('when'), - _ = require('lodash'), - models = require('../../models'), +var when = require('when'), + _ = require('lodash'), + models = require('../../models'), + errors = require('../../errors'), + globalUtils = require('../../utils'), - internal = {context: {internal: true}}, + internal = {context: {internal: true}}, utils, areEmpty, updatedSettingKeys, @@ -33,6 +35,72 @@ stripProperties = function stripProperties(properties, data) { }; utils = { + processUsers: function preProcessUsers(tableData, owner, existingUsers, objs) { + // We need to: + // 1. figure out who the owner of the blog is + // 2. figure out what users we have + // 3. figure out what users the import data refers to in foreign keys + // 4. try to map each one to a user + var userKeys = ['created_by', 'updated_by', 'published_by', 'author_id'], + userMap = {}; + + // Search the passed in objects for any user foreign keys + _.each(objs, function (obj) { + if (tableData[obj]) { + // For each object in the tableData that matches + _.each(tableData[obj], function (data) { + //console.log('checking ' + obj + ' ' + data.slug); + // For each possible user foreign key + _.each(userKeys, function (key) { + if (_.has(data, key) && data[key] !== null) { + //console.log('found ' + key + ' with value ' + data[key]); + userMap[data[key]] = {}; + } + }); + }); + } + }); + + // 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); + var foundUser = _.find(tableData.users, function (tableDataUser) { + return tableDataUser.id === userToMap; + }); + + // 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 (userToMap === 1) { + // if we don't have user data and the id is 1, we assume this means the owner + existingUsers[owner.email].importId = userToMap; + userMap[userToMap] = existingUsers[owner.email].realId; + } else { + throw new errors.DataImportError( + 'Attempting to import data linked to unknown user id ' + userToMap, 'user.id', userToMap + ); + } + }); + + // now replace any user foreign keys + _.each(objs, function (obj) { + if (tableData[obj]) { + // For each object in the tableData that matches + _.each(tableData[obj], function (data) { + // For each possible user foreign key + _.each(userKeys, function (key) { + if (_.has(data, key) && data[key] !== null) { + data[key] = userMap[data[key]]; + } + }); + }); + } + }); + + return tableData; + }, + preProcessPostTags: function preProcessPostTags(tableData) { var postTags, postsWithTags = {}; @@ -117,7 +185,9 @@ utils = { }); }, - importUsers: function importUsers(ops, tableData, transaction) { + importUsers: function importUsers(tableData, existingUsers, transaction) { + var ops = []; + tableData = stripProperties(['id'], tableData); _.each(tableData, function (user) { // Validate minimum user fields @@ -125,23 +195,23 @@ utils = { return; } + if (_.has(existingUsers, user.email)) { + // User is already present, ignore + return; + } + + // Set password to a random password, and lock the account + user.password = globalUtils.uid(50); + user.status = 'locked'; + ops.push(models.User.add(user, _.extend(internal, {transacting: transaction})) // add pass-through error handling so that bluebird doesn't think we've dropped it .catch(function (error) { return when.reject({raw: error, model: 'user', data: user}); })); }); - }, - importSingleUser: function importSingleUser(ops, tableData, transaction) { - // don't override the users credentials - tableData = stripProperties(['id', 'email', 'password'], tableData); - tableData[0].id = 1; - ops.push(models.User.edit(tableData[0], _.extend(internal, {id: 1, 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 ops; }, importSettings: function importSettings(ops, tableData, transaction) { diff --git a/core/server/models/post.js b/core/server/models/post.js index e36bfe9111..05e01a66ad 100644 --- a/core/server/models/post.js +++ b/core/server/models/post.js @@ -73,7 +73,9 @@ Post = ghostBookshelf.Model.extend({ this.set('published_at', new Date()); } // This will need to go elsewhere in the API layer. - this.set('published_by', this.contextUser(options)); + if (!this.get('published_by')) { + this.set('published_by', this.contextUser(options)); + } } if (this.hasChanged('slug') || !this.get('slug')) { diff --git a/core/test/functional/routes/api/users_test.js b/core/test/functional/routes/api/users_test.js index 93babd858a..d44b9ce0d7 100644 --- a/core/test/functional/routes/api/users_test.js +++ b/core/test/functional/routes/api/users_test.js @@ -133,7 +133,7 @@ describe('User API', function () { }); it('can retrieve a user by slug', function (done) { - request.get(testUtils.API.getApiQuery('users/slug/joe-blogs/')) + request.get(testUtils.API.getApiQuery('users/slug/joe-bloggs/')) .set('Authorization', 'Bearer ' + accesstoken) .expect('Content-Type', /json/) .expect(200) @@ -221,7 +221,7 @@ describe('User API', function () { }); it('can retrieve a user by slug with role and permissions', function (done) { - request.get(testUtils.API.getApiQuery('users/slug/joe-blogs/?include=roles,roles.permissions')) + request.get(testUtils.API.getApiQuery('users/slug/joe-bloggs/?include=roles,roles.permissions')) .set('Authorization', 'Bearer ' + accesstoken) .expect('Content-Type', /json/) .expect(200) diff --git a/core/test/integration/import_spec.js b/core/test/integration/import_spec.js index 7e4e510698..14d95e5ca7 100644 --- a/core/test/integration/import_spec.js +++ b/core/test/integration/import_spec.js @@ -1,4 +1,4 @@ -/*globals describe, before, beforeEach, afterEach, it*/ +/*globals describe, before, beforeEach, afterEach, after, it */ /*jshint expr:true*/ var testUtils = require('../utils/index'), should = require('should'), @@ -24,6 +24,7 @@ var testUtils = require('../utils/index'), knex, sandbox = sinon.sandbox.create(); +// Tests in here do an import for each test describe('Import', function () { before(testUtils.teardown); afterEach(testUtils.teardown); @@ -107,8 +108,10 @@ describe('Import', function () { }); describe('000', function () { - - beforeEach(testUtils.setup('owner', 'settings')); + before(function () { + knex = config.database.knex; + }); + beforeEach(testUtils.setup('roles', 'owner', 'settings')); should.exist(Importer000); @@ -161,7 +164,10 @@ describe('Import', function () { describe('001', function () { - beforeEach(testUtils.setup('owner', 'settings')); + before(function () { + knex = config.database.knex; + }); + beforeEach(testUtils.setup('roles', 'owner', 'settings')); should.exist(Importer001); @@ -206,7 +212,7 @@ describe('Import', function () { // 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); + should.not.exist(users[0].bio, 'bio is not imported'); // import no longer requires all data to be dropped, and adds posts posts.length.should.equal(exportData.data.posts.length, 'Wrong number of posts'); @@ -339,7 +345,10 @@ describe('Import', function () { describe('002', function () { - beforeEach(testUtils.setup('owner', 'settings')); + before(function () { + knex = config.database.knex; + }); + beforeEach(testUtils.setup('roles', 'owner', 'settings')); should.exist(Importer002); @@ -384,7 +393,7 @@ describe('Import', function () { // 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); + should.not.exist(users[0].bio, 'bio is not imported'); // import no longer requires all data to be dropped, and adds posts posts.length.should.equal(exportData.data.posts.length, 'Wrong number of posts'); @@ -413,9 +422,7 @@ describe('Import', function () { assert.equal(new Date(posts[0].published_at).getTime(), timestamp); done(); - }).catch(function (error) { - done(new Error(error)); - }); + }).catch(done); }); it('doesn\'t import invalid post data from 002', function (done) { @@ -515,8 +522,9 @@ describe('Import', function () { }); describe('003', function () { - knex = config.database.knex; - + before(function () { + knex = config.database.knex; + }); beforeEach(testUtils.setup('roles', 'owner', 'settings')); should.exist(Importer003); @@ -551,74 +559,7 @@ describe('Import', function () { // 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); - // test posts - posts.length.should.equal(1, 'Wrong number of posts'); - // test tags - tags.length.should.equal(1, 'no new tags'); - - // test settings - settings.length.should.be.above(0, 'Wrong number of settings'); - _.findWhere(settings, {key: 'databaseVersion'}).value.should.equal('003', 'Wrong database version'); - - done(); - }).catch(done); - }); - - it('safely imports data from 003 (multi user)', function (done) { - var exportData; - - testUtils.fixtures.loadExportFixture('export-003-mu').then(function (exported) { - exportData = exported; - return importer('003', exportData); - }).then(function () { - // Grab the data from tables - return when.all([ - knex('users').select(), - knex('posts').select(), - knex('settings').select(), - knex('tags').select(), - knex('roles_users').select() - ]); - }).then(function (importedData) { - should.exist(importedData); - var user2, - user3; - - importedData.length.should.equal(5, 'Did not get data successfully'); - - var users = importedData[0], - posts = importedData[1], - settings = importedData[2], - tags = importedData[3], - roles_users = importedData[4]; - - // we imported 2 users, the original user should be untouched - users.length.should.equal(3, 'There should only be three users'); - users[0].email.should.equal(testUtils.DataGenerator.Content.users[0].email); - users[0].password.should.equal(testUtils.DataGenerator.Content.users[0].password); - - // the other two users should have the imported data, but they get inserted in different orders - user2 = _.find(users, function (user) { - return user.name === 'Josephine Bloggs'; - }); - user3 = _.find(users, function (user) { - return user.name === 'Smith Wellingsworth'; - }); - user2.email.should.equal(exportData.data.users[0].email); - user3.email.should.equal(exportData.data.users[1].email); - - roles_users.length.should.equal(2, 'There should be 3 role relations'); - - _.each(roles_users, function (roleUser) { - if (roleUser.user_id === user2.id) { - roleUser.role_id.should.equal(1, 'Josephine should be an admin'); - } - - if (roleUser.user_id === user3.id) { - roleUser.role_id.should.equal(3, 'Smith should be an author by default'); - } - }); + should.not.exist(users[0].bio, 'bio is not imported'); // test posts posts.length.should.equal(1, 'Wrong number of posts'); @@ -639,6 +580,8 @@ describe('Import', function () { testUtils.fixtures.loadExportFixture('export-003-badValidation').then(function (exported) { exportData = exported; return importer('003', exportData); + }).then(function () { + done(new Error('Allowed import of duplicate data')); }).catch(function (response) { response.length.should.equal(5); response[0].type.should.equal('ValidationError'); @@ -660,6 +603,8 @@ describe('Import', function () { testUtils.fixtures.loadExportFixture('export-003-dbErrors').then(function (exported) { exportData = exported; return importer('003', exportData); + }).then(function () { + done(new Error('Allowed import of duplicate data')); }).catch(function (response) { response.length.should.equal(3); response[0].type.should.equal('DataImportError'); @@ -678,6 +623,24 @@ describe('Import', function () { }).catch(done); }); + it('doesn\'t import posts with an invalid author', function (done) { + var exportData; + + testUtils.fixtures.loadExportFixture('export-003-mu-unknownAuthor').then(function (exported) { + exportData = exported; + + return importer('003', exportData); + }).then(function () { + done(new Error('Allowed import of unknown author')); + }).catch(function (response) { + response.length.should.equal(1); + response[0].message.should.eql('Attempting to import data linked to unknown user id 2'); + response[0].type.should.equal('DataImportError'); + + done(); + }).catch(done); + }); + it('doesn\'t import invalid tags data from 003', function (done) { var exportData; @@ -722,3 +685,704 @@ describe('Import', function () { }); }); }); + + +// Tests in here do an import-per-describe, and then have several tests to check various bits of data +describe('Import (new test structure)', function () { + before(testUtils.teardown); + + describe('003', function () { + + after(testUtils.teardown); + + should.exist(Importer003); + + describe('imports multi user data onto blank ghost install', function () { + var exportData; + + before(function doImport(done) { + knex = config.database.knex; + + testUtils.initFixtures('roles', 'owner', 'settings').then(function () { + return testUtils.fixtures.loadExportFixture('export-003-mu'); + }).then(function (exported) { + exportData = exported; + return importer('003', exportData); + }).then(function () { + done(); + }).catch(done); + }); + after(testUtils.teardown); + + it('gets the right data', function (done) { + var fetchImported = when.join( + knex('posts').select(), + knex('settings').select(), + knex('tags').select() + ); + + fetchImported.then(function (importedData) { + var posts, + settings, + tags, + post1, + post2, + post3; + + // General data checks + should.exist(importedData); + importedData.length.should.equal(3, 'Did not get data successfully'); + + // Test posts, settings and tags + posts = importedData[0]; + settings = importedData[1]; + tags = importedData[2]; + + post1 = _.find(posts, function (post) { + return post.slug === exportData.data.posts[0].slug; + }); + post2 = _.find(posts, function (post) { + return post.slug === exportData.data.posts[1].slug; + }); + post3 = _.find(posts, function (post) { + return post.slug === exportData.data.posts[2].slug; + }); + + // test posts + posts.length.should.equal(3, 'Wrong number of posts'); + post1.title.should.equal(exportData.data.posts[0].title); + post2.title.should.equal(exportData.data.posts[1].title); + post3.title.should.equal(exportData.data.posts[2].title); + + // test tags + tags.length.should.equal(3, 'should be 3 tags'); + + // test settings + settings.length.should.be.above(0, 'Wrong number of settings'); + _.findWhere(settings, {key: 'databaseVersion'}).value.should.equal('003', 'Wrong database version'); + + done(); + }).catch(done); + }); + + it('imports users with correct roles and status', function (done) { + var fetchImported = when.join( + knex('users').select(), + knex('roles_users').select() + ); + + fetchImported.then(function (importedData) { + var user1, + user2, + user3, + users, + roles_users; + + // General data checks + should.exist(importedData); + importedData.length.should.equal(2, 'Did not get data successfully'); + + // Test the users and roles + users = importedData[0]; + roles_users = importedData[1]; + + // we imported 3 users + // the original user should be untouched + // the two news users should have been created + users.length.should.equal(3, 'There should only be three users'); + + // the owner user is first + user1 = users[0]; + // the other two users should have the imported data, but they get inserted in different orders + user2 = _.find(users, function (user) { + return user.name === exportData.data.users[1].name; + }); + user3 = _.find(users, function (user) { + return user.name === exportData.data.users[2].name; + }); + + user1.email.should.equal(testUtils.DataGenerator.Content.users[0].email); + user1.password.should.equal(testUtils.DataGenerator.Content.users[0].password); + user1.status.should.equal('active'); + user2.email.should.equal(exportData.data.users[1].email); + user3.email.should.equal(exportData.data.users[2].email); + + // Newly created users should have a status of locked + user2.status.should.equal('locked'); + user3.status.should.equal('locked'); + + // Newly created users should have created_at/_by and updated_at/_by set to when they were imported + user2.created_by.should.equal(user1.id); + user2.created_at.should.not.equal(exportData.data.users[1].created_at); + user2.updated_by.should.equal(user1.id); + user2.updated_at.should.not.equal(exportData.data.users[1].updated_at); + user3.created_by.should.equal(user1.id); + user3.created_at.should.not.equal(exportData.data.users[2].created_at); + user3.updated_by.should.equal(user1.id); + user3.updated_at.should.not.equal(exportData.data.users[2].updated_at); + + roles_users.length.should.equal(3, 'There should be 3 role relations'); + + _.each(roles_users, function (roleUser) { + if (roleUser.user_id === user1.id) { + roleUser.role_id.should.equal(4, 'Original user should be an owner'); + } + if (roleUser.user_id === user2.id) { + roleUser.role_id.should.equal(1, 'Josephine should be an admin'); + } + if (roleUser.user_id === user3.id) { + roleUser.role_id.should.equal(3, 'Smith should be an author by default'); + } + }); + + done(); + }).catch(done); + }); + + it('imports posts & tags with correct authors, owners etc', function (done) { + var fetchImported = when.join( + knex('users').select(), + knex('posts').select(), + knex('tags').select() + ); + + fetchImported.then(function (importedData) { + var users, user1, user2, user3, + posts, post1, post2, post3, + tags, tag1, tag2, tag3; + + // General data checks + should.exist(importedData); + importedData.length.should.equal(3, 'Did not get data successfully'); + + // Test the users and roles + users = importedData[0]; + posts = importedData[1]; + tags = importedData[2]; + + // Grab the users + // the owner user is first + user1 = users[0]; + // the other two users should have the imported data, but they get inserted in different orders + user2 = _.find(users, function (user) { + return user.name === exportData.data.users[1].name; + }); + user3 = _.find(users, function (user) { + return user.name === exportData.data.users[2].name; + }); + post1 = _.find(posts, function (post) { + return post.slug === exportData.data.posts[0].slug; + }); + post2 = _.find(posts, function (post) { + return post.slug === exportData.data.posts[1].slug; + }); + post3 = _.find(posts, function (post) { + return post.slug === exportData.data.posts[2].slug; + }); + tag1 = _.find(tags, function (tag) { + return tag.slug === exportData.data.tags[0].slug; + }); + tag2 = _.find(tags, function (tag) { + return tag.slug === exportData.data.tags[1].slug; + }); + tag3 = _.find(tags, function (tag) { + return tag.slug === exportData.data.tags[2].slug; + }); + + //Check the authors are correct + post1.author_id.should.equal(user2.id); + post2.author_id.should.equal(user3.id); + post3.author_id.should.equal(user1.id); + + // Created by should be what was in the import file + post1.created_by.should.equal(user1.id); + post2.created_by.should.equal(user3.id); + post3.created_by.should.equal(user1.id); + + // Updated by gets set to the current user + post1.updated_by.should.equal(user1.id); + post2.updated_by.should.equal(user1.id); + post3.updated_by.should.equal(user1.id); + + // Published by should be what was in the import file + post1.published_by.should.equal(user2.id); + post2.published_by.should.equal(user3.id); + post3.published_by.should.equal(user1.id); + + // Created by should be what was in the import file + tag1.created_by.should.equal(user1.id); + tag2.created_by.should.equal(user2.id); + tag3.created_by.should.equal(user3.id); + + // Updated by gets set to the current user + tag1.updated_by.should.equal(user1.id); + tag2.updated_by.should.equal(user1.id); + tag3.updated_by.should.equal(user1.id); + + done(); + }).catch(done); + }); + }); + + describe('imports multi user data with no owner onto blank ghost install', function () { + var exportData; + + before(function doImport(done) { + knex = config.database.knex; + + testUtils.initFixtures('roles', 'owner', 'settings').then(function () { + return testUtils.fixtures.loadExportFixture('export-003-mu-noOwner'); + }).then(function (exported) { + exportData = exported; + return importer('003', exportData); + }).then(function () { + done(); + }).catch(done); + }); + after(testUtils.teardown); + + it('gets the right data', function (done) { + var fetchImported = when.join( + knex('posts').select(), + knex('settings').select(), + knex('tags').select() + ); + + fetchImported.then(function (importedData) { + var posts, + settings, + tags, + post1, + post2, + post3; + + // General data checks + should.exist(importedData); + importedData.length.should.equal(3, 'Did not get data successfully'); + + // Test posts, settings and tags + posts = importedData[0]; + settings = importedData[1]; + tags = importedData[2]; + + post1 = _.find(posts, function (post) { + return post.slug === exportData.data.posts[0].slug; + }); + post2 = _.find(posts, function (post) { + return post.slug === exportData.data.posts[1].slug; + }); + post3 = _.find(posts, function (post) { + return post.slug === exportData.data.posts[2].slug; + }); + + // test posts + posts.length.should.equal(3, 'Wrong number of posts'); + post1.title.should.equal(exportData.data.posts[0].title); + post2.title.should.equal(exportData.data.posts[1].title); + post3.title.should.equal(exportData.data.posts[2].title); + + // test tags + tags.length.should.equal(3, 'should be 3 tags'); + + // test settings + settings.length.should.be.above(0, 'Wrong number of settings'); + _.findWhere(settings, {key: 'databaseVersion'}).value.should.equal('003', 'Wrong database version'); + + done(); + }).catch(done); + }); + + it('imports users with correct roles and status', function (done) { + var fetchImported = when.join( + knex('users').select(), + knex('roles_users').select() + ); + + fetchImported.then(function (importedData) { + var user1, + user2, + user3, + users, + roles_users; + + // General data checks + should.exist(importedData); + importedData.length.should.equal(2, 'Did not get data successfully'); + + // Test the users and roles + users = importedData[0]; + roles_users = importedData[1]; + + // we imported 3 users + // the original user should be untouched + // the two news users should have been created + users.length.should.equal(3, 'There should only be three users'); + + // the owner user is first + user1 = users[0]; + // the other two users should have the imported data, but they get inserted in different orders + user2 = _.find(users, function (user) { + return user.name === exportData.data.users[0].name; + }); + user3 = _.find(users, function (user) { + return user.name === exportData.data.users[1].name; + }); + + user1.email.should.equal(testUtils.DataGenerator.Content.users[0].email); + user1.password.should.equal(testUtils.DataGenerator.Content.users[0].password); + user1.status.should.equal('active'); + user2.email.should.equal(exportData.data.users[0].email); + user3.email.should.equal(exportData.data.users[1].email); + + // Newly created users should have a status of locked + user2.status.should.equal('locked'); + user3.status.should.equal('locked'); + + // Newly created users should have created_at/_by and updated_at/_by set to when they were imported + user2.created_by.should.equal(user1.id); + user2.created_at.should.not.equal(exportData.data.users[0].created_at); + user2.updated_by.should.equal(user1.id); + user2.updated_at.should.not.equal(exportData.data.users[0].updated_at); + user3.created_by.should.equal(user1.id); + user3.created_at.should.not.equal(exportData.data.users[1].created_at); + user3.updated_by.should.equal(user1.id); + user3.updated_at.should.not.equal(exportData.data.users[1].updated_at); + + roles_users.length.should.equal(3, 'There should be 3 role relations'); + + _.each(roles_users, function (roleUser) { + if (roleUser.user_id === user1.id) { + roleUser.role_id.should.equal(4, 'Original user should be an owner'); + } + if (roleUser.user_id === user2.id) { + roleUser.role_id.should.equal(1, 'Josephine should be an admin'); + } + if (roleUser.user_id === user3.id) { + roleUser.role_id.should.equal(3, 'Smith should be an author by default'); + } + }); + + done(); + }).catch(done); + }); + + it('imports posts & tags with correct authors, owners etc', function (done) { + var fetchImported = when.join( + knex('users').select(), + knex('posts').select(), + knex('tags').select() + ); + + fetchImported.then(function (importedData) { + var users, user1, user2, user3, + posts, post1, post2, post3, + tags, tag1, tag2, tag3; + + // General data checks + should.exist(importedData); + importedData.length.should.equal(3, 'Did not get data successfully'); + + // Test the users and roles + users = importedData[0]; + posts = importedData[1]; + tags = importedData[2]; + + // Grab the users + // the owner user is first + user1 = users[0]; + // the other two users should have the imported data, but they get inserted in different orders + user2 = _.find(users, function (user) { + return user.name === exportData.data.users[0].name; + }); + user3 = _.find(users, function (user) { + return user.name === exportData.data.users[1].name; + }); + post1 = _.find(posts, function (post) { + return post.slug === exportData.data.posts[0].slug; + }); + post2 = _.find(posts, function (post) { + return post.slug === exportData.data.posts[1].slug; + }); + post3 = _.find(posts, function (post) { + return post.slug === exportData.data.posts[2].slug; + }); + tag1 = _.find(tags, function (tag) { + return tag.slug === exportData.data.tags[0].slug; + }); + tag2 = _.find(tags, function (tag) { + return tag.slug === exportData.data.tags[1].slug; + }); + tag3 = _.find(tags, function (tag) { + return tag.slug === exportData.data.tags[2].slug; + }); + + //Check the authors are correct + post1.author_id.should.equal(user2.id); + post2.author_id.should.equal(user3.id); + post3.author_id.should.equal(user1.id); + + // Created by should be what was in the import file + post1.created_by.should.equal(user1.id); + post2.created_by.should.equal(user3.id); + post3.created_by.should.equal(user1.id); + + // Updated by gets set to the current user + post1.updated_by.should.equal(user1.id); + post2.updated_by.should.equal(user1.id); + post3.updated_by.should.equal(user1.id); + + // Published by should be what was in the import file + post1.published_by.should.equal(user2.id); + post2.published_by.should.equal(user3.id); + post3.published_by.should.equal(user1.id); + + // Created by should be what was in the import file + tag1.created_by.should.equal(user1.id); + tag2.created_by.should.equal(user2.id); + tag3.created_by.should.equal(user3.id); + + // Updated by gets set to the current user + tag1.updated_by.should.equal(user1.id); + tag2.updated_by.should.equal(user1.id); + tag3.updated_by.should.equal(user1.id); + + done(); + }).catch(done); + }); + }); + + describe('imports multi user data onto existing data', function () { + var exportData; + + before(function doImport(done) { + knex = config.database.knex; + + // initialise the blog with some data + testUtils.initFixtures('users:roles', 'posts', 'settings').then(function () { + return testUtils.fixtures.loadExportFixture('export-003-mu'); + }).then(function (exported) { + exportData = exported; + return importer('003', exportData); + }).then(function () { + done(); + }).catch(done); + }); + after(testUtils.teardown); + + it('gets the right data', function (done) { + var fetchImported = when.join( + knex('posts').select(), + knex('settings').select(), + knex('tags').select() + ); + + fetchImported.then(function (importedData) { + var posts, + settings, + tags, + post1, + post2, + post3; + + // General data checks + should.exist(importedData); + importedData.length.should.equal(3, 'Did not get data successfully'); + + // Test posts, settings and tags + posts = importedData[0]; + settings = importedData[1]; + tags = importedData[2]; + + post1 = _.find(posts, function (post) { + return post.slug === exportData.data.posts[0].slug; + }); + post2 = _.find(posts, function (post) { + return post.slug === exportData.data.posts[1].slug; + }); + post3 = _.find(posts, function (post) { + return post.slug === exportData.data.posts[2].slug; + }); + + // test posts + posts.length.should.equal( + (exportData.data.posts.length + testUtils.DataGenerator.Content.posts.length), + 'Wrong number of posts' + ); + + posts[0].title.should.equal(testUtils.DataGenerator.Content.posts[0].title); + + post1.title.should.equal(exportData.data.posts[0].title); + post2.title.should.equal(exportData.data.posts[1].title); + post3.title.should.equal(exportData.data.posts[2].title); + + // test tags + tags.length.should.equal( + (exportData.data.tags.length + testUtils.DataGenerator.Content.tags.length), + 'Wrong number of tags' + ); + + tags[0].name.should.equal(testUtils.DataGenerator.Content.tags[0].name); + + // test settings + settings.length.should.be.above(0, 'Wrong number of settings'); + _.findWhere(settings, {key: 'databaseVersion'}).value.should.equal('003', 'Wrong database version'); + + done(); + }).catch(done); + }); + + it('imports users with correct roles and status', function (done) { + var fetchImported = when.join( + knex('users').select(), + knex('roles_users').select() + ); + + fetchImported.then(function (importedData) { + var ownerUser, + newUser, + existingUser, + users, + roles_users; + + // General data checks + should.exist(importedData); + importedData.length.should.equal(2, 'Did not get data successfully'); + + // Test the users and roles + users = importedData[0]; + roles_users = importedData[1]; + + // we imported 3 users, there were already 4 users, only one of the imported users is new + users.length.should.equal(5, 'There should only be three users'); + + // the owner user is first + ownerUser = users[0]; + // the other two users should have the imported data, but they get inserted in different orders + newUser = _.find(users, function (user) { + return user.name === exportData.data.users[1].name; + }); + existingUser = _.find(users, function (user) { + return user.name === exportData.data.users[2].name; + }); + + ownerUser.email.should.equal(testUtils.DataGenerator.Content.users[0].email); + ownerUser.password.should.equal(testUtils.DataGenerator.Content.users[0].password); + ownerUser.status.should.equal('active'); + newUser.email.should.equal(exportData.data.users[1].email); + existingUser.email.should.equal(exportData.data.users[2].email); + + // Newly created users should have a status of locked + newUser.status.should.equal('locked'); + // The already existing user should still have a status of active + existingUser.status.should.equal('active'); + + // Newly created users should have created_at/_by and updated_at/_by set to when they were imported + newUser.created_by.should.equal(ownerUser.id); + newUser.created_at.should.not.equal(exportData.data.users[1].created_at); + newUser.updated_by.should.equal(ownerUser.id); + newUser.updated_at.should.not.equal(exportData.data.users[1].updated_at); + + roles_users.length.should.equal(5, 'There should be 5 role relations'); + + _.each(roles_users, function (roleUser) { + if (roleUser.user_id === ownerUser.id) { + roleUser.role_id.should.equal(4, 'Original user should be an owner'); + } + if (roleUser.user_id === newUser.id) { + roleUser.role_id.should.equal(1, 'New user should be an admin'); + } + if (roleUser.user_id === existingUser.id) { + roleUser.role_id.should.equal(1, 'Existing user was an admin'); + } + }); + + done(); + }).catch(done); + }); + + it('imports posts & tags with correct authors, owners etc', function (done) { + var fetchImported = when.join( + knex('users').select(), + knex('posts').select(), + knex('tags').select() + ); + + fetchImported.then(function (importedData) { + var users, ownerUser, newUser, existingUser, + posts, post1, post2, post3, + tags, tag1, tag2, tag3; + + // General data checks + should.exist(importedData); + importedData.length.should.equal(3, 'Did not get data successfully'); + + // Test the users and roles + users = importedData[0]; + posts = importedData[1]; + tags = importedData[2]; + + // Grab the users + // the owner user is first + ownerUser = users[0]; + // the other two users should have the imported data, but they get inserted in different orders + newUser = _.find(users, function (user) { + return user.name === exportData.data.users[1].name; + }); + existingUser = _.find(users, function (user) { + return user.name === exportData.data.users[2].name; + }); + post1 = _.find(posts, function (post) { + return post.slug === exportData.data.posts[0].slug; + }); + post2 = _.find(posts, function (post) { + return post.slug === exportData.data.posts[1].slug; + }); + post3 = _.find(posts, function (post) { + return post.slug === exportData.data.posts[2].slug; + }); + tag1 = _.find(tags, function (tag) { + return tag.slug === exportData.data.tags[0].slug; + }); + tag2 = _.find(tags, function (tag) { + return tag.slug === exportData.data.tags[1].slug; + }); + tag3 = _.find(tags, function (tag) { + return tag.slug === exportData.data.tags[2].slug; + }); + + //Check the authors are correct + post1.author_id.should.equal(newUser.id); + post2.author_id.should.equal(existingUser.id); + post3.author_id.should.equal(ownerUser.id); + + // Created by should be what was in the import file + post1.created_by.should.equal(ownerUser.id); + post2.created_by.should.equal(existingUser.id); + post3.created_by.should.equal(ownerUser.id); + + // Updated by gets set to the current user + post1.updated_by.should.equal(ownerUser.id); + post2.updated_by.should.equal(ownerUser.id); + post3.updated_by.should.equal(ownerUser.id); + + // Published by should be what was in the import file + post1.published_by.should.equal(newUser.id); + post2.published_by.should.equal(existingUser.id); + post3.published_by.should.equal(ownerUser.id); + + // Created by should be what was in the import file + tag1.created_by.should.equal(ownerUser.id); + tag2.created_by.should.equal(newUser.id); + tag3.created_by.should.equal(existingUser.id); + + // Updated by gets set to the current user + tag1.updated_by.should.equal(ownerUser.id); + tag2.updated_by.should.equal(ownerUser.id); + tag3.updated_by.should.equal(ownerUser.id); + + done(); + }).catch(done); + }); + }); + }); +}); diff --git a/core/test/utils/fixtures/data-generator.js b/core/test/utils/fixtures/data-generator.js index 079457a9ac..01db8c76cb 100644 --- a/core/test/utils/fixtures/data-generator.js +++ b/core/test/utils/fixtures/data-generator.js @@ -73,7 +73,7 @@ DataGenerator.Content = { users: [ { name: 'Joe Bloggs', - slug: 'joe-blogs', + slug: 'joe-bloggs', email: 'jbloggs@example.com', password: '$2a$10$.pZeeBE0gHXd0PTnbT/ph.GEKgd0Wd3q2pWna3ynTGBkPKnGIKZL6' }, diff --git a/core/test/utils/fixtures/export-001.json b/core/test/utils/fixtures/export-001.json index 9ad624a0df..54d6dad9c3 100644 --- a/core/test/utils/fixtures/export-001.json +++ b/core/test/utils/fixtures/export-001.json @@ -32,10 +32,10 @@ { "id": 1, "uuid": "e5188224-4742-4c32-a2d6-e9c5c5d4c123", - "name": "Josephine Bloggs", - "slug": "josephine-blogs", + "name": "Joe Bloggs", + "slug": "joe-bloggs", "password": "$2a$10$.pZeeBE0gHXd0PTnbT/ph.GEKgd0Wd3q2pWna3ynTGBkPKnGIKABC", - "email": "josephinebloggs@example.com", + "email": "jbloggs@example.com", "image": null, "cover": null, "bio": "A blogger", diff --git a/core/test/utils/fixtures/export-002.json b/core/test/utils/fixtures/export-002.json index e0181d4cb0..813c1f8dfa 100644 --- a/core/test/utils/fixtures/export-002.json +++ b/core/test/utils/fixtures/export-002.json @@ -32,10 +32,10 @@ { "id": 1, "uuid": "e5188224-4742-4c32-a2d6-e9c5c5d4c123", - "name": "Josephine Bloggs", - "slug": "josephine-blogs", + "name": "Joe Bloggs", + "slug": "joe-bloggs", "password": "$2a$10$.pZeeBE0gHXd0PTnbT/ph.GEKgd0Wd3q2pWna3ynTGBkPKnGIKABC", - "email": "josephinebloggs@example.com", + "email": "jbloggs@example.com", "image": null, "cover": null, "bio": "A blogger", diff --git a/core/test/utils/fixtures/export-003-mu-noOwner.json b/core/test/utils/fixtures/export-003-mu-noOwner.json new file mode 100644 index 0000000000..e04afcddf9 --- /dev/null +++ b/core/test/utils/fixtures/export-003-mu-noOwner.json @@ -0,0 +1,458 @@ +{ + "meta": { + "exported_on": 1388318311015, + "version": "003" + }, + "data": { + "posts": [ + { + "id": 1, + "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": 2, + "created_at": 1388318310782, + "created_by": 1, + "updated_at": 1388318310782, + "updated_by": 1, + "published_at": 1388318310783, + "published_by": 2 + }, + { + "id": 2, + "title": "Brand New Post", + "slug": "brand-new-post", + "markdown": "

HTML Ipsum Presents

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. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. Donec non enim in turpis pulvinar facilisis. Ut felis.

Header Level 2

  1. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
  2. Aliquam tincidunt mauris eu risus.

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.

Header Level 3

#header h1 a{display: block;width: 300px;height: 80px;}
", + "image": null, + "featured": 0, + "page": 0, + "status": "published", + "language": "en_US", + "meta_title": null, + "meta_description": null, + "author_id": 3, + "created_at": 1388318310782, + "created_by": 3, + "updated_at": 1388318310782, + "updated_by": 3, + "published_at": 1388318310783, + "published_by": 3 + }, + { + "id": 3, + "title": "Shiny happy bloggers", + "slug": "shiny-happy-bloggers", + "markdown": "

HTML Ipsum Presents

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. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. Donec non enim in turpis pulvinar facilisis. Ut felis.

Header Level 2

  1. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
  2. Aliquam tincidunt mauris eu risus.

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.

Header Level 3

#header h1 a{display: block;width: 300px;height: 80px;}
", + "image": null, + "featured": 0, + "page": 0, + "status": "draft", + "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": 2, + "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 + }, + { + "id": 3, + "name": "Smith Wellingsworth", + "slug": "smith-wellingsworth", + "email": "swellingsworth@example.com", + "image": null, + "cover": null, + "bio": null, + "website": null, + "location": null, + "accessibility": null, + "status": "invited", + "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": 3, + "user_id": 1 + }, + { + "id": 1, + "role_id": 1, + "user_id": 2 + } + ], + "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": "activeApps", + "value": "[]", + "type": "plugin", + "created_at": 1388318310831, + "created_by": 1, + "updated_at": 1388318310831, + "updated_by": 1 + }, + { + "id": 14, + "uuid": "2ea560a3-2304-449d-a62b-f7b622987510", + "key": "installedApps", + "value": "[]", + "type": "plugin", + "created_at": 1388318310831, + "created_by": 1, + "updated_at": 1388318310831, + "updated_by": 1 + } + ], + "tags": [ + { + "id": 1, + "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 + }, + { + "id": 2, + "name": "Another tag", + "slug": "another-tag", + "description": null, + "parent_id": null, + "meta_title": null, + "meta_description": null, + "created_at": 1388318310790, + "created_by": 2, + "updated_at": 1388318310790, + "updated_by": 2 + }, + { + "id": 3, + "name": "Taggy goodness", + "slug": "taggy-goodness", + "description": null, + "parent_id": null, + "meta_title": null, + "meta_description": null, + "created_at": 1388318310790, + "created_by": 3, + "updated_at": 1388318310790, + "updated_by": 1 + } + ], + "posts_tags": [ + { + "id": 1, + "post_id": 1, + "tag_id": 1 + } + ], + "apps": [ + { + "id": 1, + "uuid": "4d7557f0-0949-4946-9fe8-ec030e0727f0", + "name": "Kudos", + "slug": "kudos", + "version": "0.0.1", + "status": "installed", + "created_at": 1388318312790, + "created_by": 1, + "updated_at": 1388318312790, + "updated_by": 1 + } + ], + "app_settings": [ + { + "id": 1, + "uuid": "790e4551-b9cc-4954-8f5d-b6e651bc7342", + "key": "position", + "value": "bottom", + "app_id": 1, + "created_at": 1388318312790, + "created_by": 1, + "updated_at": 1388318312790, + "updated_by": 1 + }, + { + "id": 2, + "uuid": "29682b66-cdeb-4773-9821-bcf40ea93b58", + "key": "size", + "value": "60", + "app_id": 1, + "created_at": 1388318312790, + "created_by": 1, + "updated_at": 1388318312790, + "updated_by": 1 + } + ] + } +} \ No newline at end of file diff --git a/core/test/utils/fixtures/export-003-mu-unknownAuthor.json b/core/test/utils/fixtures/export-003-mu-unknownAuthor.json new file mode 100644 index 0000000000..c2250deac2 --- /dev/null +++ b/core/test/utils/fixtures/export-003-mu-unknownAuthor.json @@ -0,0 +1,352 @@ +{ + "meta": { + "exported_on": 1388318311015, + "version": "003" + }, + "data": { + "posts": [ + { + "id": 1, + "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": 2, + "created_at": 1388318310782, + "created_by": 1, + "updated_at": 1388318310782, + "updated_by": 1, + "published_at": 1388318310783, + "published_by": 2 + } + ], + "users": [ + { + "id": 1, + "name": "Joe Bloggs", + "slug": "joe-blogs", + "email": "jbloggs@example.com", + "password": "$2a$10$.pZeeBE0gHXd0PTnbT/ph.GEKgd0Wd3q2pWna3ynTGBkPKnGIKZL6", + "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": 1388319432895, + "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": 3, + "user_id": 1 + }, + { + "id": 1, + "role_id": 1, + "user_id": 2 + } + ], + "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": "activeApps", + "value": "[]", + "type": "plugin", + "created_at": 1388318310831, + "created_by": 1, + "updated_at": 1388318310831, + "updated_by": 1 + }, + { + "id": 14, + "uuid": "2ea560a3-2304-449d-a62b-f7b622987510", + "key": "installedApps", + "value": "[]", + "type": "plugin", + "created_at": 1388318310831, + "created_by": 1, + "updated_at": 1388318310831, + "updated_by": 1 + } + ], + "tags": [ + { + "id": 1, + "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 + }, + { + "id": 2, + "name": "Another tag", + "slug": "another-tag", + "description": null, + "parent_id": null, + "meta_title": null, + "meta_description": null, + "created_at": 1388318310790, + "created_by": 2, + "updated_at": 1388318310790, + "updated_by": 2 + }, + { + "id": 3, + "name": "Taggy goodness", + "slug": "taggy-goodness", + "description": null, + "parent_id": null, + "meta_title": null, + "meta_description": null, + "created_at": 1388318310790, + "created_by": 3, + "updated_at": 1388318310790, + "updated_by": 1 + } + ] + } +} \ No newline at end of file diff --git a/core/test/utils/fixtures/export-003-mu.json b/core/test/utils/fixtures/export-003-mu.json index 00394a1ddb..f0a9a171bc 100644 --- a/core/test/utils/fixtures/export-003-mu.json +++ b/core/test/utils/fixtures/export-003-mu.json @@ -7,7 +7,6 @@ "posts": [ { "id": 1, - "uuid": "8492fbba-1102-4b53-8e3e-abe207952f0c", "title": "Welcome to Ghost", "slug": "welcome-to-ghost", "markdown": "You're live! Nice.", @@ -19,6 +18,46 @@ "language": "en_US", "meta_title": null, "meta_description": null, + "author_id": 2, + "created_at": 1388318310782, + "created_by": 1, + "updated_at": 1388318310782, + "updated_by": 1, + "published_at": 1388318310783, + "published_by": 2 + }, + { + "id": 2, + "title": "Brand New Post", + "slug": "brand-new-post", + "markdown": "

HTML Ipsum Presents

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. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. Donec non enim in turpis pulvinar facilisis. Ut felis.

Header Level 2

  1. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
  2. Aliquam tincidunt mauris eu risus.

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.

Header Level 3

#header h1 a{display: block;width: 300px;height: 80px;}
", + "image": null, + "featured": 0, + "page": 0, + "status": "published", + "language": "en_US", + "meta_title": null, + "meta_description": null, + "author_id": 3, + "created_at": 1388318310782, + "created_by": 3, + "updated_at": 1388318310782, + "updated_by": 3, + "published_at": 1388318310783, + "published_by": 3 + }, + { + "id": 3, + "title": "Shiny happy bloggers", + "slug": "shiny-happy-bloggers", + "markdown": "

HTML Ipsum Presents

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. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. Donec non enim in turpis pulvinar facilisis. Ut felis.

Header Level 2

  1. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
  2. Aliquam tincidunt mauris eu risus.

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.

Header Level 3

#header h1 a{display: block;width: 300px;height: 80px;}
", + "image": null, + "featured": 0, + "page": 0, + "status": "draft", + "language": "en_US", + "meta_title": null, + "meta_description": null, "author_id": 1, "created_at": 1388318310782, "created_by": 1, @@ -31,7 +70,28 @@ "users": [ { "id": 1, - "uuid": "e5188224-4742-4c32-a2d6-e9c5c5d4c123", + "name": "Joe Bloggs", + "slug": "joe-blogs", + "email": "jbloggs@example.com", + "password": "$2a$10$.pZeeBE0gHXd0PTnbT/ph.GEKgd0Wd3q2pWna3ynTGBkPKnGIKZL6", + "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": 1388319432895, + "created_by": 1, + "updated_at": null, + "updated_by": null + }, + { + "id": 2, "name": "Josephine Bloggs", "slug": "josephine-blogs", "password": "$2a$10$.pZeeBE0gHXd0PTnbT/ph.GEKgd0Wd3q2pWna3ynTGBkPKnGIKABC", @@ -53,12 +113,10 @@ "updated_by": null }, { - "id": 2, - "uuid": "e5188224-4742-4c32-a2d6-e9c5c5d4c456", + "id": 3, "name": "Smith Wellingsworth", "slug": "smith-wellingsworth", "email": "swellingsworth@example.com", - "password": "$2a$10$.pZeeBE0gHXd0PTnbT/ph.GEKgd0Wd3q2pWna3ynTGBkPKnGIKZL6", "image": null, "cover": null, "bio": null, @@ -111,8 +169,13 @@ "roles_users": [ { "id": 1, - "role_id": 1, + "role_id": 3, "user_id": 1 + }, + { + "id": 1, + "role_id": 1, + "user_id": 2 } ], "permissions": [ @@ -330,7 +393,6 @@ "tags": [ { "id": 1, - "uuid": "a950117a-9735-4584-931d-25a28015a80d", "name": "Getting Started", "slug": "getting-started", "description": null, @@ -341,6 +403,32 @@ "created_by": 1, "updated_at": 1388318310790, "updated_by": 1 + }, + { + "id": 2, + "name": "Another tag", + "slug": "another-tag", + "description": null, + "parent_id": null, + "meta_title": null, + "meta_description": null, + "created_at": 1388318310790, + "created_by": 2, + "updated_at": 1388318310790, + "updated_by": 2 + }, + { + "id": 3, + "name": "Taggy goodness", + "slug": "taggy-goodness", + "description": null, + "parent_id": null, + "meta_title": null, + "meta_description": null, + "created_at": 1388318310790, + "created_by": 3, + "updated_at": 1388318310790, + "updated_by": 1 } ], "posts_tags": [ diff --git a/core/test/utils/fixtures/export-003.json b/core/test/utils/fixtures/export-003.json index 8db1516695..e9b4fab9df 100644 --- a/core/test/utils/fixtures/export-003.json +++ b/core/test/utils/fixtures/export-003.json @@ -32,10 +32,10 @@ { "id": 1, "uuid": "e5188224-4742-4c32-a2d6-e9c5c5d4c123", - "name": "Josephine Bloggs", - "slug": "josephine-blogs", + "name": "Joe Bloggs", + "slug": "joe-bloggs", "password": "$2a$10$.pZeeBE0gHXd0PTnbT/ph.GEKgd0Wd3q2pWna3ynTGBkPKnGIKABC", - "email": "josephinebloggs@example.com", + "email": "jbloggs@example.com", "image": null, "cover": null, "bio": "A blogger", diff --git a/core/test/utils/index.js b/core/test/utils/index.js index 8872e1440a..d316ebc00d 100644 --- a/core/test/utils/index.js +++ b/core/test/utils/index.js @@ -23,6 +23,7 @@ var when = require('when'), setup, doAuth, + initFixtures, initData, clearData; @@ -185,7 +186,9 @@ fixtures = { user = DataGenerator.forKnex.createUser(DataGenerator.Content.users[0]); - return knex('users').insert(user); + return knex('users').insert(user).then(function () { + return knex('roles_users').insert(DataGenerator.forKnex.roles_users[0]); + }); }, overrideOwnerUser: function overrideOwnerUser() { @@ -353,7 +356,7 @@ toDoList = { 'posts': function insertPosts() { return fixtures.insertPosts(); }, 'posts:mu': function insertMultiAuthorPosts() { return fixtures.insertMultiAuthorPosts(); }, 'apps': function insertApps() { return fixtures.insertApps(); }, - 'settings': function populate() { + 'settings': function populateSettings() { return settings.populateDefaults().then(function () { return SettingsAPI.updateSettingsCache(); }); }, 'users:roles': function createUsersWithRoles() { return fixtures.createUsersWithRoles(); }, @@ -410,6 +413,16 @@ getFixtureOps = function getFixtureOps(toDos) { // ## 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' @@ -417,14 +430,11 @@ getFixtureOps = function getFixtureOps(toDos) { * @returns {Function} */ setup = function setup() { - var options = _.merge({'init': true}, _.transform(arguments, function (result, val) { - result[val] = true; - }) - ), - fixtureOps = getFixtureOps(options); + var self = this, + args = arguments; return function (done) { - return sequence(fixtureOps).then(function () { + return initFixtures.apply(self, args).then(function () { done(); }).catch(done); }; @@ -433,7 +443,7 @@ setup = function setup() { /** * ## DoAuth For Route Tests * - * This function manages the work of ensuring we have an overriden owner user, and grabbing an access token + * This function manages the work of ensuring we have an overridden owner user, and grabbing an access token * @returns {deferred.promise} */ // TODO make this do the DB init as well @@ -480,6 +490,7 @@ module.exports = { setup: setup, doAuth: doAuth, + initFixtures: initFixtures, initData: initData, clearData: clearData,