Skip to content

Commit 191cff2

Browse files
authored
Updated backup tests to use latest test framework (#23586)
ref https://www.notion.so/ghost/End-to-end-Testing-6a2ef073b1754b18aff42e24a632a007 - We have a more up-to-date testing framework for our API - We should be using it everywhere and have at least one test for each endpoint - This change is more of a rewrite than a port, moving the backup tests from our regression suite into the new framework with a cleaner pattern for using various tokens and auth - This is paving the way for giving our backup integration more permissions
1 parent 6f8d181 commit 191cff2

File tree

3 files changed

+237
-46
lines changed

3 files changed

+237
-46
lines changed
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`Backup Integration Backup API Backup Integration Can create a DB backup 1: [body] 1`] = `
4+
Object {
5+
"db": Array [
6+
Object {
7+
"filename": StringMatching /ghost-test\\\\/data\\\\/test\\\\\\.json\\$/,
8+
},
9+
],
10+
}
11+
`;
12+
13+
exports[`Backup Integration Backup API Backup Integration Can create a DB backup 2: [headers] 1`] = `
14+
Object {
15+
"access-control-allow-origin": "http://127.0.0.1:2369",
16+
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
17+
"content-length": StringMatching /\\\\d\\+/,
18+
"content-type": "application/json; charset=utf-8",
19+
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
20+
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
21+
"vary": "Accept-Version, Origin, Accept-Encoding",
22+
"x-powered-by": "Express",
23+
}
24+
`;
25+
26+
exports[`Backup Integration Backup API Editor: User authentication Cannot create a DB backup 1: [body] 1`] = `
27+
Object {
28+
"errors": Array [
29+
Object {
30+
"code": null,
31+
"context": null,
32+
"details": null,
33+
"ghostErrorCode": null,
34+
"help": null,
35+
"id": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
36+
"message": "You do not have permission to backupContent db",
37+
"property": null,
38+
"type": "NoPermissionError",
39+
},
40+
],
41+
}
42+
`;
43+
44+
exports[`Backup Integration Backup API Editor: User authentication Cannot create a DB backup 2: [headers] 1`] = `
45+
Object {
46+
"access-control-allow-origin": "http://127.0.0.1:2369",
47+
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
48+
"content-length": "236",
49+
"content-type": "application/json; charset=utf-8",
50+
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
51+
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
52+
"vary": "Accept-Version, Origin, Accept-Encoding",
53+
"x-powered-by": "Express",
54+
}
55+
`;
56+
57+
exports[`Backup Integration Backup API Owner: User authentication Can create a DB backup 1: [body] 1`] = `
58+
Object {
59+
"db": Array [
60+
Object {
61+
"filename": StringMatching /ghost-test\\\\/data\\\\/test\\\\\\.json\\$/,
62+
},
63+
],
64+
}
65+
`;
66+
67+
exports[`Backup Integration Backup API Owner: User authentication Can create a DB backup 2: [headers] 1`] = `
68+
Object {
69+
"access-control-allow-origin": "http://127.0.0.1:2369",
70+
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
71+
"content-length": StringMatching /\\\\d\\+/,
72+
"content-type": "application/json; charset=utf-8",
73+
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
74+
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
75+
"vary": "Accept-Version, Origin, Accept-Encoding",
76+
"x-powered-by": "Express",
77+
}
78+
`;
79+
80+
exports[`Backup Integration Backup API Zapier Integration Cannot create a DB backup 1: [body] 1`] = `
81+
Object {
82+
"errors": Array [
83+
Object {
84+
"code": null,
85+
"context": null,
86+
"details": null,
87+
"ghostErrorCode": null,
88+
"help": null,
89+
"id": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
90+
"message": "You do not have permission to backupContent db",
91+
"property": null,
92+
"type": "NoPermissionError",
93+
},
94+
],
95+
}
96+
`;
97+
98+
exports[`Backup Integration Backup API Zapier Integration Cannot create a DB backup 2: [headers] 1`] = `
99+
Object {
100+
"access-control-allow-origin": "http://127.0.0.1:2369",
101+
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
102+
"content-length": StringMatching /\\\\d\\+/,
103+
"content-type": "application/json; charset=utf-8",
104+
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
105+
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
106+
"vary": "Accept-Version, Origin, Accept-Encoding",
107+
"x-powered-by": "Express",
108+
}
109+
`;
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
const {agentProvider, fixtureManager, matchers} = require('../../utils/e2e-framework');
2+
const {anyContentLength, anyContentVersion, anyEtag, anyErrorId, stringMatching} = matchers;
3+
const fs = require('fs-extra');
4+
const sinon = require('sinon');
5+
const assert = require('assert/strict');
6+
7+
describe('Backup Integration', function () {
8+
let agent, fsStub;
9+
10+
before(async function () {
11+
agent = await agentProvider.getAdminAPIAgent();
12+
await fixtureManager.init('users');
13+
});
14+
15+
beforeEach(function () {
16+
fsStub = sinon.stub(fs, 'writeFile').resolves();
17+
});
18+
19+
afterEach(function () {
20+
sinon.restore();
21+
});
22+
23+
describe('Backup API', function () {
24+
describe('Backup Integration', function () {
25+
before(async function () {
26+
await agent.useBackupAdminAPIKey();
27+
});
28+
29+
it('Can create a DB backup', async function () {
30+
await agent
31+
.post('db/backup?filename=test')
32+
.expectStatus(200)
33+
.matchHeaderSnapshot({
34+
'content-length': anyContentLength,
35+
'content-version': anyContentVersion,
36+
etag: anyEtag
37+
})
38+
.matchBodySnapshot({
39+
db: [{
40+
filename: stringMatching(/ghost-test\/data\/test\.json$/)
41+
}]
42+
});
43+
44+
sinon.assert.calledOnce(fsStub);
45+
const args = fsStub.firstCall.args;
46+
const fileJSON = JSON.parse(args[1]);
47+
48+
assert.match(args[0].toString(), /ghost-test\/data\/test.json$/);
49+
// @TODO: make a way do this with snapshots!
50+
assert.ok(fileJSON.meta, 'Written file has a property called meta');
51+
assert.ok(fileJSON.data, 'Written file has a property called data');
52+
});
53+
});
54+
55+
describe('Zapier Integration', function () {
56+
before(async function () {
57+
await agent.useZapierAdminAPIKey();
58+
});
59+
60+
it('Cannot create a DB backup', async function () {
61+
await agent
62+
.post('db/backup?filename=test')
63+
.expectStatus(403)
64+
.matchHeaderSnapshot({
65+
'content-length': anyContentLength,
66+
'content-version': anyContentVersion,
67+
etag: anyEtag
68+
})
69+
.matchBodySnapshot({
70+
errors: [{
71+
id: anyErrorId
72+
}]
73+
});
74+
});
75+
});
76+
77+
describe('Owner: User authentication', function () {
78+
before(async function () {
79+
await agent.loginAsOwner();
80+
});
81+
82+
it('Can create a DB backup', async function () {
83+
await agent
84+
.post('db/backup?filename=test')
85+
.expectStatus(200)
86+
.matchHeaderSnapshot({
87+
'content-length': anyContentLength,
88+
'content-version': anyContentVersion,
89+
etag: anyEtag
90+
})
91+
.matchBodySnapshot({
92+
db: [{
93+
filename: stringMatching(/ghost-test\/data\/test\.json$/)
94+
}]
95+
});
96+
97+
sinon.assert.calledOnce(fsStub);
98+
const args = fsStub.firstCall.args;
99+
const fileJSON = JSON.parse(args[1]);
100+
101+
assert.match(args[0].toString(), /ghost-test\/data\/test.json$/);
102+
// @TODO: make a way do this with snapshots!
103+
assert.ok(fileJSON.meta, 'Written file has a property called meta');
104+
assert.ok(fileJSON.data, 'Written file has a property called data');
105+
});
106+
});
107+
108+
describe('Editor: User authentication', function () {
109+
before(async function () {
110+
await agent.loginAsEditor();
111+
});
112+
113+
it('Cannot create a DB backup', async function () {
114+
await agent.post('db/backup?filename=test')
115+
.expectStatus(403)
116+
.matchHeaderSnapshot({
117+
'content-version': anyContentVersion,
118+
etag: anyEtag
119+
})
120+
.matchBodySnapshot({
121+
errors: [{
122+
id: anyErrorId
123+
}]
124+
});
125+
});
126+
});
127+
});
128+
});

