From a96a74c5a18ad50a6c8feb56c45e0ad7325379f0 Mon Sep 17 00:00:00 2001 From: Hannah Wolfe Date: Fri, 8 Apr 2016 10:09:26 +0100 Subject: [PATCH] Improve code for handling fixture migrations refs #6301, #4176 - always check existence of items before attempting to create them, in order to prevent duplicates - provide stats on how many object creations are expected vs done - split out and improve fixture utils tests (100% covers utils) --- .../data/migration/fixtures/fixtures.json | 24 +- .../data/migration/fixtures/populate.js | 1 - core/server/data/migration/fixtures/utils.js | 65 +++- core/server/models/role.js | 3 +- core/test/integration/migration_spec.js | 113 +++---- core/test/unit/migration_fixture_spec.js | 179 ++--------- .../test/unit/migration_fixture_utils_spec.js | 289 ++++++++++++++++++ core/test/utils/index.js | 2 +- 8 files changed, 433 insertions(+), 243 deletions(-) create mode 100644 core/test/unit/migration_fixture_utils_spec.js diff --git a/core/server/data/migration/fixtures/fixtures.json b/core/server/data/migration/fixtures/fixtures.json index c4d6e54bbe..bf99d57ee2 100644 --- a/core/server/data/migration/fixtures/fixtures.json +++ b/core/server/data/migration/fixtures/fixtures.json @@ -48,18 +48,18 @@ { "name": "Role", "entries": [ - { - "name": "Administrator", - "description": "Administrators" - }, - { - "name": "Editor", - "description": "Editors" - }, - { - "name": "Author", - "description": "Authors" - }, + { + "name": "Administrator", + "description": "Administrators" + }, + { + "name": "Editor", + "description": "Editors" + }, + { + "name": "Author", + "description": "Authors" + }, { "name": "Owner", "description": "Blog Owner" diff --git a/core/server/data/migration/fixtures/populate.js b/core/server/data/migration/fixtures/populate.js index 3391937a93..20efda8c76 100644 --- a/core/server/data/migration/fixtures/populate.js +++ b/core/server/data/migration/fixtures/populate.js @@ -2,7 +2,6 @@ // This module handles populating fixtures on a fresh install. // This is done automatically, by reading the fixtures.json file // All models, and relationships inside the file are then setup. - var Promise = require('bluebird'), models = require('../../../models'), coreUtils = require('../../../utils'), diff --git a/core/server/data/migration/fixtures/utils.js b/core/server/data/migration/fixtures/utils.js index 3929ddd057..01848125d4 100644 --- a/core/server/data/migration/fixtures/utils.js +++ b/core/server/data/migration/fixtures/utils.js @@ -7,8 +7,9 @@ var _ = require('lodash'), fixtures = require('./fixtures'), -// Private + // Private matchFunc, + matchObj, fetchRelationData, findRelationFixture, findModelFixture, @@ -18,7 +19,7 @@ var _ = require('lodash'), addFixturesForModel, addFixturesForRelation, findModelFixtureEntry, - findPermissionModelForObject, + findModelFixtures, findPermissionRelationsForObject; /** @@ -54,6 +55,20 @@ matchFunc = function matchFunc(match, key, value) { }; }; +matchObj = function matchObj(match, item) { + var matchObj = {}; + + if (_.isArray(match)) { + _.each(match, function (matchProp) { + matchObj[matchProp] = item.get(matchProp); + }); + } else { + matchObj[match] = item.get(match); + } + + return matchObj; +}; + /** * ### Fetch Relation Data * Before we build relations we need to fetch all of the models from both sides so that we can @@ -63,10 +78,11 @@ matchFunc = function matchFunc(match, key, value) { * @returns {Promise<*>} */ fetchRelationData = function fetchRelationData(relation) { - var props = { - from: models[relation.from.model].findAll(modelOptions), - to: models[relation.to.model].findAll(modelOptions) - }; + var fromOptions = _.extend({}, modelOptions, {withRelated: [relation.from.relation]}), + props = { + from: models[relation.from.model].findAll(fromOptions), + to: models[relation.to.model].findAll(modelOptions) + }; return Promise.props(props); }; @@ -81,7 +97,13 @@ fetchRelationData = function fetchRelationData(relation) { */ addFixturesForModel = function addFixturesForModel(modelFixture) { return Promise.mapSeries(modelFixture.entries, function (entry) { - return models[modelFixture.name].add(entry, modelOptions); + return models[modelFixture.name].findOne(entry, modelOptions).then(function (found) { + if (!found) { + return models[modelFixture.name].add(entry, modelOptions); + } + }); + }).then(function (results) { + return {expected: modelFixture.entries.length, done: _.compact(results).length}; }); }; @@ -94,23 +116,34 @@ addFixturesForModel = function addFixturesForModel(modelFixture) { * @returns {Promise.<*>} */ addFixturesForRelation = function addFixturesForRelation(relationFixture) { - return fetchRelationData(relationFixture).then(function getRelationOps(data) { - var ops = []; + var ops = [], max = 0; + return fetchRelationData(relationFixture).then(function getRelationOps(data) { _.each(relationFixture.entries, function processEntries(entry, key) { var fromItem = data.from.find(matchFunc(relationFixture.from.match, key)); _.each(entry, function processEntryValues(value, key) { - var toItem = data.to.filter(matchFunc(relationFixture.to.match, key, value)); - if (toItem) { - ops.push(function addRelationItem() { - return fromItem[relationFixture.from.relation]().attach(toItem); + var toItems = data.to.filter(matchFunc(relationFixture.to.match, key, value)); + max += toItems.length; + + // Remove any duplicates that already exist in the collection + toItems = _.reject(toItems, function (item) { + return fromItem + .related(relationFixture.from.relation) + .findWhere(matchObj(relationFixture.to.match, item)); + }); + + if (toItems && toItems.length > 0) { + ops.push(function addRelationItems() { + return fromItem[relationFixture.from.relation]().attach(toItems); }); } }); }); return sequence(ops); + }).then(function (result) { + return {expected: max, done: _(result).map('length').sum()}; }); }; @@ -139,13 +172,13 @@ findModelFixtureEntry = function findModelFixtureEntry(modelName, matchExpr) { }; /** - * ### Find All Model Fixture + * ### Find Model Fixtures * Find a model fixture name & a matching expression for the FILTER function * @param {String} modelName * @param {String|Object|Function} matchExpr * @returns {Object} model fixture */ -findPermissionModelForObject = function findPermissionModelForObject(modelName, matchExpr) { +findModelFixtures = function findModelFixtures(modelName, matchExpr) { var foundModel = _.cloneDeep(findModelFixture(modelName)); foundModel.entries = _.filter(foundModel.entries, matchExpr); return foundModel; @@ -194,7 +227,7 @@ module.exports = { addFixturesForModel: addFixturesForModel, addFixturesForRelation: addFixturesForRelation, findModelFixtureEntry: findModelFixtureEntry, - findPermissionModelForObject: findPermissionModelForObject, + findModelFixtures: findModelFixtures, findPermissionRelationsForObject: findPermissionRelationsForObject, modelOptions: modelOptions }; diff --git a/core/server/models/role.js b/core/server/models/role.js index ea9f72a779..4a30f0250e 100644 --- a/core/server/models/role.js +++ b/core/server/models/role.js @@ -30,7 +30,8 @@ Role = ghostBookshelf.Model.extend({ // whitelists for the `options` hash argument on methods, by method name. // these are the only options that can be passed to Bookshelf / Knex. validOptions = { - findOne: ['withRelated'] + findOne: ['withRelated'], + findAll: ['withRelated'] }; if (validOptions[methodName]) { diff --git a/core/test/integration/migration_spec.js b/core/test/integration/migration_spec.js index 36a8a85d21..9923c0537e 100644 --- a/core/test/integration/migration_spec.js +++ b/core/test/integration/migration_spec.js @@ -11,6 +11,11 @@ var testUtils = require('../utils'), sandbox = sinon.sandbox.create(); describe('Database Migration (special functions)', function () { + var loggerStub = { + info: sandbox.stub(), + warn: sandbox.stub() + }; + before(testUtils.teardown); afterEach(testUtils.teardown); afterEach(function () { @@ -18,8 +23,6 @@ describe('Database Migration (special functions)', function () { }); describe('Fixtures', function () { - beforeEach(testUtils.setup()); - // Custom assertion for detection that a permissions is assigned to the correct roles should.Assertion.add('AssignedToRoles', function (roles) { var roleNames; @@ -121,70 +124,68 @@ describe('Database Migration (special functions)', function () { permissions[29].should.be.AssignedToRoles(['Administrator', 'Editor', 'Author']); }); - it('should populate all fixtures correctly', function (done) { - var loggerStub = { - info: sandbox.stub(), - warn: sandbox.stub() - }; + describe('Populate', function () { + beforeEach(testUtils.setup()); + it('should populate all fixtures correctly', function (done) { + fixtures.populate(loggerStub).then(function () { + var props = { + posts: Models.Post.findAll({include: ['tags']}), + tags: Models.Tag.findAll(), + users: Models.User.findAll({include: ['roles']}), + clients: Models.Client.findAll(), + roles: Models.Role.findAll(), + permissions: Models.Permission.findAll({include: ['roles']}) + }; - fixtures.populate(loggerStub).then(function () { - var props = { - posts: Models.Post.findAll({include: ['tags']}), - tags: Models.Tag.findAll(), - users: Models.User.findAll({include: ['roles']}), - clients: Models.Client.findAll(), - roles: Models.Role.findAll(), - permissions: Models.Permission.findAll({include: ['roles']}) - }; + loggerStub.info.called.should.be.true(); + loggerStub.warn.called.should.be.false(); - loggerStub.info.called.should.be.true(); - loggerStub.warn.called.should.be.false(); + return Promise.props(props).then(function (result) { + should.exist(result); - return Promise.props(props).then(function (result) { - should.exist(result); + // Post + should.exist(result.posts); + result.posts.length.should.eql(1); + result.posts.at(0).get('title').should.eql('Welcome to Ghost'); - // Post - should.exist(result.posts); - result.posts.length.should.eql(1); - result.posts.at(0).get('title').should.eql('Welcome to Ghost'); + // Tag + should.exist(result.tags); + result.tags.length.should.eql(1); + result.tags.at(0).get('name').should.eql('Getting Started'); - // Tag - should.exist(result.tags); - result.tags.length.should.eql(1); - result.tags.at(0).get('name').should.eql('Getting Started'); + // Post Tag relation + result.posts.at(0).related('tags').length.should.eql(1); + result.posts.at(0).related('tags').at(0).get('name').should.eql('Getting Started'); - // Post Tag relation - result.posts.at(0).related('tags').length.should.eql(1); - result.posts.at(0).related('tags').at(0).get('name').should.eql('Getting Started'); + // Clients + should.exist(result.clients); + result.clients.length.should.eql(2); + result.clients.at(0).get('name').should.eql('Ghost Admin'); + result.clients.at(1).get('name').should.eql('Ghost Frontend'); - // Clients - should.exist(result.clients); - result.clients.length.should.eql(2); - result.clients.at(0).get('name').should.eql('Ghost Admin'); - result.clients.at(1).get('name').should.eql('Ghost Frontend'); + // User (Owner) + should.exist(result.users); + result.users.length.should.eql(1); + result.users.at(0).get('name').should.eql('Ghost Owner'); + result.users.at(0).related('roles').length.should.eql(1); + result.users.at(0).related('roles').at(0).get('name').should.eql('Owner'); - // User (Owner) - should.exist(result.users); - result.users.length.should.eql(1); - result.users.at(0).get('name').should.eql('Ghost Owner'); - result.users.at(0).related('roles').length.should.eql(1); - result.users.at(0).related('roles').at(0).get('name').should.eql('Owner'); + // Roles + should.exist(result.roles); + result.roles.length.should.eql(4); + result.roles.at(0).get('name').should.eql('Administrator'); + result.roles.at(1).get('name').should.eql('Editor'); + result.roles.at(2).get('name').should.eql('Author'); + result.roles.at(3).get('name').should.eql('Owner'); - // Roles - should.exist(result.roles); - result.roles.length.should.eql(4); - result.roles.at(0).get('name').should.eql('Administrator'); - result.roles.at(1).get('name').should.eql('Editor'); - result.roles.at(2).get('name').should.eql('Author'); - result.roles.at(3).get('name').should.eql('Owner'); + // Permissions + result.permissions.length.should.eql(30); + result.permissions.toJSON().should.be.CompletePermissions(); - // Permissions - result.permissions.length.should.eql(30); - result.permissions.toJSON().should.be.CompletePermissions(); - - done(); - }); - }).catch(done); + done(); + }); + }).catch(done); + }); }); }); }); diff --git a/core/test/unit/migration_fixture_spec.js b/core/test/unit/migration_fixture_spec.js index 5d823dfbed..a87f77f847 100644 --- a/core/test/unit/migration_fixture_spec.js +++ b/core/test/unit/migration_fixture_spec.js @@ -11,7 +11,6 @@ var should = require('should'), versioning = require('../../server/data/schema/versioning'), update = rewire('../../server/data/migration/fixtures/update'), populate = rewire('../../server/data/migration/fixtures/populate'), - fixtureUtils = rewire('../../server/data/migration/fixtures/utils'), fixtures004 = require('../../server/data/migration/fixtures/004'), ensureDefaultSettings = require('../../server/data/migration/fixtures/settings'), @@ -92,14 +91,13 @@ describe('Fixtures', function () { loggerStub.warn.called.should.be.false(); sequenceStub.calledTwice.should.be.true(); + sequenceStub.firstCall.calledWith(sinon.match.array, sinon.match.object, loggerStub).should.be.true(); - sequenceStub.secondCall.calledWith(sinon.match.array, sinon.match.object, loggerStub).should.be.true(); - sequenceStub.firstCall.args[0].should.be.an.Array().with.lengthOf(1); - sequenceStub.secondCall.args[0].should.be.an.Array().with.lengthOf(8); - sequenceStub.firstCall.args[0][0].should.be.a.Function().with.property('name', 'runVersionTasks'); + sequenceStub.secondCall.calledWith(sinon.match.array, sinon.match.object, loggerStub).should.be.true(); + sequenceStub.secondCall.args[0].should.be.an.Array().with.lengthOf(8); sequenceStub.secondCall.args[0][0].should.be.a.Function().with.property('name', 'moveJQuery'); sequenceStub.secondCall.args[0][1].should.be.a.Function().with.property('name', 'updatePrivateSetting'); sequenceStub.secondCall.args[0][2].should.be.a.Function().with.property('name', 'updatePasswordSetting'); @@ -699,26 +697,43 @@ describe('Fixtures', function () { clientAddStub = sandbox.stub(models.Client, 'add').returns(Promise.resolve()), permsAddStub = sandbox.stub(models.Permission, 'add').returns(Promise.resolve()), + // Existence checks + postOneStub = sandbox.stub(models.Post, 'findOne').returns(Promise.resolve()), + tagOneStub = sandbox.stub(models.Tag, 'findOne').returns(Promise.resolve()), + roleOneStub = sandbox.stub(models.Role, 'findOne').returns(Promise.resolve()), + clientOneStub = sandbox.stub(models.Client, 'findOne').returns(Promise.resolve()), + permOneStub = sandbox.stub(models.Permission, 'findOne').returns(Promise.resolve()), + // Relations - modelMethodStub = {filter: sandbox.stub(), find: sandbox.stub()}, + fromItem = { + related: sandbox.stub().returnsThis(), + findWhere: sandbox.stub().returns({}) + }, + toItem = [{get: sandbox.stub()}], + modelMethodStub = {filter: sandbox.stub().returns(toItem), find: sandbox.stub().returns(fromItem)}, permsAllStub = sandbox.stub(models.Permission, 'findAll').returns(Promise.resolve(modelMethodStub)), rolesAllStub = sandbox.stub(models.Role, 'findAll').returns(Promise.resolve(modelMethodStub)), postsAllStub = sandbox.stub(models.Post, 'findAll').returns(Promise.resolve(modelMethodStub)), tagsAllStub = sandbox.stub(models.Tag, 'findAll').returns(Promise.resolve(modelMethodStub)), // Create Owner - roleOneStub = sandbox.stub(models.Role, 'findOne').returns(Promise.resolve({id: 1})), userAddStub = sandbox.stub(models.User, 'add').returns(Promise.resolve({})); + roleOneStub.onCall(4).returns(Promise.resolve({id: 1})); populate(loggerStub).then(function () { loggerStub.info.calledTwice.should.be.true(); loggerStub.warn.called.should.be.false(); + postOneStub.calledOnce.should.be.true(); postAddStub.calledOnce.should.be.true(); + tagOneStub.calledOnce.should.be.true(); tagAddStub.calledOnce.should.be.true(); + roleOneStub.callCount.should.be.aboveOrEqual(4); roleAddStub.callCount.should.eql(4); + clientOneStub.calledTwice.should.be.true(); clientAddStub.calledTwice.should.be.true(); + permOneStub.callCount.should.eql(30); permsAddStub.called.should.be.true(); permsAddStub.callCount.should.eql(30); @@ -736,53 +751,13 @@ describe('Fixtures', function () { modelMethodStub.find.callCount.should.eql(3 + 1); // Create Owner - roleOneStub.calledOnce.should.be.true(); + roleOneStub.callCount.should.eql(5); userAddStub.calledOnce.should.be.true(); done(); }).catch(done); }); - describe('Add All Relations', function () { - it('should call attach if relation models are found', function (done) { - var addAllRelations = populate.__get__('addAllRelations'), - emptyMethodStub = {filter: sandbox.stub(), find: sandbox.stub()}, - // Setup a chain of methods - dataMethodStub = { - filter: sandbox.stub().returnsThis(), - find: sandbox.stub().returnsThis(), - tags: sandbox.stub().returnsThis(), - attach: sandbox.stub().returns(Promise.resolve()) - }, - permsAllStub = sandbox.stub(models.Permission, 'findAll').returns(Promise.resolve(emptyMethodStub)), - rolesAllStub = sandbox.stub(models.Role, 'findAll').returns(Promise.resolve(emptyMethodStub)), - postsAllStub = sandbox.stub(models.Post, 'findAll').returns(Promise.resolve(dataMethodStub)), - tagsAllStub = sandbox.stub(models.Tag, 'findAll').returns(Promise.resolve(dataMethodStub)); - - addAllRelations().then(function () { - permsAllStub.calledOnce.should.be.true(); - rolesAllStub.calledOnce.should.be.true(); - postsAllStub.calledOnce.should.be.true(); - tagsAllStub.calledOnce.should.be.true(); - - // Permissions & Roles - emptyMethodStub.filter.called.should.be.true(); - emptyMethodStub.filter.callCount.should.eql(22); - emptyMethodStub.find.called.should.be.true(); - emptyMethodStub.find.callCount.should.eql(3); - - // Posts & Tags - dataMethodStub.filter.calledOnce.should.be.true(); - dataMethodStub.find.calledOnce.should.be.true(); - dataMethodStub.tags.calledOnce.should.be.true(); - dataMethodStub.attach.calledOnce.should.be.true(); - dataMethodStub.attach.calledWith(dataMethodStub).should.be.true(); - - done(); - }).catch(done); - }); - }); - describe('Create Owner', function () { var createOwner = populate.__get__('createOwner'), roleOneStub, userAddStub; @@ -818,78 +793,6 @@ describe('Fixtures', function () { }).catch(done); }); }); - - describe('Match Func', function () { - var matchFunc = fixtureUtils.__get__('matchFunc'), - getStub; - - beforeEach(function () { - getStub = sandbox.stub(); - getStub.withArgs('foo').returns('bar'); - getStub.withArgs('fun').returns('baz'); - }); - - it('should match undefined with no args', function () { - matchFunc()({get: getStub}).should.be.true(); - getStub.calledOnce.should.be.true(); - getStub.calledWith(undefined).should.be.true(); - }); - - it('should match key with match string', function () { - matchFunc('foo', 'bar')({get: getStub}).should.be.true(); - getStub.calledOnce.should.be.true(); - getStub.calledWith('foo').should.be.true(); - - matchFunc('foo', 'buz')({get: getStub}).should.be.false(); - getStub.calledTwice.should.be.true(); - getStub.secondCall.calledWith('foo').should.be.true(); - }); - - it('should match value when key is 0', function () { - matchFunc('foo', 0, 'bar')({get: getStub}).should.be.true(); - getStub.calledOnce.should.be.true(); - getStub.calledWith('foo').should.be.true(); - - matchFunc('foo', 0, 'buz')({get: getStub}).should.be.false(); - getStub.calledTwice.should.be.true(); - getStub.secondCall.calledWith('foo').should.be.true(); - }); - - it('should match key & value when match is array', function () { - matchFunc(['foo', 'fun'], 'bar', 'baz')({get: getStub}).should.be.true(); - getStub.calledTwice.should.be.true(); - getStub.getCall(0).calledWith('fun').should.be.true(); - getStub.getCall(1).calledWith('foo').should.be.true(); - - matchFunc(['foo', 'fun'], 'baz', 'bar')({get: getStub}).should.be.false(); - getStub.callCount.should.eql(4); - getStub.getCall(2).calledWith('fun').should.be.true(); - getStub.getCall(3).calledWith('foo').should.be.true(); - }); - - it('should match key only when match is array, but value is all', function () { - matchFunc(['foo', 'fun'], 'bar', 'all')({get: getStub}).should.be.true(); - getStub.calledOnce.should.be.true(); - getStub.calledWith('foo').should.be.true(); - - matchFunc(['foo', 'fun'], 'all', 'bar')({get: getStub}).should.be.false(); - getStub.callCount.should.eql(3); - getStub.getCall(1).calledWith('fun').should.be.true(); - getStub.getCall(2).calledWith('foo').should.be.true(); - }); - - it('should match key & value when match and value are arrays', function () { - matchFunc(['foo', 'fun'], 'bar', ['baz', 'buz'])({get: getStub}).should.be.true(); - getStub.calledTwice.should.be.true(); - getStub.getCall(0).calledWith('fun').should.be.true(); - getStub.getCall(1).calledWith('foo').should.be.true(); - - matchFunc(['foo', 'fun'], 'bar', ['biz', 'buz'])({get: getStub}).should.be.false(); - getStub.callCount.should.eql(4); - getStub.getCall(2).calledWith('fun').should.be.true(); - getStub.getCall(3).calledWith('foo').should.be.true(); - }); - }); }); describe('Ensure default settings', function () { @@ -904,40 +807,4 @@ describe('Fixtures', function () { }).catch(done); }); }); - - describe('Utils', function () { - describe('findModelFixtureEntry', function () { - it('should fetch a single fixture entry', function () { - var foundFixture = fixtureUtils.findModelFixtureEntry('Client', {slug: 'ghost-admin'}); - foundFixture.should.be.an.Object(); - foundFixture.should.eql({ - name: 'Ghost Admin', - slug: 'ghost-admin', - status: 'enabled' - }); - }); - }); - - describe('findPermissionModelForObject', function () { - it('should fetch a fixture with multiple entries', function () { - var foundFixture = fixtureUtils.findPermissionModelForObject('Permission', {object_type: 'db'}); - foundFixture.should.be.an.Object(); - foundFixture.entries.should.be.an.Array().with.lengthOf(3); - foundFixture.entries[0].should.eql({ - name: 'Export database', - action_type: 'exportContent', - object_type: 'db' - }); - }); - }); - - describe('findPermissionRelationsForObject', function () { - it('should fetch a fixture with multiple entries', function () { - var foundFixture = fixtureUtils.findPermissionRelationsForObject('db'); - foundFixture.should.be.an.Object(); - foundFixture.entries.should.be.an.Object(); - foundFixture.entries.should.have.property('Administrator', {db: 'all'}); - }); - }); - }); }); diff --git a/core/test/unit/migration_fixture_utils_spec.js b/core/test/unit/migration_fixture_utils_spec.js new file mode 100644 index 0000000000..1bb8a54cfc --- /dev/null +++ b/core/test/unit/migration_fixture_utils_spec.js @@ -0,0 +1,289 @@ +/*global describe, it, beforeEach, afterEach */ +var should = require('should'), + sinon = require('sinon'), + Promise = require('bluebird'), + rewire = require('rewire'), + + models = require('../../server/models'), + + fixtureUtils = rewire('../../server/data/migration/fixtures/utils'), + fixtures = require('../../server/data/migration/fixtures/fixtures'), + sandbox = sinon.sandbox.create(); + +describe('Utils', function () { + var loggerStub; + + beforeEach(function () { + loggerStub = { + info: sandbox.stub(), + warn: sandbox.stub() + }; + + models.init(); + }); + + afterEach(function () { + sandbox.restore(); + }); + + describe('Match Func', function () { + var matchFunc = fixtureUtils.__get__('matchFunc'), + getStub; + + beforeEach(function () { + getStub = sandbox.stub(); + getStub.withArgs('foo').returns('bar'); + getStub.withArgs('fun').returns('baz'); + }); + + it('should match undefined with no args', function () { + matchFunc()({get: getStub}).should.be.true(); + getStub.calledOnce.should.be.true(); + getStub.calledWith(undefined).should.be.true(); + }); + + it('should match key with match string', function () { + matchFunc('foo', 'bar')({get: getStub}).should.be.true(); + getStub.calledOnce.should.be.true(); + getStub.calledWith('foo').should.be.true(); + + matchFunc('foo', 'buz')({get: getStub}).should.be.false(); + getStub.calledTwice.should.be.true(); + getStub.secondCall.calledWith('foo').should.be.true(); + }); + + it('should match value when key is 0', function () { + matchFunc('foo', 0, 'bar')({get: getStub}).should.be.true(); + getStub.calledOnce.should.be.true(); + getStub.calledWith('foo').should.be.true(); + + matchFunc('foo', 0, 'buz')({get: getStub}).should.be.false(); + getStub.calledTwice.should.be.true(); + getStub.secondCall.calledWith('foo').should.be.true(); + }); + + it('should match key & value when match is array', function () { + matchFunc(['foo', 'fun'], 'bar', 'baz')({get: getStub}).should.be.true(); + getStub.calledTwice.should.be.true(); + getStub.getCall(0).calledWith('fun').should.be.true(); + getStub.getCall(1).calledWith('foo').should.be.true(); + + matchFunc(['foo', 'fun'], 'baz', 'bar')({get: getStub}).should.be.false(); + getStub.callCount.should.eql(4); + getStub.getCall(2).calledWith('fun').should.be.true(); + getStub.getCall(3).calledWith('foo').should.be.true(); + }); + + it('should match key only when match is array, but value is all', function () { + matchFunc(['foo', 'fun'], 'bar', 'all')({get: getStub}).should.be.true(); + getStub.calledOnce.should.be.true(); + getStub.calledWith('foo').should.be.true(); + + matchFunc(['foo', 'fun'], 'all', 'bar')({get: getStub}).should.be.false(); + getStub.callCount.should.eql(3); + getStub.getCall(1).calledWith('fun').should.be.true(); + getStub.getCall(2).calledWith('foo').should.be.true(); + }); + + it('should match key & value when match and value are arrays', function () { + matchFunc(['foo', 'fun'], 'bar', ['baz', 'buz'])({get: getStub}).should.be.true(); + getStub.calledTwice.should.be.true(); + getStub.getCall(0).calledWith('fun').should.be.true(); + getStub.getCall(1).calledWith('foo').should.be.true(); + + matchFunc(['foo', 'fun'], 'bar', ['biz', 'buz'])({get: getStub}).should.be.false(); + getStub.callCount.should.eql(4); + getStub.getCall(2).calledWith('fun').should.be.true(); + getStub.getCall(3).calledWith('foo').should.be.true(); + }); + }); + + describe('Add Fixtures For Model', function () { + it('should call add for main post fixture', function (done) { + var postOneStub = sandbox.stub(models.Post, 'findOne').returns(Promise.resolve()), + postAddStub = sandbox.stub(models.Post, 'add').returns(Promise.resolve({})); + + fixtureUtils.addFixturesForModel(fixtures.models[0]).then(function (result) { + should.exist(result); + result.should.be.an.Object(); + result.should.have.property('expected', 1); + result.should.have.property('done', 1); + + postOneStub.calledOnce.should.be.true(); + postAddStub.calledOnce.should.be.true(); + + done(); + }); + }); + + it('should not call add for main post fixture if it is already found', function (done) { + var postOneStub = sandbox.stub(models.Post, 'findOne').returns(Promise.resolve({})), + postAddStub = sandbox.stub(models.Post, 'add').returns(Promise.resolve({})); + fixtureUtils.addFixturesForModel(fixtures.models[0]).then(function (result) { + should.exist(result); + result.should.be.an.Object(); + result.should.have.property('expected', 1); + result.should.have.property('done', 0); + + postOneStub.calledOnce.should.be.true(); + postAddStub.calledOnce.should.be.false(); + + done(); + }); + }); + }); + + describe('Add Fixtures For Relation', function () { + it('should call attach for permissions-roles', function (done) { + var fromItem = { + related: sandbox.stub().returnsThis(), + findWhere: sandbox.stub().returns(), + permissions: sandbox.stub().returnsThis(), + attach: sandbox.stub().returns(Promise.resolve([{}])) + }, + toItem = [{get: sandbox.stub()}], + dataMethodStub = { + filter: sandbox.stub().returns(toItem), + find: sandbox.stub().returns(fromItem) + }, + permsAllStub = sandbox.stub(models.Permission, 'findAll').returns(Promise.resolve(dataMethodStub)), + rolesAllStub = sandbox.stub(models.Role, 'findAll').returns(Promise.resolve(dataMethodStub)); + + fixtureUtils.addFixturesForRelation(fixtures.relations[0]).then(function (result) { + should.exist(result); + result.should.be.an.Object(); + result.should.have.property('expected', 22); + result.should.have.property('done', 22); + + // Permissions & Roles + permsAllStub.calledOnce.should.be.true(); + rolesAllStub.calledOnce.should.be.true(); + dataMethodStub.filter.callCount.should.eql(22); + dataMethodStub.find.callCount.should.eql(3); + + fromItem.related.callCount.should.eql(22); + fromItem.findWhere.callCount.should.eql(22); + toItem[0].get.callCount.should.eql(44); + + fromItem.permissions.callCount.should.eql(22); + fromItem.attach.callCount.should.eql(22); + fromItem.attach.calledWith(toItem).should.be.true(); + + done(); + }).catch(done); + }); + + it('should call attach for posts-tags', function (done) { + var fromItem = { + related: sandbox.stub().returnsThis(), + findWhere: sandbox.stub().returns(), + tags: sandbox.stub().returnsThis(), + attach: sandbox.stub().returns(Promise.resolve([{}])) + }, + toItem = [{get: sandbox.stub()}], + dataMethodStub = { + filter: sandbox.stub().returns(toItem), + find: sandbox.stub().returns(fromItem) + }, + + postsAllStub = sandbox.stub(models.Post, 'findAll').returns(Promise.resolve(dataMethodStub)), + tagsAllStub = sandbox.stub(models.Tag, 'findAll').returns(Promise.resolve(dataMethodStub)); + + fixtureUtils.addFixturesForRelation(fixtures.relations[1]).then(function (result) { + should.exist(result); + result.should.be.an.Object(); + result.should.have.property('expected', 1); + result.should.have.property('done', 1); + + // Posts & Tags + postsAllStub.calledOnce.should.be.true(); + tagsAllStub.calledOnce.should.be.true(); + dataMethodStub.filter.calledOnce.should.be.true(); + dataMethodStub.find.calledOnce.should.be.true(); + + fromItem.related.calledOnce.should.be.true(); + fromItem.findWhere.calledOnce.should.be.true(); + toItem[0].get.calledOnce.should.be.true(); + + fromItem.tags.calledOnce.should.be.true(); + fromItem.attach.calledOnce.should.be.true(); + fromItem.attach.calledWith(toItem).should.be.true(); + + done(); + }).catch(done); + }); + + it('will not call attach for posts-tags if already present', function (done) { + var fromItem = { + related: sandbox.stub().returnsThis(), + findWhere: sandbox.stub().returns({}), + tags: sandbox.stub().returnsThis(), + attach: sandbox.stub().returns(Promise.resolve({})) + }, + toItem = [{get: sandbox.stub()}], + dataMethodStub = { + filter: sandbox.stub().returns(toItem), + find: sandbox.stub().returns(fromItem) + }, + + postsAllStub = sandbox.stub(models.Post, 'findAll').returns(Promise.resolve(dataMethodStub)), + tagsAllStub = sandbox.stub(models.Tag, 'findAll').returns(Promise.resolve(dataMethodStub)); + + fixtureUtils.addFixturesForRelation(fixtures.relations[1]).then(function (result) { + should.exist(result); + result.should.be.an.Object(); + result.should.have.property('expected', 1); + result.should.have.property('done', 0); + + // Posts & Tags + postsAllStub.calledOnce.should.be.true(); + tagsAllStub.calledOnce.should.be.true(); + dataMethodStub.filter.calledOnce.should.be.true(); + dataMethodStub.find.calledOnce.should.be.true(); + + fromItem.related.calledOnce.should.be.true(); + fromItem.findWhere.calledOnce.should.be.true(); + toItem[0].get.calledOnce.should.be.true(); + + fromItem.tags.called.should.be.false(); + fromItem.attach.called.should.be.false(); + + done(); + }).catch(done); + }); + }); + + describe('findModelFixtureEntry', function () { + it('should fetch a single fixture entry', function () { + var foundFixture = fixtureUtils.findModelFixtureEntry('Client', {slug: 'ghost-admin'}); + foundFixture.should.be.an.Object(); + foundFixture.should.eql({ + name: 'Ghost Admin', + slug: 'ghost-admin', + status: 'enabled' + }); + }); + }); + + describe('findModelFixtures', function () { + it('should fetch a fixture with multiple entries', function () { + var foundFixture = fixtureUtils.findModelFixtures('Permission', {object_type: 'db'}); + foundFixture.should.be.an.Object(); + foundFixture.entries.should.be.an.Array().with.lengthOf(3); + foundFixture.entries[0].should.eql({ + name: 'Export database', + action_type: 'exportContent', + object_type: 'db' + }); + }); + }); + + describe('findPermissionRelationsForObject', function () { + it('should fetch a fixture with multiple entries', function () { + var foundFixture = fixtureUtils.findPermissionRelationsForObject('db'); + foundFixture.should.be.an.Object(); + foundFixture.entries.should.be.an.Object(); + foundFixture.entries.should.have.property('Administrator', {db: 'all'}); + }); + }); +}); diff --git a/core/test/utils/index.js b/core/test/utils/index.js index e67be69e67..4a931cedf6 100644 --- a/core/test/utils/index.js +++ b/core/test/utils/index.js @@ -316,7 +316,7 @@ fixtures = { }, permissionsFor: function permissionsFor(obj) { - var permsToInsert = fixtureUtils.findPermissionModelForObject('Permission', {object_type: obj}).entries, + var permsToInsert = fixtureUtils.findModelFixtures('Permission', {object_type: obj}).entries, permsRolesToInsert = fixtureUtils.findPermissionRelationsForObject(obj).entries, actions = [], permissionsRoles = [],