Ghost/test/regression/api/canary/admin/settings_spec.js
Kevin Ansfield f49f7699aa
Enabled all labs flags when testing (#13036)
no issue

Shows impact of new code behind labs flags through the existing acceptance/regression tests. Allows for existing tests to be updated to match new behaviour rather than requiring separate tests where individual flags are enabled. Should result in minimal test updating once code reaches GA.

- adds a forced `'labs:enabled'` fixture op that edits the `labs` setting to enable all flags then restarts the settings service to pick up the new setting
- modifies labs service to not remove ALPHA_FEATURE labs settings when running in a testing environment
2021-06-10 12:54:34 +01:00

1376 lines
49 KiB
JavaScript

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');
const db = require('../../../../../core/server/data/db');
const ghost = testUtils.startGhost;
// 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: 'string',
group: 'site'
},
{
key: 'description',
type: 'string',
group: 'site'
},
{
key: 'logo',
type: 'string',
group: 'site'
},
{
key: 'cover_image',
type: 'string',
group: 'site'
},
{
key: 'icon',
type: 'string',
group: 'site'
},
{
key: 'lang',
type: 'string',
group: 'site'
},
{
key: 'locale',
type: 'string',
group: 'site'
},
{
key: 'timezone',
type: 'string',
group: 'site'
},
{
key: 'codeinjection_head',
type: 'string',
group: 'site'
},
{
key: 'codeinjection_foot',
type: 'string',
group: 'site'
},
{
key: 'facebook',
type: 'string',
group: 'site'
},
{
key: 'twitter',
type: 'string',
group: 'site'
},
{
key: 'navigation',
type: 'array',
group: 'site'
},
{
key: 'secondary_navigation',
type: 'array',
group: 'site'
},
{
key: 'meta_title',
type: 'string',
group: 'site'
},
{
key: 'meta_description',
type: 'string',
group: 'site'
},
{
key: 'og_image',
type: 'string',
group: 'site'
},
{
key: 'og_title',
type: 'string',
group: 'site'
},
{
key: 'og_description',
type: 'string',
group: 'site'
},
{
key: 'twitter_image',
type: 'string',
group: 'site'
},
{
key: 'twitter_title',
type: 'string',
group: 'site'
},
{
key: 'twitter_description',
type: 'string',
group: 'site'
},
{
key: 'active_theme',
type: 'string',
group: 'theme'
},
{
key: 'is_private',
type: 'boolean',
group: 'private'
},
{
key: 'password',
type: 'string',
group: 'private'
},
{
key: 'public_hash',
type: 'string',
group: 'private'
},
{
key: 'default_content_visibility',
type: 'string',
group: 'members'
},
{
key: 'members_signup_access',
type: 'string',
group: 'members'
},
{
key: 'members_from_address',
type: 'string',
group: 'members'
},
{
key: 'members_support_address',
type: 'string',
group: 'members'
},
{
key: 'members_reply_address',
type: 'string',
group: 'members'
},
{
key: 'members_paid_signup_redirect',
type: 'string',
group: 'members'
},
{
key: 'members_free_signup_redirect',
type: 'string',
group: 'members'
},
{
key: 'members_free_price_name',
type: 'string',
group: 'members'
},
{
key: 'members_free_price_description',
type: 'string',
group: 'members'
},
{
key: 'members_monthly_price_id',
type: 'string',
group: 'members'
},
{
key: 'members_yearly_price_id',
type: 'string',
group: 'members'
},
{
key: 'stripe_product_name',
type: 'string',
group: 'members'
},
{
key: 'stripe_plans',
type: 'array',
group: 'members'
},
{
key: 'stripe_secret_key',
type: 'string',
group: 'members'
},
{
key: 'stripe_publishable_key',
type: 'string',
group: 'members'
},
{
key: 'stripe_connect_secret_key',
type: 'string',
group: 'members'
},
{
key: 'stripe_connect_publishable_key',
type: 'string',
group: 'members'
},
{
key: 'stripe_connect_account_id',
type: 'string',
group: 'members'
},
{
key: 'stripe_connect_display_name',
type: 'string',
group: 'members'
},
{
key: 'stripe_connect_livemode',
type: 'boolean',
group: 'members'
},
{
key: 'portal_name',
type: 'boolean',
group: 'portal'
},
{
key: 'portal_button',
type: 'boolean',
group: 'portal'
},
{
key: 'portal_plans',
type: 'array',
group: 'portal'
},
{
key: 'portal_button_style',
type: 'string',
group: 'portal'
},
{
key: 'portal_button_icon',
type: 'string',
group: 'portal'
},
{
key: 'portal_button_signup_text',
type: 'string',
group: 'portal'
},
{
key: 'mailgun_api_key',
type: 'string',
group: 'email'
},
{
key: 'mailgun_domain',
type: 'string',
group: 'email'
},
{
key: 'mailgun_base_url',
type: 'string',
group: 'email'
},
{
key: 'email_track_opens',
type: 'boolean',
group: 'email'
},
{
key: 'amp',
type: 'boolean',
group: 'amp'
},
{
key: 'amp_gtag_id',
type: 'string',
group: 'amp'
},
{
key: 'slack',
type: 'string',
group: 'slack'
},
{
key: 'slack_url',
type: 'string',
group: 'slack'
},
{
key: 'slack_username',
type: 'string',
group: 'slack'
},
{
key: 'unsplash',
type: 'boolean',
group: 'unsplash'
},
{
key: 'shared_views',
type: 'array',
group: 'views'
},
{
key: 'active_timezone',
type: 'string',
group: 'site'
},
{
key: 'default_locale',
type: 'string',
group: 'site'
},
{
key: 'accent_color',
type: 'string',
group: 'site'
},
{
key: 'newsletter_show_badge',
type: 'boolean',
group: 'newsletter'
},
{
key: 'newsletter_show_header',
type: 'boolean',
group: 'newsletter'
},
{
key: 'newsletter_header_image',
type: 'string',
group: 'newsletter'
},
{
key: 'newsletter_show_header_icon',
type: 'boolean',
group: 'newsletter'
},
{
key: 'newsletter_show_header_title',
type: 'boolean',
group: 'newsletter'
},
{
key: 'newsletter_title_alignment',
type: 'string',
group: 'newsletter'
},
{
key: 'newsletter_title_font_category',
type: 'string',
group: 'newsletter'
},
{
key: 'newsletter_show_feature_image',
type: 'boolean',
group: 'newsletter'
},
{
key: 'newsletter_body_font_category',
type: 'string',
group: 'newsletter'
},
{
key: 'newsletter_footer_content',
type: 'string',
group: 'newsletter'
},
{
key: 'firstpromoter',
type: 'boolean',
group: 'firstpromoter'
},
{
key: 'firstpromoter_id',
type: 'string',
group: 'firstpromoter'
},
{
key: 'oauth_client_id',
type: 'string',
group: 'oauth'
},
{
key: 'oauth_client_secret',
type: 'string',
group: 'oauth'
},
{
key: 'editor_default_email_recipients',
type: 'string',
group: 'editor'
},
{
key: 'editor_default_email_recipients_filter',
type: 'string',
group: 'editor'
},
{
key: 'labs',
type: 'object',
group: 'labs'
}
];
describe('Settings API (canary)', function () {
let ghostServer;
let request;
describe('As Owner', function () {
before(function () {
return ghost()
.then(function (_ghostServer) {
ghostServer = _ghostServer;
request = supertest.agent(config.get('url'));
})
.then(function () {
return 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)
&& (setting.group === defaultSetting.group);
}), `Expected to find a setting with key ${defaultSetting.key}, type ${defaultSetting.type}, and group ${defaultSetting.group}`);
}
const unsplash = settings.find(s => s.key === 'unsplash');
should.exist(unsplash);
unsplash.value.should.equal(true);
localUtils.API.checkResponse(jsonResponse, 'settings');
});
});
it('Ignores the deprecated type filter', 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;
// Returns all 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}`);
}
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('string');
settings[0].group.should.equal('theme');
localUtils.API.checkResponse(jsonResponse, 'settings');
});
});
it('Requesting core settings type ignores the parameter and returns all settings', 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(defaultSettingsKeyTypes.length);
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 secret setting', function (done) {
const key = 'stripe_secret_key';
request
.get(localUtils.API.getApiQuery(`settings/${key}/`))
.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 json = res.body;
should.exist(json);
should.exist(json.settings);
json.settings.length.should.eql(1);
json.settings[0].key.should.eql('stripe_secret_key');
should(json.settings[0].value).be.null();
done();
});
});
it('Can\'t read secret setting', function (done) {
const key = 'stripe_secret_key';
request.put(localUtils.API.getApiQuery('settings/'))
.set('Origin', config.get('url'))
.send({
settings: [{
key,
value: 'sk_test_4eC39HqLyjWDarjtT1zdp7dc'
}]
})
.then(() => {
request
.get(localUtils.API.getApiQuery(`settings/${key}/`))
.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 json = res.body;
should.exist(json);
should.exist(json.settings);
json.settings.length.should.eql(1);
json.settings[0].key.should.eql('stripe_secret_key');
json.settings[0].value.should.eql('••••••••');
done();
});
});
});
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 edit deprecated lang setting', function () {
return request.get(localUtils.API.getApiQuery('settings/lang/'))
.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: 'lang', 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('lang');
jsonResponse.settings[0].value.should.eql('ua');
});
});
});
it('Can edit newly introduced locale setting', function () {
return request.get(localUtils.API.getApiQuery('settings/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;
should.exist(jsonResponse);
should.exist(jsonResponse.settings);
jsonResponse.settings = [{key: 'locale', value: 'ge'}];
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('locale');
jsonResponse.settings[0].value.should.eql('ge');
});
});
});
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('Format of unsplash is boolean as introduced with v4', 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', 'group', 'key', 'value', 'type', 'flags', 'created_at', 'updated_at']);
jsonResponse.settings[0].key.should.eql('unsplash');
JSON.parse(jsonResponse.settings[0].value).should.eql(true);
done();
});
});
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 edit only allowed 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(1);
testUtils.API.checkResponseValue(jsonResponse.settings[0], ['id', 'group', 'key', 'value', 'type', 'flags', 'created_at', 'updated_at']);
jsonResponse.settings[0].key.should.eql('labs');
jsonResponse.settings[0].value.should.eql(JSON.stringify({
activitypub: true
}));
});
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: 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, 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 edit URLs without internal storage format leaking', async function () {
const settingsToChange = {
settings: [
{key: 'cover_image', value: `${config.get('url')}/content/images/cover_image.png`},
{key: 'logo', value: `${config.get('url')}/content/images/logo.png`},
{key: 'icon', value: `${config.get('url')}/content/images/icon.png`},
{key: 'portal_button_icon', value: `${config.get('url')}/content/images/portal_button_icon.png`},
{key: 'og_image', value: `${config.get('url')}/content/images/og_image.png`},
{key: 'twitter_image', value: `${config.get('url')}/content/images/twitter_image.png`}
]
};
const {body} = 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(200);
const responseSettings = body.settings.reduce((acc, setting) => {
acc[setting.key] = setting.value;
return acc;
}, {});
responseSettings.should.have.property('cover_image', `${config.get('url')}/content/images/cover_image.png`);
responseSettings.should.have.property('logo', `${config.get('url')}/content/images/logo.png`);
responseSettings.should.have.property('icon', `${config.get('url')}/content/images/icon.png`);
responseSettings.should.have.property('portal_button_icon', `${config.get('url')}/content/images/portal_button_icon.png`);
responseSettings.should.have.property('og_image', `${config.get('url')}/content/images/og_image.png`);
responseSettings.should.have.property('twitter_image', `${config.get('url')}/content/images/twitter_image.png`);
const dbSettingsRows = await db.knex('settings')
.select('key', 'value')
.whereIn('key', ['cover_image', 'logo', 'icon', 'portal_button_icon', 'og_image', 'twitter_image']);
const dbSettings = dbSettingsRows.reduce((acc, setting) => {
acc[setting.key] = setting.value;
return acc;
}, {});
dbSettings.should.have.property('cover_image', '__GHOST_URL__/content/images/cover_image.png');
dbSettings.should.have.property('logo', '__GHOST_URL__/content/images/logo.png');
dbSettings.should.have.property('icon', '__GHOST_URL__/content/images/icon.png');
dbSettings.should.have.property('portal_button_icon', '__GHOST_URL__/content/images/portal_button_icon.png');
dbSettings.should.have.property('og_image', '__GHOST_URL__/content/images/og_image.png');
dbSettings.should.have.property('twitter_image', '__GHOST_URL__/content/images/twitter_image.png');
});
});
describe('As Admin', function () {
before(function () {
return ghost()
.then(function (_ghostServer) {
ghostServer = _ghostServer;
request = supertest.agent(config.get('url'));
})
.then(function () {
// create admin
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;
// by default we login with the owner
return localUtils.doAuth(request);
});
});
});
describe('As Editor', function () {
let editor;
before(function () {
return ghost()
.then(function (_ghostServer) {
ghostServer = _ghostServer;
request = supertest.agent(config.get('url'));
})
.then(function () {
// create editor
return testUtils.createUser({
user: testUtils.DataGenerator.forKnex.createUser({email: 'test+1@ghost.org'}),
role: testUtils.DataGenerator.Content.roles[1].name
});
})
.then(function (_user1) {
editor = _user1;
request.user = editor;
// by default we login with the owner
return 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(function () {
return ghost()
.then(function (_ghostServer) {
ghostServer = _ghostServer;
request = supertest.agent(config.get('url'));
})
.then(function () {
// create author
return testUtils.createUser({
user: testUtils.DataGenerator.forKnex.createUser({email: 'test+2@ghost.org'}),
role: testUtils.DataGenerator.Content.roles[2].name
});
})
.then(function (author) {
request.user = author;
// by default we login with the owner
return 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'
]);
});
});
});
});
});