From 2540be63c1969486eda1069c04391c212915ef45 Mon Sep 17 00:00:00 2001 From: Naz Date: Tue, 30 Nov 2021 13:31:34 +0400 Subject: [PATCH] Removed v2/v3 regression tests for non-stable endpoints closes https://github.com/TryGhost/Toolbox/issues/148 - These regression tests introduce very little additional value in exchange for an expensive time to run them. Because we mostly care about stable support for the latest stable API version, the older API version test for "internal" API can go away - In case there are bugs found we do care about in the v2/v3 APIs we can always revert some of these tests. --- .../api/v2/admin/authentication.test.js | 431 --------- test/regression/api/v2/admin/db.test.js | 158 ---- .../api/v2/admin/notifications.test.js | 179 ---- test/regression/api/v2/admin/oembed.test.js | 346 ------- .../regression/api/v2/admin/schedules.test.js | 170 ---- test/regression/api/v2/admin/settings.test.js | 842 ------------------ test/regression/api/v2/admin/slack.test.js | 40 - test/regression/api/v2/admin/users.test.js | 262 ------ .../api/v3/admin/authentication.test.js | 392 -------- test/regression/api/v3/admin/db.test.js | 166 ---- .../api/v3/admin/identities.test.js | 95 -- test/regression/api/v3/admin/labels.test.js | 64 -- test/regression/api/v3/admin/members.test.js | 569 ------------ .../api/v3/admin/members_signin_url.test.js | 107 --- .../api/v3/admin/notifications.test.js | 179 ---- .../regression/api/v3/admin/schedules.test.js | 170 ---- test/regression/api/v3/admin/settings.test.js | 836 ----------------- test/regression/api/v3/admin/slack.test.js | 40 - test/regression/api/v3/admin/users.test.js | 262 ------ 19 files changed, 5308 deletions(-) delete mode 100644 test/regression/api/v2/admin/authentication.test.js delete mode 100644 test/regression/api/v2/admin/db.test.js delete mode 100644 test/regression/api/v2/admin/notifications.test.js delete mode 100644 test/regression/api/v2/admin/oembed.test.js delete mode 100644 test/regression/api/v2/admin/schedules.test.js delete mode 100644 test/regression/api/v2/admin/settings.test.js delete mode 100644 test/regression/api/v2/admin/slack.test.js delete mode 100644 test/regression/api/v2/admin/users.test.js delete mode 100644 test/regression/api/v3/admin/authentication.test.js delete mode 100644 test/regression/api/v3/admin/db.test.js delete mode 100644 test/regression/api/v3/admin/identities.test.js delete mode 100644 test/regression/api/v3/admin/labels.test.js delete mode 100644 test/regression/api/v3/admin/members.test.js delete mode 100644 test/regression/api/v3/admin/members_signin_url.test.js delete mode 100644 test/regression/api/v3/admin/notifications.test.js delete mode 100644 test/regression/api/v3/admin/schedules.test.js delete mode 100644 test/regression/api/v3/admin/settings.test.js delete mode 100644 test/regression/api/v3/admin/slack.test.js delete mode 100644 test/regression/api/v3/admin/users.test.js diff --git a/test/regression/api/v2/admin/authentication.test.js b/test/regression/api/v2/admin/authentication.test.js deleted file mode 100644 index e7bca9fd2e..0000000000 --- a/test/regression/api/v2/admin/authentication.test.js +++ /dev/null @@ -1,431 +0,0 @@ -const should = require('should'); -const sinon = require('sinon'); -const supertest = require('supertest'); -const localUtils = require('./utils'); -const testUtils = require('../../../../utils/index'); -const models = require('../../../../../core/server/models/index'); -const security = require('@tryghost/security'); -const settingsCache = require('../../../../../core/shared/settings-cache'); -const config = require('../../../../../core/shared/config/index'); -const mailService = require('../../../../../core/server/services/mail/index'); -const configUtils = require('../../../../utils/configUtils'); - -let request; - -describe('Authentication API v2', function () { - describe('Blog setup: default config', function () { - before(async function () { - await localUtils.startGhost({forceStart: true}); - request = supertest.agent(config.get('url')); - }); - - beforeEach(function () { - sinon.stub(mailService.GhostMailer.prototype, 'send').resolves('Mail is disabled'); - }); - - afterEach(function () { - sinon.restore(); - }); - - it('is setup? no', function () { - return request - .get(localUtils.API.getApiQuery('authentication/setup')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect(200) - .then((res) => { - res.body.setup[0].status.should.be.false(); - }); - }); - - it('complete setup', function () { - return request - .post(localUtils.API.getApiQuery('authentication/setup')) - .set('Origin', config.get('url')) - .send({ - setup: [{ - name: 'test user', - email: 'test@example.com', - password: 'thisissupersafe', - blogTitle: 'a test blog' - }] - }) - .expect('Content-Type', /json/) - .expect(201) - .then((res) => { - const jsonResponse = res.body; - should.exist(jsonResponse.users); - should.not.exist(jsonResponse.meta); - - jsonResponse.users.should.have.length(1); - localUtils.API.checkResponse(jsonResponse.users[0], 'user'); - - const newUser = jsonResponse.users[0]; - newUser.id.should.equal(testUtils.DataGenerator.Content.users[0].id); - newUser.name.should.equal('test user'); - newUser.email.should.equal('test@example.com'); - - mailService.GhostMailer.prototype.send.called.should.be.true(); - mailService.GhostMailer.prototype.send.args[0][0].to.should.equal('test@example.com'); - }); - }); - - it('is setup? yes', function () { - return request - .get(localUtils.API.getApiQuery('authentication/setup')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect(200) - .then((res) => { - res.body.setup[0].status.should.be.true(); - }); - }); - - it('complete setup again', function () { - return request - .post(localUtils.API.getApiQuery('authentication/setup')) - .set('Origin', config.get('url')) - .send({ - setup: [{ - name: 'test user', - email: 'test-leo@example.com', - password: 'thisissupersafe', - blogTitle: 'a test blog' - }] - }) - .expect('Content-Type', /json/) - .expect(403); - }); - - it('update setup', function () { - return localUtils.doAuth(request) - .then(() => { - return request - .put(localUtils.API.getApiQuery('authentication/setup')) - .set('Origin', config.get('url')) - .send({ - setup: [{ - name: 'test user edit', - email: 'test-edit@example.com', - password: 'thisissupersafe', - blogTitle: 'a test blog' - }] - }) - .expect('Content-Type', /json/) - .expect(200); - }) - .then((res) => { - const jsonResponse = res.body; - should.exist(jsonResponse.users); - should.not.exist(jsonResponse.meta); - - jsonResponse.users.should.have.length(1); - localUtils.API.checkResponse(jsonResponse.users[0], 'user'); - - const newUser = jsonResponse.users[0]; - newUser.id.should.equal(testUtils.DataGenerator.Content.users[0].id); - newUser.name.should.equal('test user edit'); - newUser.email.should.equal('test-edit@example.com'); - }); - }); - }); - - describe('Blog setup: custom config', function () { - before(async function () { - await localUtils.startGhost({forceStart: true}); - request = supertest.agent(config.get('url')); - }); - - beforeEach(function () { - configUtils.set({ - sendWelcomeEmail: false // Default value is false in pro - }); - sinon.stub(mailService.GhostMailer.prototype, 'send').resolves('Mail is disabled'); - }); - - afterEach(function () { - configUtils.restore(); - sinon.restore(); - }); - - it('is setup? no', function () { - return request - .get(localUtils.API.getApiQuery('authentication/setup')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect(200) - .then((res) => { - res.body.setup[0].status.should.be.false(); - }); - }); - - it('complete setup', function () { - return request - .post(localUtils.API.getApiQuery('authentication/setup')) - .set('Origin', config.get('url')) - .send({ - setup: [{ - name: 'test user', - email: 'test@example.com', - password: 'thisissupersafe', - blogTitle: 'a test blog' - }] - }) - .expect('Content-Type', /json/) - .expect(201) - .then((res) => { - const jsonResponse = res.body; - should.exist(jsonResponse.users); - should.not.exist(jsonResponse.meta); - - jsonResponse.users.should.have.length(1); - localUtils.API.checkResponse(jsonResponse.users[0], 'user'); - - const newUser = jsonResponse.users[0]; - newUser.id.should.equal(testUtils.DataGenerator.Content.users[0].id); - newUser.name.should.equal('test user'); - newUser.email.should.equal('test@example.com'); - - mailService.GhostMailer.prototype.send.called.should.be.false(); - }); - }); - - it('is setup? yes', function () { - return request - .get(localUtils.API.getApiQuery('authentication/setup')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect(200) - .then((res) => { - res.body.setup[0].status.should.be.true(); - }); - }); - - it('complete setup again', function () { - return request - .post(localUtils.API.getApiQuery('authentication/setup')) - .set('Origin', config.get('url')) - .send({ - setup: [{ - name: 'test user', - email: 'test-leo@example.com', - password: 'thisissupersafe', - blogTitle: 'a test blog' - }] - }) - .expect('Content-Type', /json/) - .expect(403); - }); - - it('update setup', function () { - return localUtils.doAuth(request) - .then(() => { - return request - .put(localUtils.API.getApiQuery('authentication/setup')) - .set('Origin', config.get('url')) - .send({ - setup: [{ - name: 'test user edit', - email: 'test-edit@example.com', - password: 'thisissupersafe', - blogTitle: 'a test blog' - }] - }) - .expect('Content-Type', /json/) - .expect(200); - }) - .then((res) => { - const jsonResponse = res.body; - should.exist(jsonResponse.users); - should.not.exist(jsonResponse.meta); - - jsonResponse.users.should.have.length(1); - localUtils.API.checkResponse(jsonResponse.users[0], 'user'); - - const newUser = jsonResponse.users[0]; - newUser.id.should.equal(testUtils.DataGenerator.Content.users[0].id); - newUser.name.should.equal('test user edit'); - newUser.email.should.equal('test-edit@example.com'); - }); - }); - }); - - describe('Invitation', function () { - before(function () { - return localUtils.startGhost() - .then(function () { - request = supertest.agent(config.get('url')); - - // simulates blog setup (initialises the owner) - return localUtils.doAuth(request, 'invites'); - }); - }); - - it('check invite with invalid email', function () { - return request - .get(localUtils.API.getApiQuery('authentication/invitation?email=invalidemail')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect(400); - }); - - it('check valid invite', function () { - return request - .get(localUtils.API.getApiQuery(`authentication/invitation?email=${testUtils.DataGenerator.forKnex.invites[0].email}`)) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect(200) - .then((res) => { - res.body.invitation[0].valid.should.equal(true); - }); - }); - - it('check invalid invite', function () { - return request - .get(localUtils.API.getApiQuery(`authentication/invitation?email=notinvited@example.org`)) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect(200) - .then((res) => { - res.body.invitation[0].valid.should.equal(false); - }); - }); - - it('try to accept without invite', function () { - return request - .post(localUtils.API.getApiQuery('authentication/invitation')) - .set('Origin', config.get('url')) - .send({ - invitation: [{ - token: 'lul11111', - password: 'lel123456', - email: 'not-invited@example.org', - name: 'not invited' - }] - }) - .expect('Content-Type', /json/) - .expect(404); - }); - - it('try to accept with invite', function () { - return request - .post(localUtils.API.getApiQuery('authentication/invitation')) - .set('Origin', config.get('url')) - .send({ - invitation: [{ - token: testUtils.DataGenerator.forKnex.invites[0].token, - password: '12345678910', - email: testUtils.DataGenerator.forKnex.invites[0].email, - name: 'invited' - }] - }) - .expect('Content-Type', /json/) - .expect(200) - .then((res) => { - res.body.invitation[0].message.should.equal('Invitation accepted.'); - }); - }); - }); - - describe('Password reset', function () { - const user = testUtils.DataGenerator.forModel.users[0]; - - before(function () { - return localUtils.startGhost({forceStart: true}) - .then(() => { - request = supertest.agent(config.get('url')); - }) - .then(() => { - return localUtils.doAuth(request); - }); - }); - - beforeEach(function () { - sinon.stub(mailService.GhostMailer.prototype, 'send').resolves('Mail is disabled'); - }); - - afterEach(function () { - sinon.restore(); - }); - - it('reset password', function (done) { - models.User.getOwnerUser(testUtils.context.internal) - .then(function (ownerUser) { - const token = security.tokens.resetToken.generateHash({ - expires: Date.now() + (1000 * 60), - email: user.email, - dbHash: settingsCache.get('db_hash'), - password: ownerUser.get('password') - }); - - request.put(localUtils.API.getApiQuery('authentication/passwordreset')) - .set('Origin', config.get('url')) - .set('Accept', 'application/json') - .send({ - passwordreset: [{ - token: token, - newPassword: 'thisissupersafe', - ne2Password: 'thisissupersafe' - }] - }) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .end(function (err, res) { - if (err) { - return done(err); - } - - const jsonResponse = res.body; - should.exist(jsonResponse.passwordreset[0].message); - jsonResponse.passwordreset[0].message.should.equal('Password changed successfully.'); - done(); - }); - }) - .catch(done); - }); - - it('reset password: invalid token', function () { - return request - .put(localUtils.API.getApiQuery('authentication/passwordreset')) - .set('Origin', config.get('url')) - .set('Accept', 'application/json') - .send({ - passwordreset: [{ - token: 'invalid', - newPassword: 'thisissupersafe', - ne2Password: 'thisissupersafe' - }] - }) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(401) - .then((res) => { - should.exist(res.body.errors); - res.body.errors[0].type.should.eql('UnauthorizedError'); - res.body.errors[0].message.should.eql('Cannot reset password.'); - }); - }); - - it('reset password: generate reset token', function () { - return request - .post(localUtils.API.getApiQuery('authentication/passwordreset')) - .set('Origin', config.get('url')) - .set('Accept', 'application/json') - .send({ - passwordreset: [{ - email: user.email - }] - }) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .then((res) => { - const jsonResponse = res.body; - should.exist(jsonResponse.passwordreset[0].message); - jsonResponse.passwordreset[0].message.should.equal('Check your email for further instructions.'); - mailService.GhostMailer.prototype.send.args[0][0].to.should.equal(user.email); - }); - }); - }); -}); diff --git a/test/regression/api/v2/admin/db.test.js b/test/regression/api/v2/admin/db.test.js deleted file mode 100644 index a3e941b62d..0000000000 --- a/test/regression/api/v2/admin/db.test.js +++ /dev/null @@ -1,158 +0,0 @@ -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'); -const config = require('../../../../../core/shared/config'); -const events = require('../../../../../core/server/lib/common/events'); -const testUtils = require('../../../../utils'); -const localUtils = require('./utils'); - -let request; -let eventsTriggered; - -describe('DB API (v2)', function () { - let backupKey; - let schedulerKey; - - before(async function () { - await localUtils.startGhost(); - 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'}}); - }); - - beforeEach(function () { - eventsTriggered = {}; - - sinon.stub(events, 'emit').callsFake((eventName, eventObj) => { - if (!eventsTriggered[eventName]) { - eventsTriggered[eventName] = []; - } - - eventsTriggered[eventName].push(eventObj); - }); - }); - - afterEach(function () { - sinon.restore(); - }); - - it('can export the database with more tables', function () { - return request.get(localUtils.API.getApiQuery('db/?include=mobiledoc_revisions')) - .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); - - // NOTE: 10 default tables + 1 from include parameters - Object.keys(jsonResponse.db[0].data).length.should.eql(11); - }); - }); - - it('can export & import', function () { - 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) => { - res.body.problems.length.should.eql(3); - fs.removeSync(exportFolder); - }); - }); - - it('import should fail without file', function () { - return request.post(localUtils.API.getApiQuery('db/')) - .set('Origin', config.get('url')) - .set('Accept', 'application/json') - .expect('Content-Type', /json/) - .expect(422); - }); - - it('import should fail with unsupported file', function () { - return request.post(localUtils.API.getApiQuery('db/')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .attach('importfile', path.join(__dirname, '/../../../../utils/fixtures/csv/single-column-with-header.csv')) - .expect(415); - }); - - it('export can be triggered by backup integration', function () { - const backupQuery = `?filename=test`; - const fsStub = sinon.stub(fs, 'writeFile').resolves(); - - return request.post(localUtils.API.getApiQuery(`db/backup${backupQuery}`)) - .set('Authorization', `Ghost ${localUtils.getValidAdminToken('/v2/admin/', backupKey)}`) - .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); - }); - }); - - it('export can not be triggered by integration other than backup', function () { - const fsStub = sinon.stub(fs, 'writeFile').resolves(); - - return request.post(localUtils.API.getApiQuery(`db/backup`)) - .set('Authorization', `Ghost ${localUtils.getValidAdminToken('/v2/admin/', schedulerKey)}`) - .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); - }); - }); - - it('export can be triggered by Admin authentication', function () { - 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); - }); -}); diff --git a/test/regression/api/v2/admin/notifications.test.js b/test/regression/api/v2/admin/notifications.test.js deleted file mode 100644 index 03375492a1..0000000000 --- a/test/regression/api/v2/admin/notifications.test.js +++ /dev/null @@ -1,179 +0,0 @@ -const should = require('should'); -const supertest = require('supertest'); -const testUtils = require('../../../../utils'); -const config = require('../../../../../core/shared/config'); -const localUtils = require('./utils'); - -describe('Notifications API', function () { - describe('As Editor', function () { - let request; - - before(async function () { - await localUtils.startGhost(); - request = supertest.agent(config.get('url')); - const user = await testUtils.createUser({ - user: testUtils.DataGenerator.forKnex.createUser({ - email: 'test+editor@ghost.org' - }), - role: testUtils.DataGenerator.Content.roles[1].name - }); - - request.user = user; - await localUtils.doAuth(request); - }); - - it('Add notification', function () { - const newNotification = { - type: 'info', - message: 'test notification', - custom: true - }; - - return request.post(localUtils.API.getApiQuery('notifications/')) - .set('Origin', config.get('url')) - .send({notifications: [newNotification]}) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(201) - .then((res) => { - const jsonResponse = res.body; - - should.exist(jsonResponse); - should.exist(jsonResponse.notifications); - should.equal(jsonResponse.notifications.length, 1); - }); - }); - - it('Read notifications', function () { - return request.get(localUtils.API.getApiQuery('notifications/')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .then((res) => { - const jsonResponse = res.body; - - should.exist(jsonResponse); - should.exist(jsonResponse.notifications); - should.equal(jsonResponse.notifications.length, 1); - }); - }); - }); - - describe('As Author', function () { - let request; - - before(async function () { - await localUtils.startGhost(); - - request = supertest.agent(config.get('url')); - const user = await testUtils.createUser({ - user: testUtils.DataGenerator.forKnex.createUser({ - email: 'test+author@ghost.org' - }), - role: testUtils.DataGenerator.Content.roles[2].name - }); - request.user = user; - await localUtils.doAuth(request); - }); - - it('Add notification', function () { - const newNotification = { - type: 'info', - message: 'test notification', - custom: true - }; - - return request.post(localUtils.API.getApiQuery('notifications/')) - .set('Origin', config.get('url')) - .send({notifications: [newNotification]}) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(403); - }); - - it('Read notifications', function () { - return request.get(localUtils.API.getApiQuery('notifications/')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(403); - }); - }); - - describe('Can view by multiple users', function () { - let requestEditor1; - let requestEditor2; - let notification; - - before(async function () { - await localUtils.startGhost(); - - requestEditor1 = supertest.agent(config.get('url')); - requestEditor2 = supertest.agent(config.get('url')); - - const editor1User = await testUtils.createUser({ - user: testUtils.DataGenerator.forKnex.createUser({ - email: 'test+editor1@ghost.org' - }), - role: testUtils.DataGenerator.Content.roles[1].name - }); - requestEditor1.user = editor1User; - await localUtils.doAuth(requestEditor1); - - const editor2User = await testUtils.createUser({ - user: testUtils.DataGenerator.forKnex.createUser({ - email: 'test+editor2@ghost.org' - }), - role: testUtils.DataGenerator.Content.roles[1].name - }); - requestEditor2.user = editor2User; - await localUtils.doAuth(requestEditor2); - - const newNotification = { - type: 'info', - message: 'multiple views', - custom: true - }; - - await requestEditor1.post(localUtils.API.getApiQuery('notifications/')) - .set('Origin', config.get('url')) - .send({notifications: [newNotification]}) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(201) - .then((res) => { - notification = res.body.notifications[0]; - }); - }); - - it('notification is visible and dismissible by other user', function () { - return requestEditor1.del(localUtils.API.getApiQuery(`notifications/${notification.id}`)) - .set('Origin', config.get('url')) - .expect(204) - .then(() => { - return requestEditor2.get(localUtils.API.getApiQuery(`notifications/`)) - .set('Origin', config.get('url')) - .expect(200) - .then(function (res) { - const deleted = res.body.notifications.filter(n => n.id === notification.id); - deleted.should.not.be.empty(); - }); - }) - .then(() => { - return requestEditor2.del(localUtils.API.getApiQuery(`notifications/${notification.id}`)) - .set('Origin', config.get('url')) - .expect(204); - }) - .then(() => { - return requestEditor2.get(localUtils.API.getApiQuery(`notifications/`)) - .set('Origin', config.get('url')) - .expect(200) - .then(function (res) { - const deleted = res.body.notifications.filter(n => n.id === notification.id); - deleted.should.be.empty(); - }); - }); - }); - }); -}); diff --git a/test/regression/api/v2/admin/oembed.test.js b/test/regression/api/v2/admin/oembed.test.js deleted file mode 100644 index 82f495acc1..0000000000 --- a/test/regression/api/v2/admin/oembed.test.js +++ /dev/null @@ -1,346 +0,0 @@ -const nock = require('nock'); -const sinon = require('sinon'); -const should = require('should'); -const supertest = require('supertest'); -const testUtils = require('../../../../utils/index'); -const config = require('../../../../../core/shared/config/index'); -const localUtils = require('./utils'); - -// for sinon stubs -const dnsPromises = require('dns').promises; - -describe('Oembed API (v2)', function () { - let request; - - before(function () { - return localUtils.startGhost() - .then(() => { - request = supertest.agent(config.get('url')); - }) - .then(() => { - return localUtils.doAuth(request); - }); - }); - - beforeEach(function () { - // ensure sure we're not network dependent - sinon.stub(dnsPromises, 'lookup').callsFake(function () { - return Promise.resolve({address: '123.123.123.123'}); - }); - }); - - afterEach(function () { - sinon.restore(); - nock.cleanAll(); - }); - - it('can fetch an embed', function (done) { - let requestMock = nock('https://www.youtube.com') - .get('/oembed') - .query(true) - .reply(200, { - html: '', - thumbnail_width: 480, - width: 480, - author_url: 'https://www.youtube.com/user/gorillaz', - height: 270, - thumbnail_height: 360, - provider_name: 'YouTube', - title: 'Gorillaz - Humility (Official Video)', - provider_url: 'https://www.youtube.com/', - author_name: 'Gorillaz', - version: '1.0', - thumbnail_url: 'https://i.ytimg.com/vi/E5yFcdPAGv0/hqdefault.jpg', - type: 'video' - }); - - request.get(localUtils.API.getApiQuery('oembed/?url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DE5yFcdPAGv0')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .end(function (err, res) { - if (err) { - return done(err); - } - - requestMock.isDone().should.be.true(); - should.exist(res.body.html); - done(); - }); - }); - - describe('with unknown provider', function () { - it('fetches url and follows ', function (done) { - const pageMock = nock('http://test.com') - .get('/') - .reply(200, ''); - - const oembedMock = nock('http://test.com') - .get('/oembed') - .reply(200, { - version: '1.0', - type: 'link' - }); - - const url = encodeURIComponent('http://test.com/'); - request.get(localUtils.API.getApiQuery(`oembed/?url=${url}`)) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .end(function (err, res) { - if (err) { - return done(err); - } - pageMock.isDone().should.be.true(); - oembedMock.isDone().should.be.true(); - done(); - }); - }); - - it('rejects invalid oembed responses', function (done) { - const pageMock = nock('http://test.com') - .get('/') - .reply(200, ''); - - const oembedMock = nock('http://test.com') - .get('/oembed') - .reply(200, { - version: '1.0', - html: 'test' - }); - - const url = encodeURIComponent('http://test.com/'); - request.get(localUtils.API.getApiQuery(`oembed/?url=${url}`)) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(422) - .end(function (err, res) { - if (err) { - return done(err); - } - pageMock.isDone().should.be.true(); - oembedMock.isDone().should.be.true(); - done(); - }); - }); - - it('rejects unknown oembed types', function (done) { - const pageMock = nock('http://test.com') - .get('/') - .reply(200, ''); - - const oembedMock = nock('http://test.com') - .get('/oembed') - .reply(200, { - version: '1.0', - type: 'unknown' - }); - - const url = encodeURIComponent('http://test.com/'); - request.get(localUtils.API.getApiQuery(`oembed/?url=${url}`)) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(422) - .end(function (err, res) { - if (err) { - return done(err); - } - pageMock.isDone().should.be.true(); - oembedMock.isDone().should.be.true(); - done(); - }); - }); - - it('rejects invalid photo responses', function (done) { - const pageMock = nock('http://test.com') - .get('/') - .reply(200, ''); - - const oembedMock = nock('http://test.com') - .get('/oembed') - .reply(200, { - // no `url` field - version: '1.0', - type: 'photo', - thumbnail_url: 'https://test.com/thumbnail.jpg' - }); - - const url = encodeURIComponent('http://test.com/'); - request.get(localUtils.API.getApiQuery(`oembed/?url=${url}`)) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(422) - .end(function (err, res) { - if (err) { - return done(err); - } - pageMock.isDone().should.be.true(); - oembedMock.isDone().should.be.true(); - done(); - }); - }); - - it('rejects invalid video responses', function (done) { - const pageMock = nock('http://test.com') - .get('/') - .reply(200, ''); - - const oembedMock = nock('http://test.com') - .get('/oembed') - .reply(200, { - // no `html` field - version: '1.0', - type: 'video', - thumbnail_url: 'https://test.com/thumbnail.jpg' - }); - - const url = encodeURIComponent('http://test.com/'); - request.get(localUtils.API.getApiQuery(`oembed/?url=${url}`)) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(422) - .end(function (err, res) { - if (err) { - return done(err); - } - pageMock.isDone().should.be.true(); - oembedMock.isDone().should.be.true(); - done(); - }); - }); - - it('strips unknown response fields', function (done) { - const pageMock = nock('http://test.com') - .get('/') - .reply(200, ''); - - const oembedMock = nock('http://test.com') - .get('/oembed') - .reply(200, { - version: '1.0', - type: 'video', - html: '

