2019-08-09 19:55:12 +05:30
|
|
|
const path = require('path');
|
|
|
|
const _ = require('lodash');
|
|
|
|
const os = require('os');
|
|
|
|
const fs = require('fs-extra');
|
|
|
|
const uuid = require('uuid');
|
|
|
|
const should = require('should');
|
|
|
|
const supertest = require('supertest');
|
|
|
|
const sinon = require('sinon');
|
2022-02-06 16:18:41 +00:00
|
|
|
const config = require('../../../../core/shared/config');
|
|
|
|
const events = require('../../../../core/server/lib/common/events');
|
|
|
|
const testUtils = require('../../../utils');
|
2019-08-09 19:55:12 +05:30
|
|
|
const localUtils = require('./utils');
|
|
|
|
|
|
|
|
let request;
|
|
|
|
let eventsTriggered;
|
|
|
|
|
2021-09-23 11:51:18 +01:00
|
|
|
describe('DB API (canary)', function () {
|
2019-08-09 19:55:12 +05:30
|
|
|
let backupKey;
|
|
|
|
let schedulerKey;
|
|
|
|
|
2021-11-24 13:42:57 +04:00
|
|
|
before(async function () {
|
2021-11-24 17:29:45 +04:00
|
|
|
await localUtils.startGhost();
|
2021-11-24 13:42:57 +04:00
|
|
|
request = supertest.agent(config.get('url'));
|
|
|
|
await localUtils.doAuth(request);
|
|
|
|
backupKey = _.find(testUtils.getExistingData().apiKeys, {integration: {slug: 'ghost-backup'}});
|
|
|
|
schedulerKey = _.find(testUtils.getExistingData().apiKeys, {integration: {slug: 'ghost-scheduler'}});
|
2019-08-09 19:55:12 +05:30
|
|
|
});
|
|
|
|
|
2019-08-19 12:41:09 +01:00
|
|
|
beforeEach(function () {
|
2019-08-09 19:55:12 +05:30
|
|
|
eventsTriggered = {};
|
|
|
|
|
2020-05-28 11:54:18 -05:00
|
|
|
sinon.stub(events, 'emit').callsFake((eventName, eventObj) => {
|
2019-08-09 19:55:12 +05:30
|
|
|
if (!eventsTriggered[eventName]) {
|
|
|
|
eventsTriggered[eventName] = [];
|
|
|
|
}
|
|
|
|
|
|
|
|
eventsTriggered[eventName].push(eventObj);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2019-08-19 12:41:09 +01:00
|
|
|
afterEach(function () {
|
2019-08-09 19:55:12 +05:30
|
|
|
sinon.restore();
|
|
|
|
});
|
|
|
|
|
2021-03-24 18:39:25 +13:00
|
|
|
it('can export the database with more tables', function () {
|
|
|
|
return request.get(localUtils.API.getApiQuery('db/?include=mobiledoc_revisions'))
|
2019-08-09 19:55:12 +05:30
|
|
|
.set('Origin', config.get('url'))
|
|
|
|
.expect('Content-Type', /json/)
|
|
|
|
.expect(200)
|
|
|
|
.then((res) => {
|
|
|
|
const jsonResponse = res.body;
|
|
|
|
should.exist(jsonResponse.db);
|
|
|
|
jsonResponse.db.should.have.length(1);
|
2021-03-25 19:27:49 +13:00
|
|
|
|
2022-05-16 15:59:46 +01:00
|
|
|
// NOTE: 11 default tables + 1 from include parameters
|
|
|
|
Object.keys(jsonResponse.db[0].data).length.should.eql(12);
|
2019-08-09 19:55:12 +05:30
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2019-08-19 12:41:09 +01:00
|
|
|
it('can export & import', function () {
|
2019-08-09 19:55:12 +05:30
|
|
|
const exportFolder = path.join(os.tmpdir(), uuid.v4());
|
|
|
|
const exportPath = path.join(exportFolder, 'export.json');
|
|
|
|
|
|
|
|
return request.put(localUtils.API.getApiQuery('settings/'))
|
|
|
|
.set('Origin', config.get('url'))
|
|
|
|
.send({
|
|
|
|
settings: [
|
|
|
|
{
|
|
|
|
key: 'is_private',
|
|
|
|
value: true
|
|
|
|
}
|
|
|
|
]
|
|
|
|
})
|
|
|
|
.expect('Content-Type', /json/)
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
|
|
.expect(200)
|
|
|
|
.then(() => {
|
|
|
|
return request.get(localUtils.API.getApiQuery('db/'))
|
|
|
|
.set('Origin', config.get('url'))
|
|
|
|
.expect('Content-Type', /json/)
|
|
|
|
.expect(200);
|
|
|
|
})
|
|
|
|
.then((res) => {
|
|
|
|
const jsonResponse = res.body;
|
|
|
|
should.exist(jsonResponse.db);
|
|
|
|
|
|
|
|
fs.ensureDirSync(exportFolder);
|
|
|
|
fs.writeJSONSync(exportPath, jsonResponse);
|
|
|
|
|
|
|
|
return request.post(localUtils.API.getApiQuery('db/'))
|
|
|
|
.set('Origin', config.get('url'))
|
|
|
|
.set('Accept', 'application/json')
|
|
|
|
.expect('Content-Type', /json/)
|
|
|
|
.attach('importfile', exportPath)
|
|
|
|
.expect(200);
|
|
|
|
})
|
|
|
|
.then((res) => {
|
2022-05-16 15:59:46 +01:00
|
|
|
res.body.problems.length.should.eql(4);
|
2019-08-09 19:55:12 +05:30
|
|
|
fs.removeSync(exportFolder);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2020-12-01 09:01:42 +00:00
|
|
|
it('fails when triggering an export from unknown filename ', function () {
|
|
|
|
return request.get(localUtils.API.getApiQuery('db/?filename=this_file_is_not_here.json'))
|
|
|
|
.set('Origin', config.get('url'))
|
|
|
|
.set('Accept', 'application/json')
|
|
|
|
.expect('Content-Type', /json/)
|
|
|
|
.expect(404);
|
|
|
|
});
|
|
|
|
|
2019-08-19 12:41:09 +01:00
|
|
|
it('import should fail without file', function () {
|
2019-08-09 19:55:12 +05:30
|
|
|
return request.post(localUtils.API.getApiQuery('db/'))
|
|
|
|
.set('Origin', config.get('url'))
|
|
|
|
.set('Accept', 'application/json')
|
|
|
|
.expect('Content-Type', /json/)
|
|
|
|
.expect(422);
|
|
|
|
});
|
|
|
|
|
2019-08-19 12:41:09 +01:00
|
|
|
it('import should fail with unsupported file', function () {
|
2019-08-09 19:55:12 +05:30
|
|
|
return request.post(localUtils.API.getApiQuery('db/'))
|
|
|
|
.set('Origin', config.get('url'))
|
|
|
|
.expect('Content-Type', /json/)
|
2022-02-06 16:18:41 +00:00
|
|
|
.attach('importfile', path.join(__dirname, '/../../../utils/fixtures/csv/single-column-with-header.csv'))
|
2019-08-09 19:55:12 +05:30
|
|
|
.expect(415);
|
|
|
|
});
|
|
|
|
|
2019-08-19 12:41:09 +01:00
|
|
|
it('export can be triggered by backup integration', function () {
|
2019-08-09 19:55:12 +05:30
|
|
|
const backupQuery = `?filename=test`;
|
|
|
|
const fsStub = sinon.stub(fs, 'writeFile').resolves();
|
|
|
|
|
|
|
|
return request.post(localUtils.API.getApiQuery(`db/backup${backupQuery}`))
|
2022-03-11 19:27:43 +08:00
|
|
|
.set('Authorization', `Ghost ${localUtils.getValidAdminToken('/admin/', backupKey)}`)
|
2019-08-09 19:55:12 +05:30
|
|
|
.set('Origin', config.get('url'))
|
|
|
|
.expect('Content-Type', /json/)
|
|
|
|
.expect(200)
|
|
|
|
.then((res) => {
|
|
|
|
res.body.should.be.Object();
|
|
|
|
res.body.db[0].filename.should.match(/test\.json/);
|
|
|
|
fsStub.calledOnce.should.eql(true);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2019-08-19 12:41:09 +01:00
|
|
|
it('export can not be triggered by integration other than backup', function () {
|
2019-08-09 19:55:12 +05:30
|
|
|
const fsStub = sinon.stub(fs, 'writeFile').resolves();
|
|
|
|
|
|
|
|
return request.post(localUtils.API.getApiQuery(`db/backup`))
|
2022-03-11 19:27:43 +08:00
|
|
|
.set('Authorization', `Ghost ${localUtils.getValidAdminToken('/admin/', schedulerKey)}`)
|
2019-08-09 19:55:12 +05:30
|
|
|
.set('Origin', config.get('url'))
|
|
|
|
.expect('Content-Type', /json/)
|
|
|
|
.expect(403)
|
|
|
|
.then((res) => {
|
|
|
|
should.exist(res.body.errors);
|
|
|
|
res.body.errors[0].type.should.eql('NoPermissionError');
|
|
|
|
fsStub.called.should.eql(false);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2019-08-19 12:41:09 +01:00
|
|
|
it('export can be triggered by Admin authentication', function () {
|
2019-08-09 19:55:12 +05:30
|
|
|
const fsStub = sinon.stub(fs, 'writeFile').resolves();
|
|
|
|
|
|
|
|
return request.post(localUtils.API.getApiQuery(`db/backup`))
|
|
|
|
.set('Origin', config.get('url'))
|
|
|
|
.expect('Content-Type', /json/)
|
|
|
|
.expect(200);
|
|
|
|
});
|
2021-03-24 17:12:10 +13:00
|
|
|
|
2022-01-21 12:47:25 +00:00
|
|
|
it('Can import a JSON database exported from Ghost 2.x', async function () {
|
2021-03-24 17:12:10 +13:00
|
|
|
await request.delete(localUtils.API.getApiQuery('db/'))
|
|
|
|
.set('Origin', config.get('url'))
|
|
|
|
.set('Accept', 'application/json')
|
|
|
|
.expect(204);
|
|
|
|
|
2021-03-24 20:00:34 +13:00
|
|
|
// preventively remove default "fixture" user
|
|
|
|
const fixtureUserResponse = await request.get(localUtils.API.getApiQuery('users/slug/fixture/'))
|
|
|
|
.set('Origin', config.get('url'))
|
|
|
|
.expect('Content-Type', /json/)
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.private);
|
|
|
|
|
|
|
|
if (fixtureUserResponse.body.users) {
|
|
|
|
await request.delete(localUtils.API.getApiQuery(`users/${fixtureUserResponse.body.users[0].id}`))
|
|
|
|
.set('Origin', config.get('url'))
|
|
|
|
.set('Accept', 'application/json');
|
|
|
|
}
|
|
|
|
|
2021-03-24 17:12:10 +13:00
|
|
|
const res = await request.post(localUtils.API.getApiQuery('db/'))
|
|
|
|
.set('Origin', config.get('url'))
|
|
|
|
.set('Accept', 'application/json')
|
|
|
|
.expect('Content-Type', /json/)
|
2022-02-06 16:18:41 +00:00
|
|
|
.attach('importfile', path.join(__dirname, '/../../../utils/fixtures/export/v2_export.json'))
|
2021-03-24 17:12:10 +13:00
|
|
|
.expect(200);
|
|
|
|
|
|
|
|
const jsonResponse = res.body;
|
|
|
|
should.exist(jsonResponse.db);
|
|
|
|
should.exist(jsonResponse.problems);
|
2021-03-24 20:00:34 +13:00
|
|
|
jsonResponse.problems.should.have.length(2);
|
2021-03-24 17:12:10 +13:00
|
|
|
|
2021-03-24 20:00:34 +13:00
|
|
|
const postsResponse = await request.get(localUtils.API.getApiQuery('posts/'))
|
2021-03-24 17:12:10 +13:00
|
|
|
.set('Origin', config.get('url'))
|
|
|
|
.expect('Content-Type', /json/)
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
|
|
.expect(200);
|
|
|
|
|
2021-03-24 20:00:34 +13:00
|
|
|
postsResponse.body.posts.should.have.length(7);
|
|
|
|
|
|
|
|
const usersResponse = await request.get(localUtils.API.getApiQuery('users/'))
|
|
|
|
.set('Origin', config.get('url'))
|
|
|
|
.expect('Content-Type', /json/)
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
|
|
.expect(200);
|
|
|
|
|
|
|
|
usersResponse.body.users.should.have.length(3);
|
2021-03-24 17:12:10 +13:00
|
|
|
});
|
|
|
|
|
2022-01-21 12:47:25 +00:00
|
|
|
it('Can import a JSON database exported from Ghost 3.x', async function () {
|
2021-03-24 17:12:10 +13:00
|
|
|
await request.delete(localUtils.API.getApiQuery('db/'))
|
|
|
|
.set('Origin', config.get('url'))
|
|
|
|
.set('Accept', 'application/json')
|
|
|
|
.expect(204);
|
|
|
|
|
2021-03-24 20:00:34 +13:00
|
|
|
// preventively remove default "fixture" user
|
|
|
|
const fixtureUserResponse = await request.get(localUtils.API.getApiQuery('users/slug/fixture/'))
|
|
|
|
.set('Origin', config.get('url'))
|
|
|
|
.expect('Content-Type', /json/)
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.private);
|
|
|
|
|
|
|
|
if (fixtureUserResponse.body.users) {
|
|
|
|
await request.delete(localUtils.API.getApiQuery(`users/${fixtureUserResponse.body.users[0].id}`))
|
|
|
|
.set('Origin', config.get('url'))
|
|
|
|
.set('Accept', 'application/json');
|
|
|
|
}
|
|
|
|
|
2021-03-24 17:12:10 +13:00
|
|
|
const res = await request.post(localUtils.API.getApiQuery('db/'))
|
|
|
|
.set('Origin', config.get('url'))
|
|
|
|
.set('Accept', 'application/json')
|
|
|
|
.expect('Content-Type', /json/)
|
2022-02-06 16:18:41 +00:00
|
|
|
.attach('importfile', path.join(__dirname, '/../../../utils/fixtures/export/v3_export.json'))
|
2021-03-24 17:12:10 +13:00
|
|
|
.expect(200);
|
|
|
|
|
|
|
|
const jsonResponse = res.body;
|
|
|
|
should.exist(jsonResponse.db);
|
|
|
|
should.exist(jsonResponse.problems);
|
|
|
|
jsonResponse.problems.should.have.length(2);
|
|
|
|
|
|
|
|
const res2 = await request.get(localUtils.API.getApiQuery('posts/'))
|
|
|
|
.set('Origin', config.get('url'))
|
|
|
|
.expect('Content-Type', /json/)
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
|
|
.expect(200);
|
|
|
|
|
|
|
|
res2.body.posts.should.have.length(7);
|
2021-03-24 20:00:34 +13:00
|
|
|
|
|
|
|
const usersResponse = await request.get(localUtils.API.getApiQuery('users/'))
|
|
|
|
.set('Origin', config.get('url'))
|
|
|
|
.expect('Content-Type', /json/)
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
|
|
.expect(200);
|
|
|
|
|
|
|
|
usersResponse.body.users.should.have.length(3);
|
2021-03-24 17:12:10 +13:00
|
|
|
});
|
|
|
|
|
2022-01-21 12:47:25 +00:00
|
|
|
it('Can import a JSON database exported from Ghost 4.x', async function () {
|
2021-03-24 17:12:10 +13:00
|
|
|
await request.delete(localUtils.API.getApiQuery('db/'))
|
|
|
|
.set('Origin', config.get('url'))
|
|
|
|
.set('Accept', 'application/json')
|
|
|
|
.expect(204);
|
|
|
|
|
2021-03-24 20:00:34 +13:00
|
|
|
// preventively remove default "fixture" user
|
|
|
|
const fixtureUserResponse = await request.get(localUtils.API.getApiQuery('users/slug/fixture/'))
|
|
|
|
.set('Origin', config.get('url'))
|
|
|
|
.expect('Content-Type', /json/)
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.private);
|
|
|
|
|
|
|
|
if (fixtureUserResponse.body.users) {
|
|
|
|
await request.delete(localUtils.API.getApiQuery(`users/${fixtureUserResponse.body.users[0].id}`))
|
|
|
|
.set('Origin', config.get('url'))
|
|
|
|
.set('Accept', 'application/json');
|
|
|
|
}
|
|
|
|
|
2021-03-24 17:12:10 +13:00
|
|
|
const res = await request.post(localUtils.API.getApiQuery('db/'))
|
|
|
|
.set('Origin', config.get('url'))
|
|
|
|
.set('Accept', 'application/json')
|
|
|
|
.expect('Content-Type', /json/)
|
2022-02-06 16:18:41 +00:00
|
|
|
.attach('importfile', path.join(__dirname, '/../../../utils/fixtures/export/v4_export.json'))
|
2021-03-24 17:12:10 +13:00
|
|
|
.expect(200);
|
|
|
|
|
|
|
|
const jsonResponse = res.body;
|
|
|
|
should.exist(jsonResponse.db);
|
|
|
|
should.exist(jsonResponse.problems);
|
2021-03-24 18:37:57 +13:00
|
|
|
jsonResponse.problems.should.have.length(2);
|
2021-03-24 17:12:10 +13:00
|
|
|
|
|
|
|
const res2 = await request.get(localUtils.API.getApiQuery('posts/'))
|
|
|
|
.set('Origin', config.get('url'))
|
|
|
|
.expect('Content-Type', /json/)
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
|
|
.expect(200);
|
|
|
|
|
|
|
|
res2.body.posts.should.have.length(7);
|
2021-03-24 20:00:34 +13:00
|
|
|
|
|
|
|
const usersResponse = await request.get(localUtils.API.getApiQuery('users/'))
|
|
|
|
.set('Origin', config.get('url'))
|
|
|
|
.expect('Content-Type', /json/)
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
|
|
.expect(200);
|
|
|
|
|
|
|
|
usersResponse.body.users.should.have.length(3);
|
2021-03-24 17:12:10 +13:00
|
|
|
});
|
2019-08-09 19:55:12 +05:30
|
|
|
});
|