var _ = require('lodash'), Promise = require('bluebird'), should = require('should'), rewire = require('rewire'), sinon = require('sinon'), moment = require('moment'), uuid = require('uuid'), testUtils = require('../utils'), configUtils = require('../utils/configUtils'), packageInfo = require('../../../package'), updateCheck = rewire('../../server/update-check'), ghostVersion = rewire('../../server/lib/ghost-version'), SettingsAPI = require('../../server/api/settings'), NotificationsAPI = rewire('../../server/api/notifications'), sandbox = sinon.sandbox.create(); describe('Update Check', function () { beforeEach(function () { updateCheck = rewire('../../server/update-check'); NotificationsAPI = rewire('../../server/api/notifications'); }); afterEach(function () { sandbox.restore(); configUtils.restore(); }); after(testUtils.teardown); describe('fn: updateCheck', function () { var updateCheckRequestSpy, updateCheckResponseSpy, updateCheckErrorSpy; beforeEach(testUtils.teardown); beforeEach(testUtils.setup('roles', 'owner')); beforeEach(function () { updateCheckRequestSpy = sandbox.stub().returns(Promise.resolve()); updateCheckResponseSpy = sandbox.stub().returns(Promise.resolve()); updateCheckErrorSpy = sandbox.stub(); updateCheck.__set__('updateCheckRequest', updateCheckRequestSpy); updateCheck.__set__('updateCheckResponse', updateCheckResponseSpy); updateCheck.__set__('updateCheckError', updateCheckErrorSpy); updateCheck.__set__('allowedCheckEnvironments', ['development', 'production', 'testing', 'testing-mysql', 'testing-pg']); }); it('update check was never executed', function (done) { sandbox.stub(SettingsAPI, 'read').returns(Promise.resolve({ settings: [{ value: null }] })); updateCheck() .then(function () { updateCheckRequestSpy.calledOnce.should.eql(true); updateCheckResponseSpy.calledOnce.should.eql(true); updateCheckErrorSpy.called.should.eql(false); done(); }) .catch(done); }); it('update check won\'t happen if it\'s too early', function (done) { sandbox.stub(SettingsAPI, 'read').returns(Promise.resolve({ settings: [{ value: moment().add('10', 'minutes').unix() }] })); updateCheck() .then(function () { updateCheckRequestSpy.calledOnce.should.eql(false); updateCheckResponseSpy.calledOnce.should.eql(false); updateCheckErrorSpy.called.should.eql(false); done(); }) .catch(done); }); it('update check will happen if it\'s time to check', function (done) { sandbox.stub(SettingsAPI, 'read').returns(Promise.resolve({ settings: [{ value: moment().subtract('10', 'minutes').unix() }] })); updateCheck() .then(function () { updateCheckRequestSpy.calledOnce.should.eql(true); updateCheckResponseSpy.calledOnce.should.eql(true); updateCheckErrorSpy.called.should.eql(false); done(); }) .catch(done); }); }); describe('fn: updateCheckData', function () { var environmentsOrig; before(function () { configUtils.set('privacy:useUpdateCheck', true); }); after(function () { configUtils.restore(); }); beforeEach(testUtils.teardown); beforeEach(testUtils.setup('roles', 'owner', 'settings', 'posts', 'perms:setting', 'perms:user', 'perms:init')); it('should report the correct data', function (done) { var updateCheckData = updateCheck.__get__('updateCheckData'); updateCheckData().then(function (data) { should.exist(data); data.ghost_version.should.equal(packageInfo.version); data.node_version.should.equal(process.versions.node); data.env.should.equal(process.env.NODE_ENV); data.database_type.should.match(/sqlite3|mysql/); data.blog_id.should.be.a.String(); data.blog_id.should.not.be.empty(); data.theme.should.be.equal('casper'); data.apps.should.be.a.String(); data.blog_created_at.should.be.a.Number(); data.user_count.should.be.above(0); data.post_count.should.be.above(0); data.npm_version.should.be.a.String(); data.npm_version.should.not.be.empty(); data.lts.should.eql(false); done(); }).catch(done); }); }); describe('fn: createCustomNotification', function () { var currentVersionOrig; before(function () { currentVersionOrig = updateCheck.__get__('ghostVersion.original'); updateCheck.__set__('ghostVersion.original', '0.9.0'); }); after(function () { updateCheck.__set__('ghostVersion.original', currentVersionOrig); }); beforeEach(testUtils.teardown); beforeEach(testUtils.setup('settings', 'roles', 'owner', 'perms:setting', 'perms:notification', 'perms:user', 'perms:init')); beforeEach(function () { return NotificationsAPI.destroyAll(testUtils.context.internal); }); it('should create a release notification for target version', function (done) { var createCustomNotification = updateCheck.__get__('createCustomNotification'), notification = { id: 1, custom: 0, messages: [{ id: uuid.v4(), version: '0.9.x', content: '
Hey there! This is for 0.9.0 version
', dismissible: true, top: true }] }; NotificationsAPI.__set__('ghostVersion.full', '0.8.1'); createCustomNotification(notification).then(function () { return NotificationsAPI.browse(testUtils.context.internal); }).then(function (results) { should.exist(results); should.exist(results.notifications); results.notifications.length.should.eql(1); var targetNotification = _.find(results.notifications, {id: notification.messages[0].id}); should.exist(targetNotification); targetNotification.dismissible.should.eql(notification.messages[0].dismissible); targetNotification.id.should.eql(notification.messages[0].id); targetNotification.top.should.eql(notification.messages[0].top); targetNotification.type.should.eql('info'); targetNotification.message.should.eql(notification.messages[0].content); done(); }).catch(done); }); it('release notification version format is wrong', function (done) { var createCustomNotification = updateCheck.__get__('createCustomNotification'), notification = { id: 1, custom: 0, messages: [{ id: uuid.v4(), version: '0.9.x', content: 'Hey there! This is for 0.9 version
', dismissible: true, top: true }] }; NotificationsAPI.__set__('ghostVersion.full', '0.8.1'); createCustomNotification(notification).then(function () { return NotificationsAPI.browse(testUtils.context.internal); }).then(function (results) { should.exist(results); should.exist(results.notifications); results.notifications.length.should.eql(0); done(); }).catch(done); }); it('blog version format is wrong', function (done) { var createCustomNotification = updateCheck.__get__('createCustomNotification'), notification = { id: 1, custom: 0, messages: [{ id: uuid.v4(), version: '0.9.x', content: 'Hey there! This is for 0.9.0 version
', dismissible: true, top: true }] }; NotificationsAPI.__set__('ghostVersion.full', '0.8'); createCustomNotification(notification).then(function () { return NotificationsAPI.browse(testUtils.context.internal); }).then(function (results) { should.exist(results); should.exist(results.notifications); results.notifications.length.should.eql(0); done(); }).catch(done); }); it('should create a custom notification', function (done) { var createCustomNotification = updateCheck.__get__('createCustomNotification'), notification = { id: 1, custom: 1, messages: [{ id: uuid.v4(), version: 'custom1', content: 'How about migrating your blog?
', dismissible: false, top: true, type: 'warn' }] }; createCustomNotification(notification).then(function () { return NotificationsAPI.browse(testUtils.context.internal); }).then(function (results) { should.exist(results); should.exist(results.notifications); results.notifications.length.should.eql(1); var targetNotification = _.find(results.notifications, {id: notification.messages[0].id}); should.exist(targetNotification); targetNotification.dismissible.should.eql(notification.messages[0].dismissible); targetNotification.top.should.eql(notification.messages[0].top); targetNotification.type.should.eql(notification.messages[0].type); done(); }).catch(done); }); it('should not add duplicates', function (done) { var createCustomNotification = updateCheck.__get__('createCustomNotification'), notification = { id: 1, custom: 1, messages: [{ id: uuid.v4(), version: 'custom1', content: 'How about migrating your blog?
', dismissible: false, top: true, type: 'warn' }] }; createCustomNotification(notification) .then(function () { return NotificationsAPI.browse(testUtils.context.internal); }) .then(function (results) { should.exist(results); should.exist(results.notifications); results.notifications.length.should.eql(1); }) .then(function () { return createCustomNotification(notification); }) .then(function () { return NotificationsAPI.browse(testUtils.context.internal); }) .then(function (results) { should.exist(results); should.exist(results.notifications); results.notifications.length.should.eql(1); done(); }) .catch(done); }); }); describe('fn: updateCheckResponse', function () { beforeEach(testUtils.teardown); beforeEach(testUtils.setup('roles', 'settings', 'perms:setting', 'perms:init')); it('receives a notifications with messages', function (done) { var updateCheckResponse = updateCheck.__get__('updateCheckResponse'), createNotificationSpy = sandbox.spy(), message = { id: uuid.v4(), version: '^0.11.11', content: 'Test', dismissible: true, top: true }; updateCheck.__set__('createCustomNotification', createNotificationSpy); updateCheckResponse({version: '0.11.12', messages: [message]}) .then(function () { createNotificationSpy.callCount.should.eql(1); done(); }) .catch(done); }); it('receives multiple notifications', function (done) { var updateCheckResponse = updateCheck.__get__('updateCheckResponse'), createNotificationSpy = sandbox.spy(), message1 = { id: uuid.v4(), version: '^0.11.11', content: 'Test1', dismissible: true, top: true }, message2 = { id: uuid.v4(), version: '^0', content: 'Test2', dismissible: true, top: false }, notifications = [ {version: '0.11.12', messages: [message1]}, {version: 'custom1', messages: [message2]} ]; updateCheck.__set__('createCustomNotification', createNotificationSpy); updateCheckResponse(notifications) .then(function () { createNotificationSpy.callCount.should.eql(2); done(); }) .catch(done); }); it('ignores some custom notifications which are not marked as group', function (done) { var updateCheckResponse = updateCheck.__get__('updateCheckResponse'), createNotificationSpy = sandbox.spy(), message1 = { id: uuid.v4(), version: '^0.11.11', content: 'Test1', dismissible: true, top: true }, message2 = { id: uuid.v4(), version: '^0', content: 'Test2', dismissible: true, top: false }, message3 = { id: uuid.v4(), version: '^0', content: 'Test2', dismissible: true, top: false }, notifications = [ {version: '0.11.12', messages: [message1]}, {version: 'all1', messages: [message2], custom: 1}, {version: 'migration1', messages: [message3], custom: 1} ]; updateCheck.__set__('createCustomNotification', createNotificationSpy); updateCheckResponse(notifications) .then(function () { createNotificationSpy.callCount.should.eql(2); done(); }) .catch(done); }); it('group matches', function (done) { var updateCheckResponse = updateCheck.__get__('updateCheckResponse'), createNotificationSpy = sandbox.spy(), message1 = { id: uuid.v4(), version: '^0.11.11', content: 'Test1', dismissible: true, top: true }, message2 = { id: uuid.v4(), version: '^0', content: 'Test2', dismissible: true, top: false }, message3 = { id: uuid.v4(), version: '^0', content: 'Test2', dismissible: true, top: false }, notifications = [ {version: '0.11.12', messages: [message1], custom: 0}, {version: 'all1', messages: [message2], custom: 1}, {version: 'migration1', messages: [message3], custom: 1} ]; updateCheck.__set__('createCustomNotification', createNotificationSpy); configUtils.set({notificationGroups: ['migration']}); updateCheckResponse(notifications) .then(function () { createNotificationSpy.callCount.should.eql(3); done(); }) .catch(done); }); it('single custom notification received, group matches', function (done) { var updateCheckResponse = updateCheck.__get__('updateCheckResponse'), createNotificationSpy = sandbox.spy(), message1 = { id: uuid.v4(), version: '^0.11.11', content: 'Custom', dismissible: true, top: true }, notifications = [ {version: 'something', messages: [message1], custom: 1} ]; updateCheck.__set__('createCustomNotification', createNotificationSpy); configUtils.set({notificationGroups: ['something']}); updateCheckResponse(notifications) .then(function () { createNotificationSpy.callCount.should.eql(1); done(); }) .catch(done); }); it('single custom notification received, group does not match', function (done) { var updateCheckResponse = updateCheck.__get__('updateCheckResponse'), createNotificationSpy = sandbox.spy(), message1 = { id: uuid.v4(), version: '^0.11.11', content: 'Custom', dismissible: true, top: true }, notifications = [ {version: 'something', messages: [message1], custom: 1} ]; updateCheck.__set__('createCustomNotification', createNotificationSpy); configUtils.set({notificationGroups: ['migration']}); updateCheckResponse(notifications) .then(function () { createNotificationSpy.callCount.should.eql(0); done(); }) .catch(done); }); }); describe('fn: updateCheckRequest', function () { beforeEach(function () { configUtils.set('privacy:useUpdateCheck', true); }); afterEach(function () { configUtils.restore(); }); it('[default]', function () { var updateCheckRequest = updateCheck.__get__('updateCheckRequest'), updateCheckDataSpy = sandbox.stub(), hostname, reqObj, data = { ghost_version: '0.11.11', blog_id: 'something', npm_version: 'something' }; updateCheck.__set__('request', function (_hostname, _reqObj) { hostname = _hostname; reqObj = _reqObj; return Promise.resolve({ statusCode: 200, body: {version: 'something'} }); }); updateCheck.__set__('updateCheckData', updateCheckDataSpy); updateCheckDataSpy.returns(Promise.resolve(data)); return updateCheckRequest() .then(function () { hostname.should.eql('https://updates.ghost.org'); should.exist(reqObj.headers['Content-Length']); reqObj.body.should.eql(data); reqObj.json.should.eql(true); }); }); it('privacy flag is used', function () { var updateCheckRequest = updateCheck.__get__('updateCheckRequest'), updateCheckDataSpy = sandbox.stub(), reqObj, hostname; configUtils.set({ privacy: { useUpdateCheck: false } }); updateCheck.__set__('request', function (_hostname, _reqObj) { hostname = _hostname; reqObj = _reqObj; return Promise.resolve({ statusCode: 200, body: {version: 'something'} }); }); updateCheck.__set__('updateCheckData', updateCheckDataSpy); updateCheckDataSpy.returns(Promise.resolve({ ghost_version: '0.11.11', blog_id: 'something', npm_version: 'something' })); return updateCheckRequest() .then(function () { hostname.should.eql('https://updates.ghost.org'); reqObj.query.should.eql({ ghost_version: '0.11.11' }); should.not.exist(reqObj.body); reqObj.json.should.eql(true); should.not.exist(reqObj.headers['Content-Length']); }); }); it('received 500 from the service', function () { var updateCheckRequest = updateCheck.__get__('updateCheckRequest'), updateCheckDataSpy = sandbox.stub(), reqObj, hostname; updateCheck.__set__('request', function (_hostname, _reqObj) { hostname = _hostname; reqObj = _reqObj; return Promise.reject({ statusCode: 500, message: 'something went wrong' }); }); updateCheck.__set__('updateCheckData', updateCheckDataSpy); updateCheckDataSpy.returns(Promise.resolve({ ghost_version: '0.11.11', blog_id: 'something', npm_version: 'something' })); return updateCheckRequest() .then(function () { throw new Error('Should fail.'); }) .catch(function (err) { err.message.should.eql('something went wrong'); }); }); it('received 404 from the service', function () { var updateCheckRequest = updateCheck.__get__('updateCheckRequest'), updateCheckDataSpy = sandbox.stub(), reqObj, hostname; updateCheck.__set__('request', function (_hostname, _reqObj) { hostname = _hostname; reqObj = _reqObj; return Promise.reject({ statusCode: 404, response: { body: { errors: [{detail: 'No Notifications available.'}] } } }); }); updateCheck.__set__('updateCheckData', updateCheckDataSpy); updateCheckDataSpy.returns(Promise.resolve({ ghost_version: '0.11.11', blog_id: 'something', npm_version: 'something' })); return updateCheckRequest() .then(function () { hostname.should.eql('https://updates.ghost.org'); }); }); it('custom url', function () { var updateCheckRequest = updateCheck.__get__('updateCheckRequest'), updateCheckDataSpy = sandbox.stub(), reqObj, hostname; configUtils.set({ updateCheck: { url: 'http://localhost:3000' } }); updateCheck.__set__('request', function (_hostname, _reqObj) { hostname = _hostname; reqObj = _reqObj; return Promise.resolve({ statusCode: 200, body: { version: 'something' } }); }); updateCheck.__set__('updateCheckData', updateCheckDataSpy); updateCheckDataSpy.returns(Promise.resolve({ ghost_version: '0.11.11', blog_id: 'something', npm_version: 'something' })); return updateCheckRequest() .then(function () { hostname.should.eql('http://localhost:3000'); }); }); }); });