Test

', - width: 200, - height: 100, - unknown: 'test' - }); - - const url = encodeURIComponent('http://test.com'); - request.get(localUtils.API.getApiQuery(`oembed/?url=${url}`)) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .end(function (err, res) { - if (err) { - return done(err); - } - pageMock.isDone().should.be.true(); - oembedMock.isDone().should.be.true(); - - res.body.should.deepEqual({ - version: '1.0', - type: 'video', - html: '

Test

', - width: 200, - height: 100 - }); - should.not.exist(res.body.unknown); - - done(); - }); - }); - - it('skips fetching IPv4 addresses', function (done) { - const pageMock = nock('http://test.com') - .get('/') - .reply(200, ''); - - const oembedMock = nock('http://192.168.0.1') - .get('/oembed') - .reply(200, { - version: '1.0', - type: 'link' - }); - - const url = encodeURIComponent('http://test.com/'); - request.get(localUtils.API.getApiQuery(`oembed/?url=${url}`)) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(422) - .end(function (err, res) { - if (err) { - return done(err); - } - pageMock.isDone().should.be.true(); - oembedMock.isDone().should.be.false(); - done(); - }); - }); - - it('skips fetching IPv6 addresses', function (done) { - const pageMock = nock('http://test.com') - .get('/') - .reply(200, ''); - - const oembedMock = nock('http://[2607:f0d0:1002:51::4]:9999') - .get('/oembed') - .reply(200, { - version: '1.0', - type: 'link' - }); - - const url = encodeURIComponent('http://test.com/'); - request.get(localUtils.API.getApiQuery(`oembed/?url=${url}`)) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(422) - .end(function (err, res) { - if (err) { - return done(err); - } - pageMock.isDone().should.be.true(); - oembedMock.isDone().should.be.false(); - done(); - }); - }); - - it('skips fetching localhost', function (done) { - const pageMock = nock('http://test.com') - .get('/') - .reply(200, ''); - - const oembedMock = nock('http://localhost:9999') - .get('/oembed') - .reply(200, { - // no `html` field - version: '1.0', - type: 'video', - thumbnail_url: 'https://test.com/thumbnail.jpg' - }); - - const url = encodeURIComponent('http://test.com/'); - request.get(localUtils.API.getApiQuery(`oembed/?url=${url}`)) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(422) - .end(function (err, res) { - if (err) { - return done(err); - } - pageMock.isDone().should.be.true(); - oembedMock.isDone().should.be.false(); - done(); - }); - }); - }); -}); diff --git a/test/regression/api/v2/admin/schedules.test.js b/test/regression/api/v2/admin/schedules.test.js deleted file mode 100644 index 63078ac528..0000000000 --- a/test/regression/api/v2/admin/schedules.test.js +++ /dev/null @@ -1,170 +0,0 @@ -const _ = require('lodash'); -const should = require('should'); -const supertest = require('supertest'); -const Promise = require('bluebird'); -const sinon = require('sinon'); -const moment = require('moment-timezone'); -const SchedulingDefault = require('../../../../../core/server/adapters/scheduling/SchedulingDefault'); -const models = require('../../../../../core/server/models/index'); -const config = require('../../../../../core/shared/config/index'); -const testUtils = require('../../../../utils/index'); -const localUtils = require('./utils'); - -describe('v2 Schedules API', function () { - const resources = []; - let request; - - before(function () { - models.init(); - - // @NOTE: mock the post scheduler, otherwise it will auto publish the post - sinon.stub(SchedulingDefault.prototype, '_pingUrl').resolves(); - }); - - after(function () { - sinon.restore(); - }); - - before(async function () { - await localUtils.startGhost(); - - request = supertest.agent(config.get('url')); - - resources.push(testUtils.DataGenerator.forKnex.createPost({ - created_by: testUtils.getExistingData().users[0].id, - author_id: testUtils.getExistingData().users[0].id, - published_by: testUtils.getExistingData().users[0].id, - published_at: moment().add(30, 'seconds').toDate(), - status: 'scheduled', - slug: 'first' - })); - - resources.push(testUtils.DataGenerator.forKnex.createPost({ - created_by: testUtils.getExistingData().users[0].id, - author_id: testUtils.getExistingData().users[0].id, - published_by: testUtils.getExistingData().users[0].id, - published_at: moment().subtract(30, 'seconds').toDate(), - status: 'scheduled', - slug: 'second' - })); - - resources.push(testUtils.DataGenerator.forKnex.createPost({ - created_by: testUtils.getExistingData().users[0].id, - author_id: testUtils.getExistingData().users[0].id, - published_by: testUtils.getExistingData().users[0].id, - published_at: moment().add(10, 'minute').toDate(), - status: 'scheduled', - slug: 'third' - })); - - resources.push(testUtils.DataGenerator.forKnex.createPost({ - created_by: testUtils.getExistingData().users[0].id, - author_id: testUtils.getExistingData().users[0].id, - published_by: testUtils.getExistingData().users[0].id, - published_at: moment().subtract(10, 'minute').toDate(), - status: 'scheduled', - slug: 'fourth' - })); - - resources.push(testUtils.DataGenerator.forKnex.createPost({ - created_by: testUtils.getExistingData().users[0].id, - author_id: testUtils.getExistingData().users[0].id, - published_by: testUtils.getExistingData().users[0].id, - published_at: moment().add(30, 'seconds').toDate(), - status: 'scheduled', - slug: 'fifth', - type: 'page' - })); - - const result = await Promise.mapSeries(resources, function (post) { - return models.Post.add(post, {context: {internal: true}}); - }); - - result.length.should.eql(5); - }); - - describe('publish', function () { - let token; - - before(function () { - const schedulerKey = _.find(testUtils.getExistingData().apiKeys, {integration: {slug: 'ghost-scheduler'}}); - - token = localUtils.getValidAdminToken('/v2/admin/', schedulerKey); - }); - - it('publishes posts', async function () { - const res = await request - .put(localUtils.API.getApiQuery(`schedules/posts/${resources[0].id}/?token=${token}`)) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200); - - should.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - should.exist(jsonResponse); - jsonResponse.posts[0].id.should.eql(resources[0].id); - jsonResponse.posts[0].status.should.eql('published'); - }); - - it('publishes page', async function () { - const res = await request - .put(localUtils.API.getApiQuery(`schedules/pages/${resources[4].id}/?token=${token}`)) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200); - - should.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - should.exist(jsonResponse); - jsonResponse.pages[0].id.should.eql(resources[4].id); - jsonResponse.pages[0].status.should.eql('published'); - }); - - it('no access', function () { - const zapierKey = _.find(testUtils.getExistingData().apiKeys, {integration: {slug: 'ghost-backup'}}); - const zapierToken = localUtils.getValidAdminToken('/v2/admin/', zapierKey); - - return request - .put(localUtils.API.getApiQuery(`schedules/posts/${resources[0].id}/?token=${zapierToken}`)) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(403); - }); - - it('should fail with invalid resource type', function () { - return request - .put(localUtils.API.getApiQuery(`schedules/this_is_invalid/${resources[0].id}/?token=${token}`)) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(422); - }); - - it('published_at is x seconds in past, but still in tolerance', function () { - return request - .put(localUtils.API.getApiQuery(`schedules/posts/${resources[1].id}/?token=${token}`)) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200); - }); - - it('not found', function () { - return request - .put(localUtils.API.getApiQuery(`schedules/posts/${resources[2].id}/?token=${token}`)) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(404); - }); - - it('force publish', function () { - return request - .put(localUtils.API.getApiQuery(`schedules/posts/${resources[3].id}/?token=${token}`)) - .send({ - force: true - }) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200); - }); - }); -}); diff --git a/test/regression/api/v2/admin/settings.test.js b/test/regression/api/v2/admin/settings.test.js deleted file mode 100644 index 4a984c6a11..0000000000 --- a/test/regression/api/v2/admin/settings.test.js +++ /dev/null @@ -1,842 +0,0 @@ -const should = require('should'); -const supertest = require('supertest'); -const config = require('../../../../../core/shared/config'); -const testUtils = require('../../../../utils'); -const localUtils = require('./utils'); - -// NOTE: in future iterations these fields should be fetched from a central module. -// Have put a list as is here for the lack of better place for it. -const defaultSettingsKeyTypes = [ - {key: 'title', type: 'blog'}, - {key: 'description', type: 'blog'}, - {key: 'logo', type: 'blog'}, - {key: 'cover_image', type: 'blog'}, - {key: 'icon', type: 'blog'}, - {key: 'codeinjection_head', type: 'blog'}, - {key: 'codeinjection_foot', type: 'blog'}, - {key: 'facebook', type: 'blog'}, - {key: 'twitter', type: 'blog'}, - {key: 'navigation', type: 'blog'}, - {key: 'secondary_navigation', type: 'blog'}, - {key: 'meta_title', type: 'blog'}, - {key: 'meta_description', type: 'blog'}, - {key: 'og_image', type: 'blog'}, - {key: 'og_title', type: 'blog'}, - {key: 'og_description', type: 'blog'}, - {key: 'twitter_image', type: 'blog'}, - {key: 'twitter_title', type: 'blog'}, - {key: 'twitter_description', type: 'blog'}, - {key: 'active_theme', type: 'theme'}, - {key: 'is_private', type: 'private'}, - {key: 'password', type: 'private'}, - {key: 'public_hash', type: 'private'}, - {key: 'default_content_visibility', type: 'members'}, - {key: 'members_signup_access', type: 'members'}, - {key: 'members_from_address', type: 'members'}, - {key: 'members_support_address', type: 'members'}, - {key: 'members_reply_address', type: 'members'}, - {key: 'members_paid_signup_redirect', type: 'members'}, - {key: 'members_free_signup_redirect', type: 'members'}, - {key: 'members_free_price_name', type: 'members'}, - {key: 'members_free_price_description', type: 'members'}, - {key: 'members_monthly_price_id', type: 'members'}, - {key: 'members_yearly_price_id', type: 'members'}, - {key: 'stripe_product_name', type: 'members'}, - {key: 'stripe_plans', type: 'members'}, - {key: 'stripe_secret_key', type: 'members'}, - {key: 'stripe_publishable_key', type: 'members'}, - {key: 'stripe_connect_secret_key', type: 'members'}, - {key: 'stripe_connect_publishable_key', type: 'members'}, - {key: 'stripe_connect_account_id', type: 'members'}, - {key: 'stripe_connect_display_name', type: 'members'}, - {key: 'stripe_connect_livemode', type: 'members'}, - {key: 'portal_name', type: 'portal'}, - {key: 'portal_button', type: 'portal'}, - {key: 'portal_plans', type: 'portal'}, - {key: 'portal_products', type: 'portal'}, - {key: 'portal_button_style', type: 'portal'}, - {key: 'portal_button_icon', type: 'portal'}, - {key: 'portal_button_signup_text', type: 'portal'}, - {key: 'mailgun_api_key', type: 'bulk_email'}, - {key: 'mailgun_domain', type: 'bulk_email'}, - {key: 'mailgun_base_url', type: 'bulk_email'}, - {key: 'email_track_opens', type: 'bulk_email'}, - {key: 'amp', type: 'blog'}, - {key: 'amp_gtag_id', type: 'blog'}, - {key: 'slack', type: 'blog'}, - {key: 'unsplash', type: 'blog'}, - {key: 'shared_views', type: 'blog'}, - {key: 'ghost_head', type: 'blog'}, - {key: 'ghost_foot', type: 'blog'}, - {key: 'active_timezone', type: 'blog'}, - {key: 'default_locale', type: 'blog'}, - {key: 'newsletter_show_badge', type: 'newsletter'}, - {key: 'newsletter_header_image', type: 'newsletter'}, - {key: 'newsletter_show_header_icon', type: 'newsletter'}, - {key: 'newsletter_show_header_title', type: 'newsletter'}, - {key: 'newsletter_title_alignment', type: 'newsletter'}, - {key: 'newsletter_title_font_category', type: 'newsletter'}, - {key: 'newsletter_show_feature_image', type: 'newsletter'}, - {key: 'newsletter_body_font_category', type: 'newsletter'}, - {key: 'newsletter_footer_content', type: 'newsletter'}, - {key: 'firstpromoter', type: 'firstpromoter'}, - {key: 'firstpromoter_id', type: 'firstpromoter'}, - {key: 'oauth_client_id', type: 'oauth'}, - {key: 'oauth_client_secret', type: 'oauth'}, - {key: 'editor_default_email_recipients', type: 'editor'}, - {key: 'editor_default_email_recipients_filter', type: 'editor'}, - {key: 'editor_is_launch_complete', type: 'editor'}, - {key: 'labs', type: 'blog'}, - {key: 'email_verification_required', type: 'bulk_email'} -]; - -describe('Settings API (v2)', function () { - let request; - - describe('As Owner', function () { - before(async function () { - await localUtils.startGhost(); - request = supertest.agent(config.get('url')); - await localUtils.doAuth(request); - }); - - it('Can request all settings', function () { - return request.get(localUtils.API.getApiQuery(`settings/`)) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .then((res) => { - should.not.exist(res.headers['x-cache-invalidate']); - - const jsonResponse = res.body; - should.exist(jsonResponse.settings); - should.exist(jsonResponse.meta); - - jsonResponse.settings.should.be.an.Object(); - const settings = jsonResponse.settings; - - should.equal(settings.length, defaultSettingsKeyTypes.length); - for (const defaultSetting of defaultSettingsKeyTypes) { - should.exist(settings.find((setting) => { - return setting.key === defaultSetting.key && setting.type === defaultSetting.type; - }), `Expected to find a setting with key ${defaultSetting.key} and type ${defaultSetting.type}`); - } - - const unsplash = settings.find(s => s.key === 'unsplash'); - should.exist(unsplash); - unsplash.value.should.equal(JSON.stringify({isActive: true})); - - localUtils.API.checkResponse(jsonResponse, 'settings'); - }); - }); - - it('Can request settings by type', function () { - return request.get(localUtils.API.getApiQuery(`settings/?type=theme`)) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .then((res) => { - should.not.exist(res.headers['x-cache-invalidate']); - - const jsonResponse = res.body; - should.exist(jsonResponse.settings); - should.exist(jsonResponse.meta); - - jsonResponse.settings.should.be.an.Object(); - const settings = jsonResponse.settings; - - Object.keys(settings).length.should.equal(1); - settings[0].key.should.equal('active_theme'); - settings[0].value.should.equal('casper'); - settings[0].type.should.equal('theme'); - - localUtils.API.checkResponse(jsonResponse, 'settings'); - }); - }); - - it('Can not request settings by group, returns all settings instead', function () { - return request.get(localUtils.API.getApiQuery(`settings/?group=theme`)) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .then((res) => { - should.not.exist(res.headers['x-cache-invalidate']); - - const jsonResponse = res.body; - should.exist(jsonResponse.settings); - should.exist(jsonResponse.meta); - - jsonResponse.settings.should.be.an.Object(); - const settings = jsonResponse.settings; - - Object.keys(settings).length.should.equal(defaultSettingsKeyTypes.length); - settings.map(s => s.key).sort().should.eql(defaultSettingsKeyTypes.map(s => s.key).sort()); - - localUtils.API.checkResponse(jsonResponse, 'settings'); - }); - }); - - it('Can request settings by type and ignores group ', function () { - return request.get(localUtils.API.getApiQuery(`settings/?group=theme&type=private`)) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .then((res) => { - should.not.exist(res.headers['x-cache-invalidate']); - - const jsonResponse = res.body; - should.exist(jsonResponse.settings); - should.exist(jsonResponse.meta); - - jsonResponse.settings.should.be.an.Object(); - const settings = jsonResponse.settings; - - Object.keys(settings).length.should.equal(3); - settings[0].key.should.equal('is_private'); - settings[0].value.should.equal(false); - settings[0].type.should.equal('private'); - - testUtils.API.checkResponseValue(jsonResponse.settings[0], ['id', 'key', 'value', 'type', 'flags', 'created_at', 'updated_at']); - localUtils.API.checkResponse(jsonResponse, 'settings'); - }); - }); - - it('Requesting core settings type returns no results', function () { - return request.get(localUtils.API.getApiQuery(`settings/?type=core`)) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .then((res) => { - should.not.exist(res.headers['x-cache-invalidate']); - - const jsonResponse = res.body; - should.exist(jsonResponse.settings); - should.exist(jsonResponse.meta); - - jsonResponse.settings.should.be.an.Object(); - const settings = jsonResponse.settings; - - Object.keys(settings).length.should.equal(0); - - localUtils.API.checkResponse(jsonResponse, 'settings'); - }); - }); - - it('Can\'t read core setting', function () { - return request - .get(localUtils.API.getApiQuery('settings/db_hash/')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(403); - }); - - it('Can\'t read permalinks', function (done) { - request.get(localUtils.API.getApiQuery('settings/permalinks/')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(404) - .end(function (err, res) { - if (err) { - return done(err); - } - - done(); - }); - }); - - it('Can\'t read slack_url introduced in v4', function (done) { - request.get(localUtils.API.getApiQuery('settings/slack_url/')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(404) - .end(function (err, res) { - if (err) { - return done(err); - } - - done(); - }); - }); - - it('Can read labs', async function () { - const res = await request.get(localUtils.API.getApiQuery('settings/labs/')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200); - - should.not.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - - should.exist(jsonResponse); - should.exist(jsonResponse.settings); - - jsonResponse.settings.length.should.eql(1); - testUtils.API.checkResponseValue(jsonResponse.settings[0], ['id', 'key', 'value', 'type', 'flags', 'created_at', 'updated_at']); - - const jsonObjectRegex = /^\{.*\}$/; // '{...}' - jsonResponse.settings[0].key.should.eql('labs'); - jsonResponse.settings[0].value.should.match(jsonObjectRegex); - }); - - it('Can read default_locale deprecated in v3', function (done) { - request.get(localUtils.API.getApiQuery('settings/default_locale/')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .end(function (err, res) { - if (err) { - return done(err); - } - - should.not.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - - should.exist(jsonResponse); - should.exist(jsonResponse.settings); - - jsonResponse.settings.length.should.eql(1); - - testUtils.API.checkResponseValue(jsonResponse.settings[0], ['id', 'key', 'value', 'type', 'flags', 'created_at', 'updated_at']); - jsonResponse.settings[0].key.should.eql('default_locale'); - done(); - }); - }); - - it('Can read active_timezone deprecated in v3', function (done) { - request.get(localUtils.API.getApiQuery('settings/active_timezone/')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .end(function (err, res) { - if (err) { - return done(err); - } - - should.not.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - - should.exist(jsonResponse); - should.exist(jsonResponse.settings); - - jsonResponse.settings.length.should.eql(1); - - testUtils.API.checkResponseValue(jsonResponse.settings[0], ['id', 'key', 'value', 'type', 'flags', 'created_at', 'updated_at']); - jsonResponse.settings[0].key.should.eql('active_timezone'); - done(); - }); - }); - - it('Can read ghost_head deprecated in v3', function (done) { - request.get(localUtils.API.getApiQuery('settings/ghost_head/')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .end(function (err, res) { - if (err) { - return done(err); - } - - should.not.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - - should.exist(jsonResponse); - should.exist(jsonResponse.settings); - - jsonResponse.settings.length.should.eql(1); - - testUtils.API.checkResponseValue(jsonResponse.settings[0], ['id', 'key', 'value', 'type', 'flags', 'created_at', 'updated_at']); - jsonResponse.settings[0].key.should.eql('ghost_head'); - done(); - }); - }); - - it('Can read codeinjection_foot renamed in v3', function (done) { - request.get(localUtils.API.getApiQuery('settings/codeinjection_foot/')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .end(function (err, res) { - if (err) { - return done(err); - } - - should.not.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - - should.exist(jsonResponse); - should.exist(jsonResponse.settings); - - jsonResponse.settings.length.should.eql(1); - - testUtils.API.checkResponseValue(jsonResponse.settings[0], ['id', 'key', 'value', 'type', 'flags', 'created_at', 'updated_at']); - jsonResponse.settings[0].key.should.eql('codeinjection_foot'); - done(); - }); - }); - - it('Can read slack renamed&reformatted in v4', function (done) { - request.get(localUtils.API.getApiQuery('settings/slack/')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .end(function (err, res) { - if (err) { - return done(err); - } - - should.not.exist(res.headers['x-cache-invalidate']); - - const jsonResponse = res.body; - - should.exist(jsonResponse); - should.exist(jsonResponse.settings); - - jsonResponse.settings.length.should.eql(1); - - testUtils.API.checkResponseValue(jsonResponse.settings[0], ['id', 'key', 'value', 'type', 'flags', 'created_at', 'updated_at']); - jsonResponse.settings[0].key.should.eql('slack'); - - done(); - }); - }); - - it('Format of unsplash stays same as pre v4 migration when reading', function (done) { - request.get(localUtils.API.getApiQuery('settings/unsplash/')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .end(function (err, res) { - if (err) { - return done(err); - } - - const jsonResponse = res.body; - - should.exist(jsonResponse); - should.exist(jsonResponse.settings); - - jsonResponse.settings.length.should.eql(1); - - testUtils.API.checkResponseValue(jsonResponse.settings[0], ['id', 'key', 'value', 'type', 'flags', 'created_at', 'updated_at']); - jsonResponse.settings[0].key.should.eql('unsplash'); - JSON.parse(jsonResponse.settings[0].value).should.eql({ - isActive: true - }); - - done(); - }); - }); - - it('Format of unsplash stays same as pre v4 migration when editing', function () { - const setting = { - settings: [{ - key: 'unsplash', - value: JSON.stringify({ - isActive: false - }) - }] - }; - - return request.put(localUtils.API.getApiQuery('settings/')) - .send(setting) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .then(function (res) { - const jsonResponse = res.body; - - should.exist(jsonResponse); - should.exist(jsonResponse.settings); - - jsonResponse.settings.length.should.eql(1); - - testUtils.API.checkResponseValue(jsonResponse.settings[0], ['id', 'key', 'value', 'type', 'flags', 'created_at', 'updated_at']); - jsonResponse.settings[0].key.should.eql('unsplash'); - JSON.parse(jsonResponse.settings[0].value).should.eql({ - isActive: false - }); - }); - }); - - it('Can\'t read non existent setting', function (done) { - request.get(localUtils.API.getApiQuery('settings/testsetting/')) - .set('Origin', config.get('url')) - .set('Accept', 'application/json') - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(404) - .end(function (err, res) { - if (err) { - return done(err); - } - - should.not.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - should.exist(jsonResponse); - should.exist(jsonResponse.errors); - testUtils.API.checkResponseValue(jsonResponse.errors[0], [ - 'message', - 'context', - 'type', - 'details', - 'property', - 'help', - 'code', - 'id' - ]); - done(); - }); - }); - - it('Can\'t edit permalinks', function (done) { - const settingToChange = { - settings: [{key: 'permalinks', value: '/:primary_author/:slug/'}] - }; - - request.put(localUtils.API.getApiQuery('settings/')) - .set('Origin', config.get('url')) - .send(settingToChange) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(404) - .end(function (err, res) { - if (err) { - return done(err); - } - - done(); - }); - }); - - it('Cannot edit labs keys', async function () { - const settingToChange = { - settings: [{ - key: 'labs', - value: JSON.stringify({ - activitypub: true, - gibberish: true - }) - }] - }; - - const res = await request.put(localUtils.API.getApiQuery('settings/')) - .set('Origin', config.get('url')) - .send(settingToChange) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200); - - const jsonResponse = res.body; - - should.exist(jsonResponse); - should.exist(jsonResponse.settings); - - jsonResponse.settings.length.should.eql(0); - }); - - it('Can\'t edit non existent setting', function () { - return request.get(localUtils.API.getApiQuery('settings/')) - .set('Origin', config.get('url')) - .set('Accept', 'application/json') - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .then(function (res) { - let jsonResponse = res.body; - const newValue = 'new value'; - should.exist(jsonResponse); - should.exist(jsonResponse.settings); - jsonResponse.settings = [{key: 'testvalue', value: newValue}]; - - return request.put(localUtils.API.getApiQuery('settings/')) - .set('Origin', config.get('url')) - .send(jsonResponse) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(404) - .then(function ({body, headers}) { - jsonResponse = body; - should.not.exist(headers['x-cache-invalidate']); - should.exist(jsonResponse.errors); - testUtils.API.checkResponseValue(jsonResponse.errors[0], [ - 'message', - 'context', - 'type', - 'details', - 'property', - 'help', - 'code', - 'id' - ]); - }); - }); - }); - - it('Will transform "1"', function () { - return request.get(localUtils.API.getApiQuery('settings/')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .then(function (res) { - const jsonResponse = res.body; - - const settingToChange = { - settings: [ - { - key: 'is_private', - value: '1' - } - ] - }; - - should.exist(jsonResponse); - should.exist(jsonResponse.settings); - - return request.put(localUtils.API.getApiQuery('settings/')) - .set('Origin', config.get('url')) - .send(settingToChange) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .then(function ({body, headers}) { - const putBody = body; - headers['x-cache-invalidate'].should.eql('/*'); - should.exist(putBody); - - putBody.settings[0].key.should.eql('is_private'); - putBody.settings[0].value.should.eql(true); - - localUtils.API.checkResponse(putBody, 'settings'); - }); - }); - }); - - it('Can edit multiple setting along with a deprecated one from v4', async function () { - const settingToChange = { - settings: [ - { - key: 'slack', - value: JSON.stringify([{ - url: 'https://newurl.tld/slack', - username: 'New Slack Username' - }]) - }, { - key: 'unsplash', - value: JSON.stringify({ - isActive: true - }) - }, { - key: 'title', - value: 'New Value' - } - - ] - }; - - const {body, headers} = await request.put(localUtils.API.getApiQuery('settings/')) - .set('Origin', config.get('url')) - .send(settingToChange) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200); - - const putBody = body; - headers['x-cache-invalidate'].should.eql('/*'); - should.exist(putBody); - - putBody.settings.length.should.equal(3); - - putBody.settings[0].key.should.eql('unsplash'); - should.equal(putBody.settings[0].value, JSON.stringify({ - isActive: true - })); - - putBody.settings[1].key.should.eql('title'); - should.equal(putBody.settings[1].value, 'New Value'); - - putBody.settings[2].key.should.eql('slack'); - should.equal(putBody.settings[2].value, JSON.stringify([{ - url: 'https://newurl.tld/slack', - username: 'New Slack Username' - }])); - - localUtils.API.checkResponse(putBody, 'settings'); - }); - - it('Ignores editing a setting introduced in v4', async function () { - const settingToChange = { - settings: [ - { - key: 'slack_username', - value: 'wont edit me' - } - ] - }; - - const {body, headers} = await request.put(localUtils.API.getApiQuery('settings/')) - .set('Origin', config.get('url')) - .send(settingToChange) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200); - - const putBody = body; - headers['x-cache-invalidate'].should.eql('/*'); - should.exist(putBody); - - putBody.settings.length.should.equal(0); - - localUtils.API.checkResponse(putBody, 'settings'); - }); - - it('Can only send array values for keys defined with array type', async function () { - const settingsToChange = { - settings: [ - {key: 'navigation', value: 'not an array'} - ] - }; - - await request.put(localUtils.API.getApiQuery('settings/')) - .set('Origin', config.get('url')) - .send(settingsToChange) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(422); - }); - }); - - describe('As Admin', function () { - before(async function () { - await localUtils.startGhost(); - request = supertest.agent(config.get('url')); - - // create admin - const admin = await testUtils.createUser({ - user: testUtils.DataGenerator.forKnex.createUser({email: 'admin+1@ghost.org'}), - role: testUtils.DataGenerator.Content.roles[0].name - }); - request.user = admin; - // by default we login with the owner - await localUtils.doAuth(request); - }); - }); - - describe('As Editor', function () { - before(async function () { - await localUtils.startGhost(); - request = supertest.agent(config.get('url')); - // create editor - request.user = await testUtils.createUser({ - user: testUtils.DataGenerator.forKnex.createUser({email: 'test+1@ghost.org'}), - role: testUtils.DataGenerator.Content.roles[1].name - }); - - // by default we login with the owner - await localUtils.doAuth(request); - }); - - it('should not be able to edit settings', function () { - return request.get(localUtils.API.getApiQuery('settings/')) - .set('Origin', config.get('url')) - .set('Accept', 'application/json') - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .then(function (res) { - let jsonResponse = res.body; - - should.exist(jsonResponse); - should.exist(jsonResponse.settings); - jsonResponse.settings = [{key: 'visibility', value: 'public'}]; - - return request.put(localUtils.API.getApiQuery('settings/')) - .set('Origin', config.get('url')) - .send(jsonResponse) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(403) - .then(function ({body, headers}) { - jsonResponse = body; - should.not.exist(headers['x-cache-invalidate']); - should.exist(jsonResponse.errors); - testUtils.API.checkResponseValue(jsonResponse.errors[0], [ - 'message', - 'context', - 'type', - 'details', - 'property', - 'help', - 'code', - 'id' - ]); - }); - }); - }); - }); - - describe('As Author', function () { - before(async function () { - await localUtils.startGhost(); - request = supertest.agent(config.get('url')); - - // create author - request.user = await testUtils.createUser({ - user: testUtils.DataGenerator.forKnex.createUser({email: 'test+2@ghost.org'}), - role: testUtils.DataGenerator.Content.roles[2].name - }); - - // by default we login with the owner - await localUtils.doAuth(request); - }); - - it('should not be able to edit settings', function () { - return request.get(localUtils.API.getApiQuery('settings/')) - .set('Origin', config.get('url')) - .set('Accept', 'application/json') - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .then(function (res) { - let jsonResponse = res.body; - should.exist(jsonResponse); - should.exist(jsonResponse.settings); - jsonResponse.settings = [{key: 'visibility', value: 'public'}]; - - return request.put(localUtils.API.getApiQuery('settings/')) - .set('Origin', config.get('url')) - .send(jsonResponse) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(403) - .then(function ({body, headers}) { - jsonResponse = body; - should.not.exist(headers['x-cache-invalidate']); - should.exist(jsonResponse.errors); - testUtils.API.checkResponseValue(jsonResponse.errors[0], [ - 'message', - 'context', - 'type', - 'details', - 'property', - 'help', - 'code', - 'id' - ]); - }); - }); - }); - }); -}); diff --git a/test/regression/api/v2/admin/slack.test.js b/test/regression/api/v2/admin/slack.test.js deleted file mode 100644 index 7701891ed4..0000000000 --- a/test/regression/api/v2/admin/slack.test.js +++ /dev/null @@ -1,40 +0,0 @@ -const should = require('should'); -const supertest = require('supertest'); -const sinon = require('sinon'); -const testUtils = require('../../../../utils'); -const localUtils = require('./utils'); -const config = require('../../../../../core/shared/config'); -const events = require('../../../../../core/server/lib/common/events'); - -let request; - -describe('Slack API', function () { - before(async function () { - await localUtils.startGhost(); - request = supertest.agent(config.get('url')); - await localUtils.doAuth(request); - }); - after(function () { - sinon.restore(); - }); - - it('Can post slack test', function (done) { - const eventSpy = sinon.spy(events, 'emit'); - request.post(localUtils.API.getApiQuery('slack/test/')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .end(function (err, res) { - if (err) { - return done(err); - } - - should.not.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - should.exist(jsonResponse); - eventSpy.calledWith('slack.test').should.be.true(); - done(); - }); - }); -}); diff --git a/test/regression/api/v2/admin/users.test.js b/test/regression/api/v2/admin/users.test.js deleted file mode 100644 index 51519634ac..0000000000 --- a/test/regression/api/v2/admin/users.test.js +++ /dev/null @@ -1,262 +0,0 @@ -const should = require('should'); -const supertest = require('supertest'); -const ObjectId = require('bson-objectid'); -const testUtils = require('../../../../utils'); -const config = require('../../../../../core/shared/config'); -const localUtils = require('./utils'); -let request; - -describe('User API', function () { - describe('As Owner', function () { - let otherAuthor; - - before(async function () { - await localUtils.startGhost(); - request = supertest.agent(config.get('url')); - - // create inactive user - otherAuthor = await testUtils.createUser({ - user: testUtils.DataGenerator.forKnex.createUser({email: 'test+3@ghost.org'}), - role: testUtils.DataGenerator.Content.roles[2].name - }); - - // by default we login with the owner - await localUtils.doAuth(request); - }); - - describe('Read', function () { - it('can\'t retrieve non existent user by id', function (done) { - request.get(localUtils.API.getApiQuery('users/' + ObjectId().toHexString() + '/')) - .set('Origin', config.get('url')) - .set('Accept', 'application/json') - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(404) - .end(function (err, res) { - if (err) { - return done(err); - } - - should.not.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - should.exist(jsonResponse); - should.exist(jsonResponse.errors); - testUtils.API.checkResponseValue(jsonResponse.errors[0], [ - 'message', - 'context', - 'type', - 'details', - 'property', - 'help', - 'code', - 'id' - ]); - done(); - }); - }); - - it('can\'t retrieve non existent user by slug', function (done) { - request.get(localUtils.API.getApiQuery('users/slug/blargh/')) - .set('Origin', config.get('url')) - .set('Accept', 'application/json') - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(404) - .end(function (err, res) { - if (err) { - return done(err); - } - - should.not.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - should.exist(jsonResponse); - should.exist(jsonResponse.errors); - testUtils.API.checkResponseValue(jsonResponse.errors[0], [ - 'message', - 'context', - 'type', - 'details', - 'property', - 'help', - 'code', - 'id' - ]); - done(); - }); - }); - }); - - describe('Edit', function () { - it('can change the other users password', function (done) { - request.put(localUtils.API.getApiQuery('users/password/')) - .set('Origin', config.get('url')) - .send({ - password: [{ - newPassword: 'superSecure', - ne2Password: 'superSecure', - user_id: otherAuthor.id - }] - }) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .end(function (err) { - if (err) { - return done(err); - } - - done(); - }); - }); - }); - - describe('Destroy', function () { - it('[failure] Destroy unknown user id', function (done) { - request.delete(localUtils.API.getApiQuery('users/' + ObjectId().toHexString())) - .set('Origin', config.get('url')) - .expect(404) - .end(function (err) { - if (err) { - return done(err); - } - - done(); - }); - }); - }); - }); - - describe('As Editor', function () { - let editor; - - before(async function () { - await localUtils.startGhost(); - request = supertest.agent(config.get('url')); - // create editor - editor = await testUtils.createUser({ - user: testUtils.DataGenerator.forKnex.createUser({email: 'test+1@ghost.org'}), - role: testUtils.DataGenerator.Content.roles[1].name - }); - - request.user = editor; - // by default we login with the owner - await localUtils.doAuth(request); - }); - - describe('success cases', function () { - it('can edit himself', function (done) { - request.put(localUtils.API.getApiQuery('users/' + editor.id + '/')) - .set('Origin', config.get('url')) - .send({ - users: [{id: editor.id, name: 'test'}] - }) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .end(function (err) { - if (err) { - return done(err); - } - - done(); - }); - }); - }); - - describe('error cases', function () { - it('can\'t edit the owner', function (done) { - request.put(localUtils.API.getApiQuery('users/' + testUtils.DataGenerator.Content.users[0].id + '/')) - .set('Origin', config.get('url')) - .send({ - users: [{ - id: testUtils.DataGenerator.Content.users[0].id - }] - }) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(403) - .end(function (err) { - if (err) { - return done(err); - } - - done(); - }); - }); - - it('Cannot transfer ownership to any other user', function () { - return request - .put(localUtils.API.getApiQuery('users/owner')) - .set('Origin', config.get('url')) - .send({ - owner: [{ - id: testUtils.getExistingData().users[1].id - }] - }) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(403); - }); - }); - }); - - describe('As Author', function () { - let author; - - before(async function () { - await localUtils.startGhost(); - request = supertest.agent(config.get('url')); - // create author - author = await testUtils.createUser({ - user: testUtils.DataGenerator.forKnex.createUser({email: 'test+2@ghost.org'}), - role: testUtils.DataGenerator.Content.roles[2].name - }); - - request.user = author; - // by default we login with the owner - await localUtils.doAuth(request); - }); - - describe('success cases', function () { - it('can edit himself', function (done) { - request.put(localUtils.API.getApiQuery('users/' + author.id + '/')) - .set('Origin', config.get('url')) - .send({ - users: [{id: author.id, name: 'test'}] - }) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .end(function (err) { - if (err) { - return done(err); - } - - done(); - }); - }); - }); - - describe('error cases', function () { - it('can\'t edit the owner', function (done) { - request.put(localUtils.API.getApiQuery('users/' + testUtils.DataGenerator.Content.users[0].id + '/')) - .set('Origin', config.get('url')) - .send({ - users: [{ - id: testUtils.DataGenerator.Content.users[0].id - }] - }) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(403) - .end(function (err) { - if (err) { - return done(err); - } - - done(); - }); - }); - }); - }); -}); diff --git a/test/regression/api/v3/admin/authentication.test.js b/test/regression/api/v3/admin/authentication.test.js deleted file mode 100644 index 39e07093d8..0000000000 --- a/test/regression/api/v3/admin/authentication.test.js +++ /dev/null @@ -1,392 +0,0 @@ -const should = require('should'); -const sinon = require('sinon'); -const supertest = require('supertest'); -const localUtils = require('./utils'); -const testUtils = require('../../../../utils/index'); -const models = require('../../../../../core/server/models/index'); -const security = require('@tryghost/security'); -const settingsCache = require('../../../../../core/shared/settings-cache'); -const config = require('../../../../../core/shared/config/index'); -const mailService = require('../../../../../core/server/services/mail/index'); - -let request; - -describe('Authentication API v3', function () { - describe('Blog setup', function () { - before(async function () { - await localUtils.startGhost({forceStart: true}); - request = supertest.agent(config.get('url')); - }); - - beforeEach(function () { - sinon.stub(mailService.GhostMailer.prototype, 'send').resolves('Mail is disabled'); - }); - - afterEach(function () { - sinon.restore(); - }); - - it('is setup? no', function () { - return request - .get(localUtils.API.getApiQuery('authentication/setup')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect(200) - .then((res) => { - res.body.setup[0].status.should.be.false(); - }); - }); - - it('complete setup', function () { - return request - .post(localUtils.API.getApiQuery('authentication/setup')) - .set('Origin', config.get('url')) - .send({ - setup: [{ - name: 'test user', - email: 'test@example.com', - password: 'thisissupersafe', - blogTitle: 'a test blog' - }] - }) - .expect('Content-Type', /json/) - .expect(201) - .then((res) => { - const jsonResponse = res.body; - should.exist(jsonResponse.users); - should.not.exist(jsonResponse.meta); - should.exist(res.headers['x-cache-invalidate']); - - jsonResponse.users.should.have.length(1); - localUtils.API.checkResponse(jsonResponse.users[0], 'user'); - - const newUser = jsonResponse.users[0]; - newUser.id.should.equal(testUtils.DataGenerator.Content.users[0].id); - newUser.name.should.equal('test user'); - newUser.email.should.equal('test@example.com'); - - mailService.GhostMailer.prototype.send.called.should.be.true(); - mailService.GhostMailer.prototype.send.args[0][0].to.should.equal('test@example.com'); - }); - }); - - it('is setup? yes', function () { - return request - .get(localUtils.API.getApiQuery('authentication/setup')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect(200) - .then((res) => { - res.body.setup[0].status.should.be.true(); - }); - }); - - it('complete setup again', function () { - return request - .post(localUtils.API.getApiQuery('authentication/setup')) - .set('Origin', config.get('url')) - .send({ - setup: [{ - name: 'test user', - email: 'test-leo@example.com', - password: 'thisissupersafe', - blogTitle: 'a test blog' - }] - }) - .expect('Content-Type', /json/) - .expect(403); - }); - - it('update setup', function () { - return localUtils.doAuth(request) - .then(() => { - return request - .put(localUtils.API.getApiQuery('authentication/setup')) - .set('Origin', config.get('url')) - .send({ - setup: [{ - name: 'test user edit', - email: 'test-edit@example.com', - password: 'thisissupersafe', - blogTitle: 'a test blog' - }] - }) - .expect('Content-Type', /json/) - .expect(200); - }) - .then((res) => { - const jsonResponse = res.body; - should.exist(jsonResponse.users); - should.not.exist(jsonResponse.meta); - - jsonResponse.users.should.have.length(1); - localUtils.API.checkResponse(jsonResponse.users[0], 'user'); - - const newUser = jsonResponse.users[0]; - newUser.id.should.equal(testUtils.DataGenerator.Content.users[0].id); - newUser.name.should.equal('test user edit'); - newUser.email.should.equal('test-edit@example.com'); - }); - }); - }); - - describe('Invitation', function () { - before(function () { - return localUtils.startGhost() - .then(function () { - request = supertest.agent(config.get('url')); - - // simulates blog setup (initialises the owner) - return localUtils.doAuth(request, 'invites'); - }); - }); - - it('check invite with invalid email', function () { - return request - .get(localUtils.API.getApiQuery('authentication/invitation?email=invalidemail')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect(400); - }); - - it('check valid invite', function () { - return request - .get(localUtils.API.getApiQuery(`authentication/invitation?email=${testUtils.DataGenerator.forKnex.invites[0].email}`)) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect(200) - .then((res) => { - res.body.invitation[0].valid.should.equal(true); - }); - }); - - it('check invalid invite', function () { - return request - .get(localUtils.API.getApiQuery(`authentication/invitation?email=notinvited@example.org`)) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect(200) - .then((res) => { - res.body.invitation[0].valid.should.equal(false); - }); - }); - - it('try to accept without invite', function () { - return request - .post(localUtils.API.getApiQuery('authentication/invitation')) - .set('Origin', config.get('url')) - .send({ - invitation: [{ - token: 'lul11111', - password: 'lel123456', - email: 'not-invited@example.org', - name: 'not invited' - }] - }) - .expect('Content-Type', /json/) - .expect(404); - }); - - it('try to accept with invite and existing email address', function () { - return request - .post(localUtils.API.getApiQuery('authentication/invitation')) - .set('Origin', config.get('url')) - .send({ - invitation: [{ - token: testUtils.DataGenerator.forKnex.invites[0].token, - password: '12345678910', - email: testUtils.DataGenerator.forKnex.users[0].email, - name: 'invited' - }] - }) - .expect('Content-Type', /json/) - .expect(422); - }); - - it('try to accept with invite', function () { - return request - .post(localUtils.API.getApiQuery('authentication/invitation')) - .set('Origin', config.get('url')) - .send({ - invitation: [{ - token: testUtils.DataGenerator.forKnex.invites[0].token, - password: '12345678910', - email: testUtils.DataGenerator.forKnex.invites[0].email, - name: 'invited' - }] - }) - .expect('Content-Type', /json/) - .expect(200) - .then((res) => { - res.body.invitation[0].message.should.equal('Invitation accepted.'); - }); - }); - }); - - describe('Password reset', function () { - const user = testUtils.DataGenerator.forModel.users[0]; - - before(function () { - return localUtils.startGhost({forceStart: true}) - .then(() => { - request = supertest.agent(config.get('url')); - }) - .then(() => { - return localUtils.doAuth(request); - }); - }); - - beforeEach(function () { - sinon.stub(mailService.GhostMailer.prototype, 'send').resolves('Mail is disabled'); - }); - - afterEach(function () { - sinon.restore(); - }); - - it('reset password', function (done) { - models.User.getOwnerUser(testUtils.context.internal) - .then(function (ownerUser) { - const token = security.tokens.resetToken.generateHash({ - expires: Date.now() + (1000 * 60), - email: user.email, - dbHash: settingsCache.get('db_hash'), - password: ownerUser.get('password') - }); - - request.put(localUtils.API.getApiQuery('authentication/passwordreset')) - .set('Origin', config.get('url')) - .set('Accept', 'application/json') - .send({ - passwordreset: [{ - token: token, - newPassword: 'thisissupersafe', - ne2Password: 'thisissupersafe' - }] - }) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .end(function (err, res) { - if (err) { - return done(err); - } - - const jsonResponse = res.body; - should.exist(jsonResponse.passwordreset[0].message); - jsonResponse.passwordreset[0].message.should.equal('Password changed successfully.'); - done(); - }); - }) - .catch(done); - }); - - it('reset password: invalid token', function () { - return request - .put(localUtils.API.getApiQuery('authentication/passwordreset')) - .set('Origin', config.get('url')) - .set('Accept', 'application/json') - .send({ - passwordreset: [{ - token: 'invalid', - newPassword: 'thisissupersafe', - ne2Password: 'thisissupersafe' - }] - }) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(401) - .then((res) => { - should.exist(res.body.errors); - res.body.errors[0].type.should.eql('UnauthorizedError'); - res.body.errors[0].message.should.eql('Cannot reset password.'); - res.body.errors[0].context.should.eql('Invalid password reset link.'); - }); - }); - - it('reset password: expired token', function () { - return models.User.getOwnerUser(testUtils.context.internal) - .then(function (ownerUser) { - const dateInThePast = Date.now() - (1000 * 60); - const token = security.tokens.resetToken.generateHash({ - expires: dateInThePast, - email: user.email, - dbHash: settingsCache.get('db_hash'), - password: ownerUser.get('password') - }); - - return request - .put(localUtils.API.getApiQuery('authentication/passwordreset')) - .set('Origin', config.get('url')) - .set('Accept', 'application/json') - .send({ - passwordreset: [{ - token: token, - newPassword: 'thisissupersafe', - ne2Password: 'thisissupersafe' - }] - }) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(400); - }) - .then((res) => { - should.exist(res.body.errors); - res.body.errors[0].type.should.eql('BadRequestError'); - res.body.errors[0].message.should.eql('Cannot reset password.'); - res.body.errors[0].context.should.eql('Password reset link expired.'); - }); - }); - - it('reset password: unmatched token', function () { - const token = security.tokens.resetToken.generateHash({ - expires: Date.now() + (1000 * 60), - email: user.email, - dbHash: settingsCache.get('db_hash'), - password: 'invalid_password' - }); - - return request - .put(localUtils.API.getApiQuery('authentication/passwordreset')) - .set('Origin', config.get('url')) - .set('Accept', 'application/json') - .send({ - passwordreset: [{ - token: token, - newPassword: 'thisissupersafe', - ne2Password: 'thisissupersafe' - }] - }) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(400) - .then((res) => { - should.exist(res.body.errors); - res.body.errors[0].type.should.eql('BadRequestError'); - res.body.errors[0].message.should.eql('Cannot reset password.'); - res.body.errors[0].context.should.eql('Password reset link has already been used.'); - }); - }); - - it('reset password: generate reset token', function () { - return request - .post(localUtils.API.getApiQuery('authentication/passwordreset')) - .set('Origin', config.get('url')) - .set('Accept', 'application/json') - .send({ - passwordreset: [{ - email: user.email - }] - }) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .then((res) => { - const jsonResponse = res.body; - should.exist(jsonResponse.passwordreset[0].message); - jsonResponse.passwordreset[0].message.should.equal('Check your email for further instructions.'); - mailService.GhostMailer.prototype.send.args[0][0].to.should.equal(user.email); - }); - }); - }); -}); diff --git a/test/regression/api/v3/admin/db.test.js b/test/regression/api/v3/admin/db.test.js deleted file mode 100644 index 1b751eb43b..0000000000 --- a/test/regression/api/v3/admin/db.test.js +++ /dev/null @@ -1,166 +0,0 @@ -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'); -const config = require('../../../../../core/shared/config'); -const events = require('../../../../../core/server/lib/common/events'); -const testUtils = require('../../../../utils'); -const localUtils = require('./utils'); - -let request; -let eventsTriggered; - -describe('DB API (v3)', function () { - let backupKey; - let schedulerKey; - - before(async function () { - await localUtils.startGhost(); - 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'}}); - }); - - beforeEach(function () { - eventsTriggered = {}; - - sinon.stub(events, 'emit').callsFake((eventName, eventObj) => { - if (!eventsTriggered[eventName]) { - eventsTriggered[eventName] = []; - } - - eventsTriggered[eventName].push(eventObj); - }); - }); - - afterEach(function () { - sinon.restore(); - }); - - it('can export the database with more tables', function () { - return request.get(localUtils.API.getApiQuery('db/?include=mobiledoc_revisions')) - .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); - - // NOTE: 10 default tables + 1 from include parameters - Object.keys(jsonResponse.db[0].data).length.should.eql(11); - }); - }); - - it('can export & import', function () { - 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) => { - res.body.problems.length.should.eql(3); - fs.removeSync(exportFolder); - }); - }); - - 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); - }); - - it('import should fail without file', function () { - return request.post(localUtils.API.getApiQuery('db/')) - .set('Origin', config.get('url')) - .set('Accept', 'application/json') - .expect('Content-Type', /json/) - .expect(422); - }); - - it('import should fail with unsupported file', function () { - return request.post(localUtils.API.getApiQuery('db/')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .attach('importfile', path.join(__dirname, '/../../../../utils/fixtures/csv/single-column-with-header.csv')) - .expect(415); - }); - - it('export can be triggered by backup integration', function () { - const backupQuery = `?filename=test`; - const fsStub = sinon.stub(fs, 'writeFile').resolves(); - - return request.post(localUtils.API.getApiQuery(`db/backup${backupQuery}`)) - .set('Authorization', `Ghost ${localUtils.getValidAdminToken('/v3/admin/', backupKey)}`) - .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); - }); - }); - - it('export can not be triggered by integration other than backup', function () { - const fsStub = sinon.stub(fs, 'writeFile').resolves(); - - return request.post(localUtils.API.getApiQuery(`db/backup`)) - .set('Authorization', `Ghost ${localUtils.getValidAdminToken('/v3/admin/', schedulerKey)}`) - .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); - }); - }); - - it('export can be triggered by Admin authentication', function () { - 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); - }); -}); diff --git a/test/regression/api/v3/admin/identities.test.js b/test/regression/api/v3/admin/identities.test.js deleted file mode 100644 index f8c220c60d..0000000000 --- a/test/regression/api/v3/admin/identities.test.js +++ /dev/null @@ -1,95 +0,0 @@ -const should = require('should'); -const supertest = require('supertest'); -const jwt = require('jsonwebtoken'); -const jwksClient = require('jwks-rsa'); -const testUtils = require('../../../../utils'); -const localUtils = require('./utils'); -const config = require('../../../../../core/shared/config'); - -let request; - -const verifyJWKS = (endpoint, token) => { - return new Promise((resolve, reject) => { - const client = jwksClient({ - jwksUri: endpoint - }); - - async function getKey(header, callback) { - const key = await client.getSigningKey(header.kid); - let signingKey = key.publicKey || key.rsaPublicKey; - callback(null, signingKey); - } - - jwt.verify(token, getKey, {}, (err, decoded) => { - if (err) { - reject(err); - } - - resolve(decoded); - }); - }); -}; - -describe('Identities API', function () { - describe('As Owner', function () { - before(async function () { - await localUtils.startGhost(); - request = supertest.agent(config.get('url')); - await localUtils.doAuth(request); - }); - - it('Can create JWT token and verify it afterwards with public jwks', function () { - let identity; - - return request - .get(localUtils.API.getApiQuery(`identities/`)) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .then((res) => { - should.not.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - should.exist(jsonResponse); - should.exist(jsonResponse.identities); - - identity = jsonResponse.identities[0]; - }) - .then(() => { - return verifyJWKS(`${request.app}/ghost/.well-known/jwks.json`, identity.token); - }) - .then((decoded) => { - decoded.sub.should.equal('jbloggs@example.com'); - }); - }); - }); - - describe('As non-Owner', function () { - before(function () { - return localUtils.startGhost() - .then(function () { - request = supertest.agent(config.get('url')); - }) - .then(function () { - return testUtils.createUser({ - user: testUtils.DataGenerator.forKnex.createUser({email: 'admin+1@ghost.org'}), - role: testUtils.DataGenerator.Content.roles[0].name - }); - }) - .then(function (admin) { - request.user = admin; - - return localUtils.doAuth(request); - }); - }); - - it('Cannot read', function () { - return request - .get(localUtils.API.getApiQuery(`identities/`)) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(403); - }); - }); -}); diff --git a/test/regression/api/v3/admin/labels.test.js b/test/regression/api/v3/admin/labels.test.js deleted file mode 100644 index 1e63232118..0000000000 --- a/test/regression/api/v3/admin/labels.test.js +++ /dev/null @@ -1,64 +0,0 @@ -const path = require('path'); -const should = require('should'); -const supertest = require('supertest'); -const sinon = require('sinon'); -const testUtils = require('../../../../utils'); -const localUtils = require('./utils'); -const config = require('../../../../../core/shared/config'); - -let request; - -describe('Labels API', function () { - after(function () { - sinon.restore(); - }); - - before(async function () { - await localUtils.startGhost(); - request = supertest.agent(config.get('url')); - await localUtils.doAuth(request); - }); - - it('Errors when adding label with the same name', function () { - const label = { - name: 'test' - }; - - return request - .post(localUtils.API.getApiQuery(`labels/`)) - .send({labels: [label]}) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(201) - .then((res) => { - should.not.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - should.exist(jsonResponse); - should.exist(jsonResponse.labels); - jsonResponse.labels.should.have.length(1); - - jsonResponse.labels[0].name.should.equal(label.name); - jsonResponse.labels[0].slug.should.equal(label.name); - }) - .then(() => { - return request - .post(localUtils.API.getApiQuery(`labels/`)) - .send({labels: [label]}) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(422); - }) - .then((res) => { - should.not.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - should.exist(jsonResponse); - should.exist(jsonResponse.errors); - jsonResponse.errors.should.have.length(1); - - jsonResponse.errors[0].type.should.equal('ValidationError'); - jsonResponse.errors[0].context.should.equal('Label already exists'); - }); - }); -}); diff --git a/test/regression/api/v3/admin/members.test.js b/test/regression/api/v3/admin/members.test.js deleted file mode 100644 index 950004755e..0000000000 --- a/test/regression/api/v3/admin/members.test.js +++ /dev/null @@ -1,569 +0,0 @@ -const path = require('path'); -const querystring = require('querystring'); -const should = require('should'); -const supertest = require('supertest'); -const sinon = require('sinon'); -const testUtils = require('../../../../utils'); -const localUtils = require('./utils'); -const config = require('../../../../../core/shared/config'); -const labs = require('../../../../../core/shared/labs'); -const mailService = require('../../../../../core/server/services/mail'); - -let request; - -describe('Members API', function () { - before(function () { - sinon.stub(labs, 'isSet').withArgs('members').returns(true); - }); - - after(function () { - sinon.restore(); - }); - - before(async function () { - await localUtils.startGhost(); - request = supertest.agent(config.get('url')); - await localUtils.doAuth(request, 'members'); - }); - - beforeEach(function () { - sinon.stub(mailService.GhostMailer.prototype, 'send').resolves('Mail is disabled'); - }); - - afterEach(function () { - sinon.restore(); - }); - - it('Can add and send a signup confirmation email', async function () { - const member = { - name: 'Send Me Confirmation', - email: 'member_getting_confirmation@test.com', - subscribed: true - }; - - const queryParams = { - send_email: true, - email_type: 'signup' - }; - - const res = await request - .post(localUtils.API.getApiQuery(`members/?${querystring.stringify(queryParams)}`)) - .send({members: [member]}) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(201); - - should.not.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - should.exist(jsonResponse); - should.exist(jsonResponse.members); - jsonResponse.members.should.have.length(1); - - jsonResponse.members[0].name.should.equal(member.name); - jsonResponse.members[0].email.should.equal(member.email); - jsonResponse.members[0].subscribed.should.equal(member.subscribed); - testUtils.API.isISO8601(jsonResponse.members[0].created_at).should.be.true(); - - should.exist(res.headers.location); - res.headers.location.should.equal(`http://127.0.0.1:2369${localUtils.API.getApiQuery('members/')}${res.body.members[0].id}/`); - - mailService.GhostMailer.prototype.send.called.should.be.true(); - mailService.GhostMailer.prototype.send.args[0][0].to.should.equal('member_getting_confirmation@test.com'); - - await request - .delete(localUtils.API.getApiQuery(`members/${jsonResponse.members[0].id}/`)) - .set('Origin', config.get('url')) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(204); - }); - - it('Can order by email_open_rate', async function () { - await request - .get(localUtils.API.getApiQuery('members/?order=email_open_rate%20desc')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .then((res) => { - should.not.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - should.exist(jsonResponse.members); - localUtils.API.checkResponse(jsonResponse, 'members'); - jsonResponse.members.should.have.length(8); - - jsonResponse.members[0].email.should.equal('paid@test.com'); - jsonResponse.members[0].email_open_rate.should.equal(80); - jsonResponse.members[1].email.should.equal('member2@test.com'); - jsonResponse.members[1].email_open_rate.should.equal(50); - jsonResponse.members[2].email.should.equal('member1@test.com'); - should.equal(null, jsonResponse.members[2].email_open_rate); - jsonResponse.members[3].email.should.equal('trialing@test.com'); - should.equal(null, jsonResponse.members[3].email_open_rate); - }); - - await request - .get(localUtils.API.getApiQuery('members/?order=email_open_rate%20asc')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .then((res) => { - const jsonResponse = res.body; - localUtils.API.checkResponse(jsonResponse, 'members'); - jsonResponse.members.should.have.length(8); - - jsonResponse.members[0].email.should.equal('member2@test.com'); - jsonResponse.members[0].email_open_rate.should.equal(50); - jsonResponse.members[1].email.should.equal('paid@test.com'); - jsonResponse.members[1].email_open_rate.should.equal(80); - jsonResponse.members[2].email.should.equal('member1@test.com'); - should.equal(null, jsonResponse.members[2].email_open_rate); - jsonResponse.members[3].email.should.equal('trialing@test.com'); - should.equal(null, jsonResponse.members[3].email_open_rate); - }); - }); - - it('Can search by case-insensitive name', function () { - return request - .get(localUtils.API.getApiQuery('members/?search=egg')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .then((res) => { - should.not.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - should.exist(jsonResponse); - should.exist(jsonResponse.members); - jsonResponse.members.should.have.length(1); - jsonResponse.members[0].email.should.equal('member1@test.com'); - localUtils.API.checkResponse(jsonResponse, 'members'); - localUtils.API.checkResponse(jsonResponse.members[0], 'member', 'stripe'); - localUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination'); - }); - }); - - it('Can search by case-insensitive email', function () { - return request - .get(localUtils.API.getApiQuery('members/?search=MEMBER2')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .then((res) => { - should.not.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - should.exist(jsonResponse); - should.exist(jsonResponse.members); - jsonResponse.members.should.have.length(1); - jsonResponse.members[0].email.should.equal('member2@test.com'); - localUtils.API.checkResponse(jsonResponse, 'members'); - localUtils.API.checkResponse(jsonResponse.members[0], 'member', 'stripe'); - localUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination'); - }); - }); - - it('Can search for paid members', function () { - return request - .get(localUtils.API.getApiQuery('members/?search=egon&paid=true')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .then((res) => { - should.not.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - should.exist(jsonResponse); - should.exist(jsonResponse.members); - jsonResponse.members.should.have.length(1); - jsonResponse.members[0].email.should.equal('paid@test.com'); - localUtils.API.checkResponse(jsonResponse, 'members'); - localUtils.API.checkResponse(jsonResponse.members[0], 'member', 'stripe'); - localUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination'); - }); - }); - - it('Search for non existing member returns empty result set', function () { - return request - .get(localUtils.API.getApiQuery('members/?search=do_not_exist')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .then((res) => { - should.not.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - should.exist(jsonResponse); - should.exist(jsonResponse.members); - jsonResponse.members.should.have.length(0); - }); - }); - - it('Add should fail when passing incorrect email_type query parameter', function () { - const member = { - name: 'test', - email: 'memberTestAdd@test.com' - }; - - return request - .post(localUtils.API.getApiQuery(`members/?send_email=true&email_type=lel`)) - .send({members: [member]}) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(422); - }); - - it('Add should fail when comped flag is passed in but Stripe is not enabled', function () { - const member = { - email: 'memberTestAdd@test.com', - comped: true - }; - - return request - .post(localUtils.API.getApiQuery(`members/`)) - .send({members: [member]}) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(422) - .then((res) => { - const jsonResponse = res.body; - - should.exist(jsonResponse); - should.exist(jsonResponse.errors); - - jsonResponse.errors[0].message.should.eql('Validation error, cannot save member.'); - jsonResponse.errors[0].context.should.match(/Missing Stripe connection./); - }); - }); - - it('Can delete a member without cancelling Stripe Subscription', async function () { - const member = { - name: 'Member 2 Delete', - email: 'Member2Delete@test.com' - }; - - const createdMember = await request.post(localUtils.API.getApiQuery(`members/`)) - .send({members: [member]}) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(201) - .then((res) => { - should.not.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - should.exist(jsonResponse); - should.exist(jsonResponse.members); - jsonResponse.members.should.have.length(1); - - return jsonResponse.members[0]; - }); - - await request.delete(localUtils.API.getApiQuery(`members/${createdMember.id}/`)) - .set('Origin', config.get('url')) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(204) - .then((res) => { - should.not.exist(res.headers['x-cache-invalidate']); - - const jsonResponse = res.body; - - should.exist(jsonResponse); - }); - }); - - it('Can import CSV with minimum one field and labels', function () { - let importLabel; - - return request - .post(localUtils.API.getApiQuery(`members/upload/`)) - .field('labels', ['global-label-1', 'global-label-1']) - .attach('membersfile', path.join(__dirname, '/../../../../utils/fixtures/csv/valid-members-labels.csv')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(201) - .then((res) => { - should.not.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - - should.exist(jsonResponse); - should.exist(jsonResponse.meta); - should.exist(jsonResponse.meta.stats); - - should.exist(jsonResponse.meta.import_label); - jsonResponse.meta.import_label.slug.should.match(/^import-/); - jsonResponse.meta.stats.imported.should.equal(2); - jsonResponse.meta.stats.invalid.length.should.equal(0); - - importLabel = jsonResponse.meta.import_label.slug; - return request - .get(localUtils.API.getApiQuery(`members/?&filter=label:${importLabel}`)) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200); - }) - .then((res) => { - should.not.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - - should.exist(jsonResponse); - should.exist(jsonResponse.members); - should.equal(jsonResponse.members.length, 2); - - const importedMember1 = jsonResponse.members.find(m => m.email === 'member+labels_1@example.com'); - should.exist(importedMember1); - should(importedMember1.name).equal(null); - should(importedMember1.note).equal(null); - importedMember1.subscribed.should.equal(true); - importedMember1.comped.should.equal(false); - importedMember1.stripe.should.not.be.undefined(); - importedMember1.stripe.subscriptions.length.should.equal(0); - - // check label order - // 1 unique global + 1 record labels + 1 auto generated label - importedMember1.labels.length.should.equal(3); - should.exist(importedMember1.labels.find(({slug}) => slug === 'label')); - should.exist(importedMember1.labels.find(({slug}) => slug === 'global-label-1')); - should.exist(importedMember1.labels.find(({slug}) => slug.match(/^import-/))); - - const importedMember2 = jsonResponse.members.find(m => m.email === 'member+labels_2@example.com'); - should.exist(importedMember2); - // 1 unique global + 2 record labels - importedMember2.labels.length.should.equal(4); - should.exist(importedMember2.labels.find(({slug}) => slug === 'another-label')); - should.exist(importedMember2.labels.find(({slug}) => slug === 'and-one-more')); - should.exist(importedMember2.labels.find(({slug}) => slug === 'global-label-1')); - should.exist(importedMember2.labels.find(({slug}) => slug.match(/^import-/))); - }); - }); - - it('Can import CSV with mapped fields', function () { - return request - .post(localUtils.API.getApiQuery(`members/upload/`)) - .field('mapping[correo_electrpnico]', 'email') - .field('mapping[nombre]', 'name') - .attach('membersfile', path.join(__dirname, '/../../../../utils/fixtures/csv/members-with-mappings.csv')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(201) - .then((res) => { - should.not.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - - should.exist(jsonResponse); - should.exist(jsonResponse.meta); - should.exist(jsonResponse.meta.stats); - - jsonResponse.meta.stats.imported.should.equal(1); - jsonResponse.meta.stats.invalid.length.should.equal(0); - - should.exist(jsonResponse.meta.import_label); - jsonResponse.meta.import_label.slug.should.match(/^import-/); - }) - .then(() => { - return request - .get(localUtils.API.getApiQuery(`members/?search=${encodeURIComponent('member+mapped_1@example.com')}`)) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200); - }) - .then((res) => { - should.not.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - - should.exist(jsonResponse); - should.exist(jsonResponse.members); - should.exist(jsonResponse.members[0]); - - const importedMember1 = jsonResponse.members[0]; - should(importedMember1.email).equal('member+mapped_1@example.com'); - should(importedMember1.name).equal('Hannah'); - should(importedMember1.note).equal('no need to map me'); - importedMember1.subscribed.should.equal(true); - importedMember1.comped.should.equal(false); - importedMember1.stripe.should.not.be.undefined(); - importedMember1.stripe.subscriptions.length.should.equal(0); - importedMember1.labels.length.should.equal(1); // auto-generated import label - }); - }); - - it('Can import CSV with labels and provide additional labels', function () { - return request - .post(localUtils.API.getApiQuery(`members/upload/`)) - .attach('membersfile', path.join(__dirname, '/../../../../utils/fixtures/csv/valid-members-defaults.csv')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(201) - .then((res) => { - should.not.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - - should.exist(jsonResponse); - should.exist(jsonResponse.meta); - should.exist(jsonResponse.meta.stats); - - jsonResponse.meta.stats.imported.should.equal(2); - jsonResponse.meta.stats.invalid.length.should.equal(0); - }) - .then(() => { - return request - .get(localUtils.API.getApiQuery(`members/`)) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200); - }) - .then((res) => { - should.not.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - - should.exist(jsonResponse); - should.exist(jsonResponse.members); - - const defaultMember1 = jsonResponse.members.find(member => (member.email === 'member+defaults_1@example.com')); - should(defaultMember1.name).equal(null); - should(defaultMember1.note).equal(null); - defaultMember1.subscribed.should.equal(true); - defaultMember1.comped.should.equal(false); - defaultMember1.stripe.should.not.be.undefined(); - defaultMember1.stripe.subscriptions.length.should.equal(0); - defaultMember1.labels.length.should.equal(1); // auto-generated import label - - const defaultMember2 = jsonResponse.members.find(member => (member.email === 'member+defaults_2@example.com')); - should(defaultMember2).not.be.undefined(); - }); - }); - - it('Runs imports with stripe_customer_id as background job', function () { - return request - .post(localUtils.API.getApiQuery(`members/upload/`)) - .attach('membersfile', path.join(__dirname, '/../../../../utils/fixtures/csv/members-with-stripe-ids.csv')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(202) - .then((res) => { - should.not.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - - should.exist(jsonResponse); - should.exist(jsonResponse.meta); - should.not.exist(jsonResponse.meta.stats); - }); - }); - - it('Fails to import member with invalid values', function () { - return request - .post(localUtils.API.getApiQuery(`members/upload/`)) - .field('labels', ['new-global-label']) - .attach('membersfile', path.join(__dirname, '/../../../../utils/fixtures/csv/members-invalid-values.csv')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(201) - .then((res) => { - should.not.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - - should.exist(jsonResponse); - should.exist(jsonResponse.meta); - should.exist(jsonResponse.meta.stats); - - jsonResponse.meta.stats.imported.should.equal(1); - jsonResponse.meta.stats.invalid.length.should.equal(1); - - jsonResponse.meta.stats.invalid[0].error.should.match(/Validation \(isEmail\) failed for email/); - - should.exist(jsonResponse.meta.import_label); - jsonResponse.meta.import_label.slug.should.match(/^import-/); - }); - }); - - it('Can fetch stats with no ?days param', function () { - return request - .get(localUtils.API.getApiQuery('members/stats/')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - // .expect(200) - doesn't surface underlying errors in tests - .then((res) => { - res.status.should.equal(200, JSON.stringify(res.body)); - - should.not.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - - should.exist(jsonResponse); - should.exist(jsonResponse.total); - should.exist(jsonResponse.total_in_range); - should.exist(jsonResponse.total_on_date); - should.exist(jsonResponse.new_today); - - // 8 from fixtures and 6 imported in previous tests - jsonResponse.total.should.equal(14); - }); - }); - - it('Can fetch stats with ?days=90', function () { - return request - .get(localUtils.API.getApiQuery('members/stats/?days=90')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - // .expect(200) - doesn't surface underlying errors in tests - .then((res) => { - res.status.should.equal(200, JSON.stringify(res.body)); - - should.not.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - - should.exist(jsonResponse); - should.exist(jsonResponse.total); - should.exist(jsonResponse.total_in_range); - should.exist(jsonResponse.total_on_date); - should.exist(jsonResponse.new_today); - - // 8 from fixtures and 6 imported in previous tests - jsonResponse.total.should.equal(14); - }); - }); - - it('Can fetch stats with ?days=all-time', function () { - return request - .get(localUtils.API.getApiQuery('members/stats/?days=all-time')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - // .expect(200) - doesn't surface underlying errors in tests - .then((res) => { - res.status.should.equal(200, JSON.stringify(res.body)); - - should.not.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - - should.exist(jsonResponse); - should.exist(jsonResponse.total); - should.exist(jsonResponse.total_in_range); - should.exist(jsonResponse.total_on_date); - should.exist(jsonResponse.new_today); - - // 8 from fixtures and 6 imported in previous tests - jsonResponse.total.should.equal(14); - }); - }); - - it('Errors when fetching stats with unknown days param value', function () { - return request - .get(localUtils.API.getApiQuery('members/stats/?days=nope')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(422); - }); -}); diff --git a/test/regression/api/v3/admin/members_signin_url.test.js b/test/regression/api/v3/admin/members_signin_url.test.js deleted file mode 100644 index ccaff64925..0000000000 --- a/test/regression/api/v3/admin/members_signin_url.test.js +++ /dev/null @@ -1,107 +0,0 @@ -const path = require('path'); -const should = require('should'); -const supertest = require('supertest'); -const sinon = require('sinon'); -const testUtils = require('../../../../utils'); -const localUtils = require('./utils'); -const config = require('../../../../../core/shared/config'); -const labs = require('../../../../../core/shared/labs'); - -let request; - -describe('Members Sigin URL API', function () { - before(function () { - sinon.stub(labs, 'isSet').withArgs('members').returns(true); - }); - - after(function () { - sinon.restore(); - }); - - describe('As Owner', function () { - before(async function () { - await localUtils.startGhost(); - request = supertest.agent(config.get('url')); - await localUtils.doAuth(request, 'member'); - }); - - it('Can read', function () { - return request - .get(localUtils.API.getApiQuery(`members/${testUtils.DataGenerator.Content.members[0].id}/signin_urls/`)) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .then((res) => { - should.not.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - should.exist(jsonResponse); - should.exist(jsonResponse.member_signin_urls); - jsonResponse.member_signin_urls.should.have.length(1); - localUtils.API.checkResponse(jsonResponse.member_signin_urls[0], 'member_signin_url'); - }); - }); - }); - - describe('As Admin', function () { - before(async function () { - await localUtils.startGhost(); - request = supertest.agent(config.get('url')); - const admin = await testUtils.createUser({ - user: testUtils.DataGenerator.forKnex.createUser({email: 'admin+1@ghost.org'}), - role: testUtils.DataGenerator.Content.roles[0].name - }); - - request.user = admin; - await localUtils.doAuth(request, 'member'); - }); - - it('Can read', function () { - return request - .get(localUtils.API.getApiQuery(`members/${testUtils.DataGenerator.Content.members[0].id}/signin_urls/`)) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .then((res) => { - should.not.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - should.exist(jsonResponse); - should.exist(jsonResponse.member_signin_urls); - jsonResponse.member_signin_urls.should.have.length(1); - localUtils.API.checkResponse(jsonResponse.member_signin_urls[0], 'member_signin_url'); - }); - }); - }); - - describe('As non-Owner and non-Admin', function () { - before(function () { - return localUtils.startGhost() - .then(function () { - request = supertest.agent(config.get('url')); - }) - .then(function () { - return testUtils.createUser({ - user: testUtils.DataGenerator.forKnex.createUser({ - email: 'test+editor@ghost.org' - }), - role: testUtils.DataGenerator.Content.roles[1].name - }); - }) - .then((user) => { - request.user = user; - - return localUtils.doAuth(request, 'member'); - }); - }); - - it('Cannot read', function () { - return request - .get(localUtils.API.getApiQuery(`members/${testUtils.DataGenerator.Content.members[0].id}/signin_urls/`)) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(403); - }); - }); -}); diff --git a/test/regression/api/v3/admin/notifications.test.js b/test/regression/api/v3/admin/notifications.test.js deleted file mode 100644 index 03375492a1..0000000000 --- a/test/regression/api/v3/admin/notifications.test.js +++ /dev/null @@ -1,179 +0,0 @@ -const should = require('should'); -const supertest = require('supertest'); -const testUtils = require('../../../../utils'); -const config = require('../../../../../core/shared/config'); -const localUtils = require('./utils'); - -describe('Notifications API', function () { - describe('As Editor', function () { - let request; - - before(async function () { - await localUtils.startGhost(); - request = supertest.agent(config.get('url')); - const user = await testUtils.createUser({ - user: testUtils.DataGenerator.forKnex.createUser({ - email: 'test+editor@ghost.org' - }), - role: testUtils.DataGenerator.Content.roles[1].name - }); - - request.user = user; - await localUtils.doAuth(request); - }); - - it('Add notification', function () { - const newNotification = { - type: 'info', - message: 'test notification', - custom: true - }; - - return request.post(localUtils.API.getApiQuery('notifications/')) - .set('Origin', config.get('url')) - .send({notifications: [newNotification]}) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(201) - .then((res) => { - const jsonResponse = res.body; - - should.exist(jsonResponse); - should.exist(jsonResponse.notifications); - should.equal(jsonResponse.notifications.length, 1); - }); - }); - - it('Read notifications', function () { - return request.get(localUtils.API.getApiQuery('notifications/')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .then((res) => { - const jsonResponse = res.body; - - should.exist(jsonResponse); - should.exist(jsonResponse.notifications); - should.equal(jsonResponse.notifications.length, 1); - }); - }); - }); - - describe('As Author', function () { - let request; - - before(async function () { - await localUtils.startGhost(); - - request = supertest.agent(config.get('url')); - const user = await testUtils.createUser({ - user: testUtils.DataGenerator.forKnex.createUser({ - email: 'test+author@ghost.org' - }), - role: testUtils.DataGenerator.Content.roles[2].name - }); - request.user = user; - await localUtils.doAuth(request); - }); - - it('Add notification', function () { - const newNotification = { - type: 'info', - message: 'test notification', - custom: true - }; - - return request.post(localUtils.API.getApiQuery('notifications/')) - .set('Origin', config.get('url')) - .send({notifications: [newNotification]}) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(403); - }); - - it('Read notifications', function () { - return request.get(localUtils.API.getApiQuery('notifications/')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(403); - }); - }); - - describe('Can view by multiple users', function () { - let requestEditor1; - let requestEditor2; - let notification; - - before(async function () { - await localUtils.startGhost(); - - requestEditor1 = supertest.agent(config.get('url')); - requestEditor2 = supertest.agent(config.get('url')); - - const editor1User = await testUtils.createUser({ - user: testUtils.DataGenerator.forKnex.createUser({ - email: 'test+editor1@ghost.org' - }), - role: testUtils.DataGenerator.Content.roles[1].name - }); - requestEditor1.user = editor1User; - await localUtils.doAuth(requestEditor1); - - const editor2User = await testUtils.createUser({ - user: testUtils.DataGenerator.forKnex.createUser({ - email: 'test+editor2@ghost.org' - }), - role: testUtils.DataGenerator.Content.roles[1].name - }); - requestEditor2.user = editor2User; - await localUtils.doAuth(requestEditor2); - - const newNotification = { - type: 'info', - message: 'multiple views', - custom: true - }; - - await requestEditor1.post(localUtils.API.getApiQuery('notifications/')) - .set('Origin', config.get('url')) - .send({notifications: [newNotification]}) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(201) - .then((res) => { - notification = res.body.notifications[0]; - }); - }); - - it('notification is visible and dismissible by other user', function () { - return requestEditor1.del(localUtils.API.getApiQuery(`notifications/${notification.id}`)) - .set('Origin', config.get('url')) - .expect(204) - .then(() => { - return requestEditor2.get(localUtils.API.getApiQuery(`notifications/`)) - .set('Origin', config.get('url')) - .expect(200) - .then(function (res) { - const deleted = res.body.notifications.filter(n => n.id === notification.id); - deleted.should.not.be.empty(); - }); - }) - .then(() => { - return requestEditor2.del(localUtils.API.getApiQuery(`notifications/${notification.id}`)) - .set('Origin', config.get('url')) - .expect(204); - }) - .then(() => { - return requestEditor2.get(localUtils.API.getApiQuery(`notifications/`)) - .set('Origin', config.get('url')) - .expect(200) - .then(function (res) { - const deleted = res.body.notifications.filter(n => n.id === notification.id); - deleted.should.be.empty(); - }); - }); - }); - }); -}); diff --git a/test/regression/api/v3/admin/schedules.test.js b/test/regression/api/v3/admin/schedules.test.js deleted file mode 100644 index 85f48a82db..0000000000 --- a/test/regression/api/v3/admin/schedules.test.js +++ /dev/null @@ -1,170 +0,0 @@ -const _ = require('lodash'); -const should = require('should'); -const supertest = require('supertest'); -const Promise = require('bluebird'); -const sinon = require('sinon'); -const moment = require('moment-timezone'); -const SchedulingDefault = require('../../../../../core/server/adapters/scheduling/SchedulingDefault'); -const models = require('../../../../../core/server/models/index'); -const config = require('../../../../../core/shared/config/index'); -const testUtils = require('../../../../utils/index'); -const localUtils = require('./utils'); - -describe('v3 Schedules API', function () { - const resources = []; - let request; - - before(function () { - models.init(); - - // @NOTE: mock the post scheduler, otherwise it will auto publish the post - sinon.stub(SchedulingDefault.prototype, '_pingUrl').resolves(); - }); - - after(function () { - sinon.restore(); - }); - - before(async function () { - await localUtils.startGhost(); - - request = supertest.agent(config.get('url')); - - resources.push(testUtils.DataGenerator.forKnex.createPost({ - created_by: testUtils.getExistingData().users[0].id, - author_id: testUtils.getExistingData().users[0].id, - published_by: testUtils.getExistingData().users[0].id, - published_at: moment().add(30, 'seconds').toDate(), - status: 'scheduled', - slug: 'first' - })); - - resources.push(testUtils.DataGenerator.forKnex.createPost({ - created_by: testUtils.getExistingData().users[0].id, - author_id: testUtils.getExistingData().users[0].id, - published_by: testUtils.getExistingData().users[0].id, - published_at: moment().subtract(30, 'seconds').toDate(), - status: 'scheduled', - slug: 'second' - })); - - resources.push(testUtils.DataGenerator.forKnex.createPost({ - created_by: testUtils.getExistingData().users[0].id, - author_id: testUtils.getExistingData().users[0].id, - published_by: testUtils.getExistingData().users[0].id, - published_at: moment().add(10, 'minute').toDate(), - status: 'scheduled', - slug: 'third' - })); - - resources.push(testUtils.DataGenerator.forKnex.createPost({ - created_by: testUtils.getExistingData().users[0].id, - author_id: testUtils.getExistingData().users[0].id, - published_by: testUtils.getExistingData().users[0].id, - published_at: moment().subtract(10, 'minute').toDate(), - status: 'scheduled', - slug: 'fourth' - })); - - resources.push(testUtils.DataGenerator.forKnex.createPost({ - created_by: testUtils.getExistingData().users[0].id, - author_id: testUtils.getExistingData().users[0].id, - published_by: testUtils.getExistingData().users[0].id, - published_at: moment().add(30, 'seconds').toDate(), - status: 'scheduled', - slug: 'fifth', - type: 'page' - })); - - const result = await Promise.mapSeries(resources, function (post) { - return models.Post.add(post, {context: {internal: true}}); - }); - - result.length.should.eql(5); - }); - - describe('publish', function () { - let token; - - before(function () { - const schedulerKey = _.find(testUtils.getExistingData().apiKeys, {integration: {slug: 'ghost-scheduler'}}); - - token = localUtils.getValidAdminToken('/v3/admin/', schedulerKey); - }); - - it('publishes posts', async function () { - const res = await request - .put(localUtils.API.getApiQuery(`schedules/posts/${resources[0].id}/?token=${token}`)) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200); - - should.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - should.exist(jsonResponse); - jsonResponse.posts[0].id.should.eql(resources[0].id); - jsonResponse.posts[0].status.should.eql('published'); - }); - - it('publishes page', async function () { - const res = await request - .put(localUtils.API.getApiQuery(`schedules/pages/${resources[4].id}/?token=${token}`)) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200); - - should.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - should.exist(jsonResponse); - jsonResponse.pages[0].id.should.eql(resources[4].id); - jsonResponse.pages[0].status.should.eql('published'); - }); - - it('no access', function () { - const zapierKey = _.find(testUtils.getExistingData().apiKeys, {integration: {slug: 'ghost-backup'}}); - const zapierToken = localUtils.getValidAdminToken('/v3/admin/', zapierKey); - - return request - .put(localUtils.API.getApiQuery(`schedules/posts/${resources[0].id}/?token=${zapierToken}`)) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(403); - }); - - it('should fail with invalid resource type', function () { - return request - .put(localUtils.API.getApiQuery(`schedules/this_is_invalid/${resources[0].id}/?token=${token}`)) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(422); - }); - - it('published_at is x seconds in past, but still in tolerance', function () { - return request - .put(localUtils.API.getApiQuery(`schedules/posts/${resources[1].id}/?token=${token}`)) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200); - }); - - it('not found', function () { - return request - .put(localUtils.API.getApiQuery(`schedules/posts/${resources[2].id}/?token=${token}`)) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(404); - }); - - it('force publish', function () { - return request - .put(localUtils.API.getApiQuery(`schedules/posts/${resources[3].id}/?token=${token}`)) - .send({ - force: true - }) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200); - }); - }); -}); diff --git a/test/regression/api/v3/admin/settings.test.js b/test/regression/api/v3/admin/settings.test.js deleted file mode 100644 index dff257989d..0000000000 --- a/test/regression/api/v3/admin/settings.test.js +++ /dev/null @@ -1,836 +0,0 @@ -const _ = require('lodash'); -const should = require('should'); -const supertest = require('supertest'); -const config = require('../../../../../core/shared/config'); -const testUtils = require('../../../../utils'); -const localUtils = require('./utils'); - -// NOTE: in future iterations these fields should be fetched from a central module. -// Have put a list as is here for the lack of better place for it. -const defaultSettingsKeyTypes = [ - {key: 'title', type: 'blog'}, - {key: 'description', type: 'blog'}, - {key: 'logo', type: 'blog'}, - {key: 'cover_image', type: 'blog'}, - {key: 'icon', type: 'blog'}, - {key: 'lang', type: 'blog'}, - {key: 'timezone', type: 'blog'}, - {key: 'codeinjection_head', type: 'blog'}, - {key: 'codeinjection_foot', type: 'blog'}, - {key: 'facebook', type: 'blog'}, - {key: 'twitter', type: 'blog'}, - {key: 'navigation', type: 'blog'}, - {key: 'secondary_navigation', type: 'blog'}, - {key: 'meta_title', type: 'blog'}, - {key: 'meta_description', type: 'blog'}, - {key: 'og_image', type: 'blog'}, - {key: 'og_title', type: 'blog'}, - {key: 'og_description', type: 'blog'}, - {key: 'twitter_image', type: 'blog'}, - {key: 'twitter_title', type: 'blog'}, - {key: 'twitter_description', type: 'blog'}, - {key: 'active_theme', type: 'theme'}, - {key: 'is_private', type: 'private'}, - {key: 'password', type: 'private'}, - {key: 'public_hash', type: 'private'}, - {key: 'default_content_visibility', type: 'members'}, - {key: 'members_signup_access', type: 'members'}, - {key: 'members_from_address', type: 'members'}, - {key: 'members_support_address', type: 'members'}, - {key: 'members_reply_address', type: 'members'}, - {key: 'members_paid_signup_redirect', type: 'members'}, - {key: 'members_free_signup_redirect', type: 'members'}, - {key: 'members_free_price_name', type: 'members'}, - {key: 'members_free_price_description', type: 'members'}, - {key: 'members_monthly_price_id', type: 'members'}, - {key: 'members_yearly_price_id', type: 'members'}, - {key: 'stripe_product_name', type: 'members'}, - {key: 'stripe_plans', type: 'members'}, - {key: 'stripe_secret_key', type: 'members'}, - {key: 'stripe_publishable_key', type: 'members'}, - {key: 'stripe_connect_secret_key', type: 'members'}, - {key: 'stripe_connect_publishable_key', type: 'members'}, - {key: 'stripe_connect_account_id', type: 'members'}, - {key: 'stripe_connect_display_name', type: 'members'}, - {key: 'stripe_connect_livemode', type: 'members'}, - {key: 'portal_name', type: 'portal'}, - {key: 'portal_button', type: 'portal'}, - {key: 'portal_plans', type: 'portal'}, - {key: 'portal_products', type: 'portal'}, - {key: 'portal_button_style', type: 'portal'}, - {key: 'portal_button_icon', type: 'portal'}, - {key: 'portal_button_signup_text', type: 'portal'}, - {key: 'mailgun_api_key', type: 'bulk_email'}, - {key: 'mailgun_domain', type: 'bulk_email'}, - {key: 'mailgun_base_url', type: 'bulk_email'}, - {key: 'email_track_opens', type: 'bulk_email'}, - {key: 'amp', type: 'blog'}, - {key: 'amp_gtag_id', type: 'blog'}, - {key: 'slack', type: 'blog'}, - {key: 'slack_url', type: 'blog'}, - {key: 'slack_username', type: 'blog'}, - {key: 'unsplash', type: 'blog'}, - {key: 'shared_views', type: 'blog'}, - {key: 'active_timezone', type: 'blog'}, - {key: 'default_locale', type: 'blog'}, - {key: 'accent_color', type: 'blog'}, - {key: 'newsletter_show_badge', type: 'newsletter'}, - {key: 'newsletter_header_image', type: 'newsletter'}, - {key: 'newsletter_show_header_icon', type: 'newsletter'}, - {key: 'newsletter_show_header_title', type: 'newsletter'}, - {key: 'newsletter_title_alignment', type: 'newsletter'}, - {key: 'newsletter_title_font_category', type: 'newsletter'}, - {key: 'newsletter_show_feature_image', type: 'newsletter'}, - {key: 'newsletter_body_font_category', type: 'newsletter'}, - {key: 'newsletter_footer_content', type: 'newsletter'}, - {key: 'firstpromoter', type: 'firstpromoter'}, - {key: 'firstpromoter_id', type: 'firstpromoter'}, - {key: 'oauth_client_id', type: 'oauth'}, - {key: 'oauth_client_secret', type: 'oauth'}, - {key: 'editor_default_email_recipients', type: 'editor'}, - {key: 'editor_default_email_recipients_filter', type: 'editor'}, - {key: 'editor_is_launch_complete', type: 'editor'}, - {key: 'labs', type: 'blog'}, - {key: 'email_verification_required', type: 'bulk_email'} -]; - -describe('Settings API (v3)', function () { - let request; - - describe('As Owner', function () { - before(async function () { - await localUtils.startGhost(); - request = supertest.agent(config.get('url')); - await localUtils.doAuth(request); - }); - - it('Can request all settings', function () { - return request.get(localUtils.API.getApiQuery(`settings/`)) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .then((res) => { - should.not.exist(res.headers['x-cache-invalidate']); - - const jsonResponse = res.body; - should.exist(jsonResponse.settings); - should.exist(jsonResponse.meta); - - jsonResponse.settings.should.be.an.Object(); - const settings = jsonResponse.settings; - - should.equal(settings.length, defaultSettingsKeyTypes.length); - for (const defaultSetting of defaultSettingsKeyTypes) { - should.exist(settings.find((setting) => { - return setting.key === defaultSetting.key && setting.type === defaultSetting.type; - }), `Expected to find a setting with key ${defaultSetting.key} and type ${defaultSetting.type}`); - } - - const unsplash = settings.find(s => s.key === 'unsplash'); - should.exist(unsplash); - unsplash.value.should.equal(JSON.stringify({isActive: true})); - - localUtils.API.checkResponse(jsonResponse, 'settings'); - }); - }); - - it('Can request settings by type', function () { - return request.get(localUtils.API.getApiQuery(`settings/?type=theme`)) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .then((res) => { - should.not.exist(res.headers['x-cache-invalidate']); - - const jsonResponse = res.body; - should.exist(jsonResponse.settings); - should.exist(jsonResponse.meta); - - jsonResponse.settings.should.be.an.Object(); - const settings = jsonResponse.settings; - - Object.keys(settings).length.should.equal(1); - settings[0].key.should.equal('active_theme'); - settings[0].value.should.equal('casper'); - settings[0].type.should.equal('theme'); - - localUtils.API.checkResponse(jsonResponse, 'settings'); - }); - }); - - it('Can request settings by group', function () { - return request.get(localUtils.API.getApiQuery(`settings/?group=theme`)) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .then((res) => { - should.not.exist(res.headers['x-cache-invalidate']); - - const jsonResponse = res.body; - should.exist(jsonResponse.settings); - should.exist(jsonResponse.meta); - - jsonResponse.settings.should.be.an.Object(); - const settings = jsonResponse.settings; - - Object.keys(settings).length.should.equal(1); - settings[0].key.should.equal('active_theme'); - settings[0].value.should.equal('casper'); - settings[0].type.should.equal('theme'); - - localUtils.API.checkResponse(jsonResponse, 'settings'); - }); - }); - - it('Can request settings by group and by deprecated type', function () { - return request.get(localUtils.API.getApiQuery(`settings/?group=theme&type=private`)) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .then((res) => { - should.not.exist(res.headers['x-cache-invalidate']); - - const jsonResponse = res.body; - should.exist(jsonResponse.settings); - should.exist(jsonResponse.meta); - - jsonResponse.settings.should.be.an.Object(); - const settings = jsonResponse.settings; - - Object.keys(settings).length.should.equal(4); - settings[0].key.should.equal('active_theme'); - settings[0].value.should.equal('casper'); - settings[0].type.should.equal('theme'); - testUtils.API.checkResponseValue(jsonResponse.settings[0], ['id', 'key', 'value', 'group', 'type', 'flags', 'created_at', 'updated_at']); - localUtils.API.checkResponse(jsonResponse, 'settings'); - }); - }); - - it('Requesting core settings type returns no results', function () { - return request.get(localUtils.API.getApiQuery(`settings/?type=core`)) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .then((res) => { - should.not.exist(res.headers['x-cache-invalidate']); - - const jsonResponse = res.body; - should.exist(jsonResponse.settings); - should.exist(jsonResponse.meta); - - jsonResponse.settings.should.be.an.Object(); - const settings = jsonResponse.settings; - - Object.keys(settings).length.should.equal(0); - - localUtils.API.checkResponse(jsonResponse, 'settings'); - }); - }); - - it('Can\'t read core setting', function () { - return request - .get(localUtils.API.getApiQuery('settings/db_hash/')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(403); - }); - - it('Can\'t read permalinks', function (done) { - request.get(localUtils.API.getApiQuery('settings/permalinks/')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(404) - .end(function (err, res) { - if (err) { - return done(err); - } - - done(); - }); - }); - - it('Can read slack_url introduced in v4', function (done) { - request.get(localUtils.API.getApiQuery('settings/slack_url/')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .end(function (err, res) { - if (err) { - return done(err); - } - - if (err) { - return done(err); - } - - should.not.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - - should.exist(jsonResponse); - should.exist(jsonResponse.settings); - - jsonResponse.settings.length.should.eql(1); - jsonResponse.settings[0].key.should.eql('slack_url'); - done(); - }); - }); - - it('Can read labs', async function () { - const res = await request.get(localUtils.API.getApiQuery('settings/labs/')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200); - - should.not.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - - should.exist(jsonResponse); - should.exist(jsonResponse.settings); - - jsonResponse.settings.length.should.eql(1); - testUtils.API.checkResponseValue(jsonResponse.settings[0], ['id', 'group', 'key', 'value', 'type', 'flags', 'created_at', 'updated_at']); - - const jsonObjectRegex = /^\{.*\}$/; // '{...}' - jsonResponse.settings[0].key.should.eql('labs'); - jsonResponse.settings[0].value.should.match(jsonObjectRegex); - }); - - it('Can read deprecated default_locale', function (done) { - request.get(localUtils.API.getApiQuery('settings/default_locale/')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .end(function (err, res) { - if (err) { - return done(err); - } - - should.not.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - - should.exist(jsonResponse); - should.exist(jsonResponse.settings); - - jsonResponse.settings.length.should.eql(1); - - testUtils.API.checkResponseValue(jsonResponse.settings[0], ['id', 'group', 'key', 'value', 'type', 'flags', 'created_at', 'updated_at']); - jsonResponse.settings[0].key.should.eql('default_locale'); - done(); - }); - }); - - it('Can edit deprecated default_locale setting', function () { - return request.get(localUtils.API.getApiQuery('settings/default_locale/')) - .set('Origin', config.get('url')) - .set('Accept', 'application/json') - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .then(function (res) { - let jsonResponse = res.body; - const newValue = 'new value'; - should.exist(jsonResponse); - should.exist(jsonResponse.settings); - jsonResponse.settings = [{key: 'default_locale', value: 'ua'}]; - - return jsonResponse; - }) - .then((editedSetting) => { - return request.put(localUtils.API.getApiQuery('settings/')) - .set('Origin', config.get('url')) - .send(editedSetting) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .then(function (res) { - should.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - - should.exist(jsonResponse); - should.exist(jsonResponse.settings); - - jsonResponse.settings.length.should.eql(1); - - testUtils.API.checkResponseValue(jsonResponse.settings[0], ['id', 'group', 'key', 'value', 'type', 'flags', 'created_at', 'updated_at']); - jsonResponse.settings[0].key.should.eql('default_locale'); - jsonResponse.settings[0].value.should.eql('ua'); - }); - }); - }); - - it('Can read timezone', function (done) { - request.get(localUtils.API.getApiQuery('settings/timezone/')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .end(function (err, res) { - if (err) { - return done(err); - } - - should.not.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - - should.exist(jsonResponse); - should.exist(jsonResponse.settings); - - jsonResponse.settings.length.should.eql(1); - - testUtils.API.checkResponseValue(jsonResponse.settings[0], ['id', 'group', 'key', 'value', 'type', 'flags', 'created_at', 'updated_at']); - jsonResponse.settings[0].key.should.eql('timezone'); - done(); - }); - }); - - it('Can read active_timezone', function (done) { - request.get(localUtils.API.getApiQuery('settings/active_timezone/')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .end(function (err, res) { - if (err) { - return done(err); - } - - should.not.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - - should.exist(jsonResponse); - should.exist(jsonResponse.settings); - - jsonResponse.settings.length.should.eql(1); - - testUtils.API.checkResponseValue(jsonResponse.settings[0], ['id', 'group', 'key', 'value', 'type', 'flags', 'created_at', 'updated_at']); - jsonResponse.settings[0].key.should.eql('active_timezone'); - done(); - }); - }); - - it('Can read deprecated active_timezone', function (done) { - request.get(localUtils.API.getApiQuery('settings/active_timezone/')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .end(function (err, res) { - if (err) { - return done(err); - } - - should.not.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - - should.exist(jsonResponse); - should.exist(jsonResponse.settings); - - jsonResponse.settings.length.should.eql(1); - - testUtils.API.checkResponseValue(jsonResponse.settings[0], ['id', 'group', 'key', 'value', 'type', 'flags', 'created_at', 'updated_at']); - jsonResponse.settings[0].key.should.eql('active_timezone'); - done(); - }); - }); - - it('Can read slack renamed&reformatted in v4', function (done) { - request.get(localUtils.API.getApiQuery('settings/slack/')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .end(function (err, res) { - if (err) { - return done(err); - } - - should.not.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - - should.exist(jsonResponse); - should.exist(jsonResponse.settings); - - jsonResponse.settings.length.should.eql(1); - - testUtils.API.checkResponseValue(jsonResponse.settings[0], ['id', 'group', 'key', 'value', 'type', 'flags', 'created_at', 'updated_at']); - jsonResponse.settings[0].key.should.eql('slack'); - done(); - }); - }); - - it('Cannot edit labs keys', async function () { - const settingToChange = { - settings: [{ - key: 'labs', - value: JSON.stringify({ - activitypub: true, - gibberish: true - }) - }] - }; - - const res = await request.put(localUtils.API.getApiQuery('settings/')) - .set('Origin', config.get('url')) - .send(settingToChange) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200); - - const jsonResponse = res.body; - - should.exist(jsonResponse); - should.exist(jsonResponse.settings); - - jsonResponse.settings.length.should.eql(0); - }); - - it('Can\'t read non existent setting', function (done) { - request.get(localUtils.API.getApiQuery('settings/testsetting/')) - .set('Origin', config.get('url')) - .set('Accept', 'application/json') - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(404) - .end(function (err, res) { - if (err) { - return done(err); - } - - should.not.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - should.exist(jsonResponse); - should.exist(jsonResponse.errors); - testUtils.API.checkResponseValue(jsonResponse.errors[0], [ - 'message', - 'context', - 'type', - 'details', - 'property', - 'help', - 'code', - 'id' - ]); - done(); - }); - }); - - it('Can\'t edit permalinks', function (done) { - const settingToChange = { - settings: [{key: 'permalinks', value: '/:primary_author/:slug/'}] - }; - - request.put(localUtils.API.getApiQuery('settings/')) - .set('Origin', config.get('url')) - .send(settingToChange) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(404) - .end(function (err, res) { - if (err) { - return done(err); - } - - done(); - }); - }); - - it('Can\'t edit non existent setting', function () { - return request.get(localUtils.API.getApiQuery('settings/')) - .set('Origin', config.get('url')) - .set('Accept', 'application/json') - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .then(function (res) { - let jsonResponse = res.body; - const newValue = 'new value'; - should.exist(jsonResponse); - should.exist(jsonResponse.settings); - jsonResponse.settings = [{key: 'testvalue', value: newValue}]; - - return request.put(localUtils.API.getApiQuery('settings/')) - .set('Origin', config.get('url')) - .send(jsonResponse) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(404) - .then(function ({body, headers}) { - jsonResponse = body; - should.not.exist(headers['x-cache-invalidate']); - should.exist(jsonResponse.errors); - testUtils.API.checkResponseValue(jsonResponse.errors[0], [ - 'message', - 'context', - 'type', - 'details', - 'property', - 'help', - 'code', - 'id' - ]); - }); - }); - }); - - it('Will transform "1"', function () { - return request.get(localUtils.API.getApiQuery('settings/')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .then(function (res) { - const jsonResponse = res.body; - - const settingToChange = { - settings: [ - { - key: 'is_private', - value: '1' - } - ] - }; - - should.exist(jsonResponse); - should.exist(jsonResponse.settings); - - return request.put(localUtils.API.getApiQuery('settings/')) - .set('Origin', config.get('url')) - .send(settingToChange) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .then(function ({body, headers}) { - const putBody = body; - headers['x-cache-invalidate'].should.eql('/*'); - should.exist(putBody); - - putBody.settings[0].key.should.eql('is_private'); - putBody.settings[0].value.should.eql(true); - - localUtils.API.checkResponse(putBody, 'settings'); - }); - }); - }); - - it('Can edit multiple setting along with a deprecated one from v4', async function () { - const settingToChange = { - settings: [ - { - key: 'slack', - value: JSON.stringify([{ - url: 'https://newurl.tld/slack', - username: 'New Slack Username' - }]) - }, { - key: 'unsplash', - value: JSON.stringify({ - isActive: true - }) - }, { - key: 'title', - value: 'New Value' - } - ] - }; - - const {body, headers} = await request.put(localUtils.API.getApiQuery('settings/')) - .set('Origin', config.get('url')) - .send(settingToChange) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200); - - const putBody = body; - headers['x-cache-invalidate'].should.eql('/*'); - should.exist(putBody); - - putBody.settings.length.should.equal(3); - - putBody.settings[0].key.should.eql('unsplash'); - should.equal(putBody.settings[0].value, JSON.stringify({ - isActive: true - })); - - putBody.settings[1].key.should.eql('title'); - should.equal(putBody.settings[1].value, 'New Value'); - - putBody.settings[2].key.should.eql('slack'); - should.equal(putBody.settings[2].value, JSON.stringify([{ - url: 'https://newurl.tld/slack', - username: 'New Slack Username' - }])); - - localUtils.API.checkResponse(putBody, 'settings'); - }); - - it('Can edit a setting introduced in v4', async function () { - const settingToChange = { - settings: [ - { - key: 'slack_username', - value: 'can edit me' - } - ] - }; - - const {body, headers} = await request.put(localUtils.API.getApiQuery('settings/')) - .set('Origin', config.get('url')) - .send(settingToChange) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200); - - const putBody = body; - headers['x-cache-invalidate'].should.eql('/*'); - should.exist(putBody); - - putBody.settings.length.should.equal(1); - - localUtils.API.checkResponse(putBody, 'settings'); - putBody.settings[0].key.should.eql('slack_username'); - putBody.settings[0].value.should.eql('can edit me'); - }); - - it('Can only send array values for keys defined with array type', async function () { - const settingsToChange = { - settings: [ - {key: 'navigation', value: 'not an array'} - ] - }; - - await request.put(localUtils.API.getApiQuery('settings/')) - .set('Origin', config.get('url')) - .send(settingsToChange) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(422); - }); - }); - - describe('As Admin', function () { - before(async function () { - await localUtils.startGhost(); - request = supertest.agent(config.get('url')); - - // create admin - const admin = await testUtils.createUser({ - user: testUtils.DataGenerator.forKnex.createUser({email: 'admin+1@ghost.org'}), - role: testUtils.DataGenerator.Content.roles[0].name - }); - request.user = admin; - // by default we login with the owner - await localUtils.doAuth(request); - }); - }); - - describe('As Editor', function () { - before(async function () { - await localUtils.startGhost(); - request = supertest.agent(config.get('url')); - // create editor - request.user = await testUtils.createUser({ - user: testUtils.DataGenerator.forKnex.createUser({email: 'test+1@ghost.org'}), - role: testUtils.DataGenerator.Content.roles[1].name - }); - - // by default we login with the owner - await localUtils.doAuth(request); - }); - - it('should not be able to edit settings', function () { - return request.get(localUtils.API.getApiQuery('settings/')) - .set('Origin', config.get('url')) - .set('Accept', 'application/json') - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .then(function (res) { - let jsonResponse = res.body; - - should.exist(jsonResponse); - should.exist(jsonResponse.settings); - jsonResponse.settings = [{key: 'visibility', value: 'public'}]; - - return request.put(localUtils.API.getApiQuery('settings/')) - .set('Origin', config.get('url')) - .send(jsonResponse) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(403) - .then(function ({body, headers}) { - jsonResponse = body; - should.not.exist(headers['x-cache-invalidate']); - should.exist(jsonResponse.errors); - testUtils.API.checkResponseValue(jsonResponse.errors[0], [ - 'message', - 'context', - 'type', - 'details', - 'property', - 'help', - 'code', - 'id' - ]); - }); - }); - }); - }); - - describe('As Author', function () { - before(async function () { - await localUtils.startGhost(); - request = supertest.agent(config.get('url')); - - // create author - request.user = await testUtils.createUser({ - user: testUtils.DataGenerator.forKnex.createUser({email: 'test+2@ghost.org'}), - role: testUtils.DataGenerator.Content.roles[2].name - }); - - // by default we login with the owner - await localUtils.doAuth(request); - }); - - it('should not be able to edit settings', function () { - return request.get(localUtils.API.getApiQuery('settings/')) - .set('Origin', config.get('url')) - .set('Accept', 'application/json') - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .then(function (res) { - let jsonResponse = res.body; - should.exist(jsonResponse); - should.exist(jsonResponse.settings); - jsonResponse.settings = [{key: 'visibility', value: 'public'}]; - - return request.put(localUtils.API.getApiQuery('settings/')) - .set('Origin', config.get('url')) - .send(jsonResponse) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(403) - .then(function ({body, headers}) { - jsonResponse = body; - should.not.exist(headers['x-cache-invalidate']); - should.exist(jsonResponse.errors); - testUtils.API.checkResponseValue(jsonResponse.errors[0], [ - 'message', - 'context', - 'type', - 'details', - 'property', - 'help', - 'code', - 'id' - ]); - }); - }); - }); - }); -}); diff --git a/test/regression/api/v3/admin/slack.test.js b/test/regression/api/v3/admin/slack.test.js deleted file mode 100644 index 7701891ed4..0000000000 --- a/test/regression/api/v3/admin/slack.test.js +++ /dev/null @@ -1,40 +0,0 @@ -const should = require('should'); -const supertest = require('supertest'); -const sinon = require('sinon'); -const testUtils = require('../../../../utils'); -const localUtils = require('./utils'); -const config = require('../../../../../core/shared/config'); -const events = require('../../../../../core/server/lib/common/events'); - -let request; - -describe('Slack API', function () { - before(async function () { - await localUtils.startGhost(); - request = supertest.agent(config.get('url')); - await localUtils.doAuth(request); - }); - after(function () { - sinon.restore(); - }); - - it('Can post slack test', function (done) { - const eventSpy = sinon.spy(events, 'emit'); - request.post(localUtils.API.getApiQuery('slack/test/')) - .set('Origin', config.get('url')) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .end(function (err, res) { - if (err) { - return done(err); - } - - should.not.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - should.exist(jsonResponse); - eventSpy.calledWith('slack.test').should.be.true(); - done(); - }); - }); -}); diff --git a/test/regression/api/v3/admin/users.test.js b/test/regression/api/v3/admin/users.test.js deleted file mode 100644 index 51519634ac..0000000000 --- a/test/regression/api/v3/admin/users.test.js +++ /dev/null @@ -1,262 +0,0 @@ -const should = require('should'); -const supertest = require('supertest'); -const ObjectId = require('bson-objectid'); -const testUtils = require('../../../../utils'); -const config = require('../../../../../core/shared/config'); -const localUtils = require('./utils'); -let request; - -describe('User API', function () { - describe('As Owner', function () { - let otherAuthor; - - before(async function () { - await localUtils.startGhost(); - request = supertest.agent(config.get('url')); - - // create inactive user - otherAuthor = await testUtils.createUser({ - user: testUtils.DataGenerator.forKnex.createUser({email: 'test+3@ghost.org'}), - role: testUtils.DataGenerator.Content.roles[2].name - }); - - // by default we login with the owner - await localUtils.doAuth(request); - }); - - describe('Read', function () { - it('can\'t retrieve non existent user by id', function (done) { - request.get(localUtils.API.getApiQuery('users/' + ObjectId().toHexString() + '/')) - .set('Origin', config.get('url')) - .set('Accept', 'application/json') - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(404) - .end(function (err, res) { - if (err) { - return done(err); - } - - should.not.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - should.exist(jsonResponse); - should.exist(jsonResponse.errors); - testUtils.API.checkResponseValue(jsonResponse.errors[0], [ - 'message', - 'context', - 'type', - 'details', - 'property', - 'help', - 'code', - 'id' - ]); - done(); - }); - }); - - it('can\'t retrieve non existent user by slug', function (done) { - request.get(localUtils.API.getApiQuery('users/slug/blargh/')) - .set('Origin', config.get('url')) - .set('Accept', 'application/json') - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(404) - .end(function (err, res) { - if (err) { - return done(err); - } - - should.not.exist(res.headers['x-cache-invalidate']); - const jsonResponse = res.body; - should.exist(jsonResponse); - should.exist(jsonResponse.errors); - testUtils.API.checkResponseValue(jsonResponse.errors[0], [ - 'message', - 'context', - 'type', - 'details', - 'property', - 'help', - 'code', - 'id' - ]); - done(); - }); - }); - }); - - describe('Edit', function () { - it('can change the other users password', function (done) { - request.put(localUtils.API.getApiQuery('users/password/')) - .set('Origin', config.get('url')) - .send({ - password: [{ - newPassword: 'superSecure', - ne2Password: 'superSecure', - user_id: otherAuthor.id - }] - }) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .end(function (err) { - if (err) { - return done(err); - } - - done(); - }); - }); - }); - - describe('Destroy', function () { - it('[failure] Destroy unknown user id', function (done) { - request.delete(localUtils.API.getApiQuery('users/' + ObjectId().toHexString())) - .set('Origin', config.get('url')) - .expect(404) - .end(function (err) { - if (err) { - return done(err); - } - - done(); - }); - }); - }); - }); - - describe('As Editor', function () { - let editor; - - before(async function () { - await localUtils.startGhost(); - request = supertest.agent(config.get('url')); - // create editor - editor = await testUtils.createUser({ - user: testUtils.DataGenerator.forKnex.createUser({email: 'test+1@ghost.org'}), - role: testUtils.DataGenerator.Content.roles[1].name - }); - - request.user = editor; - // by default we login with the owner - await localUtils.doAuth(request); - }); - - describe('success cases', function () { - it('can edit himself', function (done) { - request.put(localUtils.API.getApiQuery('users/' + editor.id + '/')) - .set('Origin', config.get('url')) - .send({ - users: [{id: editor.id, name: 'test'}] - }) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .end(function (err) { - if (err) { - return done(err); - } - - done(); - }); - }); - }); - - describe('error cases', function () { - it('can\'t edit the owner', function (done) { - request.put(localUtils.API.getApiQuery('users/' + testUtils.DataGenerator.Content.users[0].id + '/')) - .set('Origin', config.get('url')) - .send({ - users: [{ - id: testUtils.DataGenerator.Content.users[0].id - }] - }) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(403) - .end(function (err) { - if (err) { - return done(err); - } - - done(); - }); - }); - - it('Cannot transfer ownership to any other user', function () { - return request - .put(localUtils.API.getApiQuery('users/owner')) - .set('Origin', config.get('url')) - .send({ - owner: [{ - id: testUtils.getExistingData().users[1].id - }] - }) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(403); - }); - }); - }); - - describe('As Author', function () { - let author; - - before(async function () { - await localUtils.startGhost(); - request = supertest.agent(config.get('url')); - // create author - author = await testUtils.createUser({ - user: testUtils.DataGenerator.forKnex.createUser({email: 'test+2@ghost.org'}), - role: testUtils.DataGenerator.Content.roles[2].name - }); - - request.user = author; - // by default we login with the owner - await localUtils.doAuth(request); - }); - - describe('success cases', function () { - it('can edit himself', function (done) { - request.put(localUtils.API.getApiQuery('users/' + author.id + '/')) - .set('Origin', config.get('url')) - .send({ - users: [{id: author.id, name: 'test'}] - }) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .end(function (err) { - if (err) { - return done(err); - } - - done(); - }); - }); - }); - - describe('error cases', function () { - it('can\'t edit the owner', function (done) { - request.put(localUtils.API.getApiQuery('users/' + testUtils.DataGenerator.Content.users[0].id + '/')) - .set('Origin', config.get('url')) - .send({ - users: [{ - id: testUtils.DataGenerator.Content.users[0].id - }] - }) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(403) - .end(function (err) { - if (err) { - return done(err); - } - - done(); - }); - }); - }); - }); -});