mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-03 23:00:14 -05:00
1ecb837981
refs https://github.com/TryGhost/Toolbox/issues/292 - When version missmatch handling is done in Ghost we need to store the 'Accept-Version' header values that have been already processed in the past (to avoid sending notifications about the same mismatch multiple times) - The `version_notifications` will be storing an array with handled versions like so: `['v3.44', 'v4.23', 'v4.39']`. - The emailing logic and processing is slightly similar to how "notification" key is handled, that's why I've placed the definition of this new key close by.
205 lines
8.3 KiB
JavaScript
205 lines
8.3 KiB
JavaScript
const should = require('should');
|
|
const sinon = require('sinon');
|
|
const Promise = require('bluebird');
|
|
const errors = require('@tryghost/errors');
|
|
const db = require('../../../../../core/server/data/db');
|
|
const exporter = require('../../../../../core/server/data/exporter');
|
|
const schema = require('../../../../../core/server/data/schema');
|
|
const models = require('../../../../../core/server/models');
|
|
const schemaTables = Object.keys(schema.tables);
|
|
|
|
describe('Exporter', function () {
|
|
let tablesStub;
|
|
let queryMock;
|
|
let knexMock;
|
|
|
|
before(function () {
|
|
models.init();
|
|
});
|
|
|
|
afterEach(function () {
|
|
sinon.restore();
|
|
});
|
|
|
|
describe('doExport', function () {
|
|
beforeEach(function () {
|
|
tablesStub = sinon.stub(schema.commands, 'getTables').returns(schemaTables);
|
|
|
|
queryMock = {
|
|
whereNot: sinon.stub(),
|
|
select: sinon.stub()
|
|
};
|
|
|
|
knexMock = sinon.stub().returns(queryMock);
|
|
|
|
sinon.stub(db, 'knex').get(function () {
|
|
return knexMock;
|
|
});
|
|
});
|
|
|
|
it('should try to export all the correct tables (without excluded)', function (done) {
|
|
exporter.doExport().then(function (exportData) {
|
|
// NOTE: 10 default tables
|
|
const expectedCallCount = 10;
|
|
|
|
should.exist(exportData);
|
|
|
|
exportData.meta.version.should.match(/\d+.\d+.\d+/gi);
|
|
|
|
tablesStub.calledOnce.should.be.true();
|
|
db.knex.called.should.be.true();
|
|
|
|
knexMock.callCount.should.eql(expectedCallCount);
|
|
queryMock.select.callCount.should.have.eql(expectedCallCount);
|
|
|
|
knexMock.getCall(0).args[0].should.eql('posts');
|
|
knexMock.getCall(1).args[0].should.eql('posts_meta');
|
|
knexMock.getCall(2).args[0].should.eql('users');
|
|
knexMock.getCall(3).args[0].should.eql('posts_authors');
|
|
knexMock.getCall(4).args[0].should.eql('roles');
|
|
knexMock.getCall(5).args[0].should.eql('roles_users');
|
|
knexMock.getCall(6).args[0].should.eql('settings');
|
|
knexMock.getCall(7).args[0].should.eql('tags');
|
|
knexMock.getCall(8).args[0].should.eql('posts_tags');
|
|
knexMock.getCall(9).args[0].should.eql('custom_theme_settings');
|
|
|
|
done();
|
|
}).catch(done);
|
|
});
|
|
|
|
it('should try to export all the correct tables with extra tables', function (done) {
|
|
const include = ['mobiledoc_revisions', 'email_recipients'];
|
|
|
|
exporter.doExport({include}).then(function (exportData) {
|
|
// NOTE: 10 default tables + 2 includes
|
|
const expectedCallCount = 12;
|
|
|
|
should.exist(exportData);
|
|
|
|
exportData.meta.version.should.match(/\d+.\d+.\d+/gi);
|
|
|
|
tablesStub.calledOnce.should.be.true();
|
|
db.knex.called.should.be.true();
|
|
queryMock.select.called.should.be.true();
|
|
|
|
knexMock.callCount.should.eql(expectedCallCount);
|
|
queryMock.select.callCount.should.have.eql(expectedCallCount);
|
|
|
|
knexMock.getCall(0).args[0].should.eql('posts');
|
|
knexMock.getCall(1).args[0].should.eql('posts_meta');
|
|
knexMock.getCall(2).args[0].should.eql('users');
|
|
knexMock.getCall(3).args[0].should.eql('posts_authors');
|
|
knexMock.getCall(4).args[0].should.eql('roles');
|
|
knexMock.getCall(5).args[0].should.eql('roles_users');
|
|
knexMock.getCall(6).args[0].should.eql('settings');
|
|
knexMock.getCall(7).args[0].should.eql('tags');
|
|
knexMock.getCall(8).args[0].should.eql('posts_tags');
|
|
knexMock.getCall(9).args[0].should.eql('mobiledoc_revisions');
|
|
knexMock.getCall(10).args[0].should.eql('email_recipients');
|
|
knexMock.getCall(11).args[0].should.eql('custom_theme_settings');
|
|
|
|
done();
|
|
}).catch(done);
|
|
});
|
|
|
|
it('should catch and log any errors', function (done) {
|
|
// Setup for failure
|
|
queryMock.select.returns(Promise.reject({}));
|
|
|
|
// Execute
|
|
exporter.doExport()
|
|
.then(function () {
|
|
done(new Error('expected error for export'));
|
|
})
|
|
.catch(function (err) {
|
|
(err instanceof errors.DataExportError).should.eql(true);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('exportFileName', function () {
|
|
it('should return a correctly structured filename', function (done) {
|
|
const settingsStub = sinon.stub(models.Settings, 'findOne').returns(
|
|
new Promise.resolve({
|
|
get: function () {
|
|
return 'testblog';
|
|
}
|
|
})
|
|
);
|
|
|
|
exporter.fileName().then(function (result) {
|
|
should.exist(result);
|
|
settingsStub.calledOnce.should.be.true();
|
|
result.should.match(/^testblog\.ghost\.[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{2}-[0-9]{2}-[0-9]{2}\.json$/);
|
|
|
|
done();
|
|
}).catch(done);
|
|
});
|
|
|
|
it('should return a correctly structured filename if settings is empty', function (done) {
|
|
const settingsStub = sinon.stub(models.Settings, 'findOne').returns(
|
|
new Promise.resolve()
|
|
);
|
|
|
|
exporter.fileName().then(function (result) {
|
|
should.exist(result);
|
|
settingsStub.calledOnce.should.be.true();
|
|
result.should.match(/^ghost\.[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{2}-[0-9]{2}-[0-9]{2}\.json$/);
|
|
|
|
done();
|
|
}).catch(done);
|
|
});
|
|
|
|
it('should return a correctly structured filename if settings errors', function (done) {
|
|
const settingsStub = sinon.stub(models.Settings, 'findOne').returns(
|
|
new Promise.reject()
|
|
);
|
|
|
|
exporter.fileName().then(function (result) {
|
|
should.exist(result);
|
|
settingsStub.calledOnce.should.be.true();
|
|
result.should.match(/^ghost\.[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{2}-[0-9]{2}-[0-9]{2}\.json$/);
|
|
|
|
done();
|
|
}).catch(done);
|
|
});
|
|
});
|
|
|
|
describe('Export table allowlists', function () {
|
|
it('should be fixed when db schema introduces new tables', function () {
|
|
const {
|
|
BACKUP_TABLES,
|
|
TABLES_ALLOWLIST
|
|
} = require('../../../../../core/server/data/exporter/table-lists.js');
|
|
|
|
const nonSchemaTables = ['migrations', 'migrations_lock'];
|
|
const requiredTables = schemaTables.concat(nonSchemaTables);
|
|
// NOTE: You should not add tables to this list unless they are temporary
|
|
const ignoredTables = ['temp_member_analytic_events'];
|
|
|
|
const expectedTables = requiredTables.filter(table => !ignoredTables.includes(table)).sort();
|
|
const actualTables = BACKUP_TABLES.concat(TABLES_ALLOWLIST).sort();
|
|
|
|
// NOTE: this test is serving a role of a reminder to have a look into exported tables allowlists
|
|
// if it failed you probably need to add or remove a table entry from table-lists module
|
|
should.deepEqual(actualTables, expectedTables);
|
|
});
|
|
|
|
it('should be fixed when default settings is changed', function () {
|
|
const {
|
|
SETTING_KEYS_BLOCKLIST
|
|
} = require('../../../../../core/server/data/exporter/table-lists.js');
|
|
const defaultSettings = require('../../../../../core/server/data/schema/default-settings/default-settings.json');
|
|
|
|
const totalKeysLength = Object.keys(defaultSettings).reduce((acc, curr, index) => {
|
|
return acc + Object.keys(defaultSettings[curr]).length;
|
|
}, 0);
|
|
|
|
// NOTE: if default settings changed either modify the settings keys blocklist or increase allowedKeysLength
|
|
// This is a reminder to think about the importer/exporter scenarios ;)
|
|
const allowedKeysLength = 86;
|
|
totalKeysLength.should.eql(SETTING_KEYS_BLOCKLIST.length + allowedKeysLength);
|
|
});
|
|
});
|
|
});
|