Ghost/test/regression/models/base/overrides.test.js
Kevin Ansfield 256f16a01f
🐛 Fixed URLs not being correctly transformed during insert operations (#13618)
closes https://github.com/TryGhost/Team/issues/1150

Our override of the base Bookshelf `insert` operation so that our own `formatOnWrite()` method is called on attributes was working on a false assumption that an `attrs` attribute is passed in as it is for the `update` operation. Instead Bookshelf's base update uses the `model.attributes` values to create an `attrs` object that is then passed through the usual `.format()` method meaning that our `insert` override was not actually doing anything.

- added a failing regression test for the `formatOnWrite()` override behaviour
- adjusted our insert/update overrides to set an internal `_isWriting` property on the model, then if that property is true our `.format()` override (which is called by Bookshelf on a generated `attrs` object during inserts) we manually call our `.formatOnWrite()` method
  - updated both overrides even though `update` was working for consistency and less cognitive overhead for reasoning between two different approaches
2021-10-20 15:22:46 +01:00

85 lines
3.4 KiB
JavaScript

const sinon = require('sinon');
const testUtils = require('../../../utils');
const db = require('../../../../core/server/data/db');
const models = require('../../../../core/server/models');
describe('Models: Base plugins: Overrides', function () {
before(testUtils.teardownDb);
before(testUtils.stopGhost);
after(testUtils.teardownDb);
// initializes models
before(testUtils.setup('users:roles'));
describe('formatOnWrite', function () {
// using CustomThemeSetting model because it transforms .value relative URLs
// to __GHOST_URL__ URLs in formatOnWrite()
it('formats values correctly on update', async function () {
const modelToUpdate = await models.CustomThemeSetting.add({
theme: 'test',
key: 'update_test',
value: 'https://example.com/image.png',
type: 'image'
});
// direct knex query for sense-check
const afterInsert = await db.knex('custom_theme_settings').where({id: modelToUpdate.id});
afterInsert.length.should.equal(1);
afterInsert[0].value.should.equal('https://example.com/image.png');
// do the actual update
const modelAfterUpdate = await models.CustomThemeSetting.edit({value: '/image.png'}, {id: modelToUpdate.id});
// model.value should return an absolute URL
modelAfterUpdate.get('value').should.equal('http://127.0.0.1:2369/image.png');
// direct knex query to check raw db value
const afterUpdate = await db.knex('custom_theme_settings').where({id: modelToUpdate.id});
afterUpdate.length.should.equal(1);
afterUpdate[0].value.should.equal('__GHOST_URL__/image.png');
});
it('formats values correctly on insert', async function () {
const insertedModel = await models.CustomThemeSetting.add({
theme: 'test',
key: 'update_test',
value: '/image.png',
type: 'image'
});
// model.value should return an absolute URL
insertedModel.get('value').should.equal('http://127.0.0.1:2369/image.png');
// direct knex query to check raw db value
const rawInsertDate = await db.knex('custom_theme_settings').where({id: insertedModel.id});
rawInsertDate.length.should.equal(1);
rawInsertDate[0].value.should.equal('__GHOST_URL__/image.png');
});
it('is not called unnecessarily', async function () {
const modelInstance = models.CustomThemeSetting.forge({
theme: 'test',
key: 'update_test',
value: '/image.png',
type: 'image'
});
const format = sinon.spy(modelInstance, 'format');
const formatOnWrite = sinon.spy(modelInstance, 'formatOnWrite');
await modelInstance.save(null, {method: 'insert'});
// sanity check
modelInstance.get('value').should.equal('http://127.0.0.1:2369/image.png');
// called twice because format is also called on fetch
// see https://github.com/TryGhost/Ghost/commit/426cbeec0f57886fbb4c7a1ebd2ce696913b03eb
format.callCount.should.equal(2);
// only called once for the actual write
formatOnWrite.callCount.should.equal(1);
});
});
});