ghost/core/test/regression/api/admin/db.test.js

Lines changed: 0 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
const path = require('path');
2-
const _ = require('lodash');
32
const os = require('os');
43
const fs = require('fs-extra');
54
const crypto = require('crypto');
@@ -18,15 +17,10 @@ let eventsTriggered;
1817
const TABLE_ALLOWLIST_LENGTH = 20;
1918

2019
describe('DB API', function () {
21-
let backupKey;
22-
let schedulerKey;
23-
2420
before(async function () {
2521
await localUtils.startGhost();
2622
request = supertest.agent(config.get('url'));
2723
await localUtils.doAuth(request);
28-
backupKey = _.find(testUtils.getExistingData().apiKeys, {integration: {slug: 'ghost-backup'}});
29-
schedulerKey = _.find(testUtils.getExistingData().apiKeys, {integration: {slug: 'ghost-scheduler'}});
3024
});
3125

3226
beforeEach(function () {
@@ -127,46 +121,6 @@ describe('DB API', function () {
127121
.expect(415);
128122
});
129123

130-
it('export can be triggered by backup integration', function () {
131-
const backupQuery = `?filename=test`;
132-
const fsStub = sinon.stub(fs, 'writeFile').resolves();
133-
134-
return request.post(localUtils.API.getApiQuery(`db/backup${backupQuery}`))
135-
.set('Authorization', `Ghost ${localUtils.getValidAdminToken('/admin/', backupKey)}`)
136-
.set('Origin', config.get('url'))
137-
.expect('Content-Type', /json/)
138-
.expect(200)
139-
.then((res) => {
140-
res.body.should.be.Object();
141-
res.body.db[0].filename.should.match(/test\.json/);
142-
fsStub.calledOnce.should.eql(true);
143-
});
144-
});
145-
146-
it('export can not be triggered by integration other than backup', function () {
147-
const fsStub = sinon.stub(fs, 'writeFile').resolves();
148-
149-
return request.post(localUtils.API.getApiQuery(`db/backup`))
150-
.set('Authorization', `Ghost ${localUtils.getValidAdminToken('/admin/', schedulerKey)}`)
151-
.set('Origin', config.get('url'))
152-
.expect('Content-Type', /json/)
153-
.expect(403)
154-
.then((res) => {
155-
should.exist(res.body.errors);
156-
res.body.errors[0].type.should.eql('NoPermissionError');
157-
fsStub.called.should.eql(false);
158-
});
159-
});
160-
161-
it('export can be triggered by Admin authentication', function () {
162-
sinon.stub(fs, 'writeFile').resolves();
163-
164-
return request.post(localUtils.API.getApiQuery(`db/backup`))
165-
.set('Origin', config.get('url'))
166-
.expect('Content-Type', /json/)
167-
.expect(200);
168-
});
169-
170124
it('Can import a JSON database exported from Ghost 2.x', async function () {
171125
await request.delete(localUtils.API.getApiQuery('db/'))
172126
.set('Origin', config.get('url'))

0 commit comments

Comments
 (0)