Prevent simple error logs by stubbing log library

refs: https://github.com/TryGhost/Toolbox/issues/389

This removes many error logs when the end-to-end test suite is run with the log-level set to error. Many errors are intentional, so the resolution is typically to stub the error log function and assert that it would have been called.
This commit is contained in:
Sam Lord 2023-03-03 17:58:19 +00:00
parent cd5aca7227
commit 13b0f51b13
13 changed files with 92 additions and 0 deletions

View File

@ -4,6 +4,8 @@ const models = require('../../../core/server/models');
const assert = require('assert');
const moment = require('moment');
const sinon = require('sinon');
const logging = require('@tryghost/logging');
let agent;
@ -94,6 +96,7 @@ describe('Activity Feed API', function () {
afterEach(function () {
mockManager.restore();
sinon.restore();
});
describe('Filter splitting', function () {
@ -119,6 +122,7 @@ describe('Activity Feed API', function () {
it('Cannot combine type filter with OR filter', async function () {
// This query is not allowed because we need to split the filter in two AND filters
const loggingStub = sinon.stub(logging, 'error');
await agent
.get(`/members/events?filter=type:comment_event,data.post_id:123`)
.expectStatus(400)
@ -133,9 +137,11 @@ describe('Activity Feed API', function () {
}
]
});
sinon.assert.calledOnce(loggingStub);
});
it('Can only combine type and other filters at the root level', async function () {
const loggingStub = sinon.stub(logging, 'error');
await agent
.get(`/members/events?filter=${encodeURIComponent('(type:comment_event+data.post_id:123)+data.post_id:123')}`)
.expectStatus(400)
@ -150,6 +156,7 @@ describe('Activity Feed API', function () {
}
]
});
sinon.assert.calledOnce(loggingStub);
});
it('Can use OR as long as it is not combined with type', async function () {

View File

@ -4,6 +4,8 @@ const testUtils = require('../../utils');
const localUtils = require('./utils');
const config = require('../../../core/shared/config');
const sinon = require('sinon');
const logging = require('@tryghost/logging');
describe('Custom Theme Settings API', function () {
let request;
@ -38,6 +40,10 @@ describe('Custom Theme Settings API', function () {
});
});
afterEach(function () {
sinon.restore();
});
describe('Browse', function () {
it('can fetch settings for current theme', async function () {
const res = await request
@ -176,6 +182,7 @@ describe('Custom Theme Settings API', function () {
value: 'Not gonna work'
}];
const loggingStub = sinon.stub(logging, 'error');
const res = await request
.put(localUtils.API.getApiQuery(`custom_theme_settings/`))
.set('Origin', config.get('url'))
@ -189,6 +196,7 @@ describe('Custom Theme Settings API', function () {
const jsonResponse = res.body;
should.exist(jsonResponse);
should.exist(jsonResponse.errors);
sinon.assert.calledOnce(loggingStub);
});
it('errors for invalid select value', async function () {
@ -197,6 +205,7 @@ describe('Custom Theme Settings API', function () {
value: 'Not gonna work'
}];
const loggingStub = sinon.stub(logging, 'error');
const res = await request
.put(localUtils.API.getApiQuery(`custom_theme_settings/`))
.set('Origin', config.get('url'))
@ -210,6 +219,7 @@ describe('Custom Theme Settings API', function () {
const jsonResponse = res.body;
should.exist(jsonResponse);
should.exist(jsonResponse.errors);
sinon.assert.calledOnce(loggingStub);
});
});
});

View File

@ -1,11 +1,13 @@
const {agentProvider, fixtureManager, matchers, mockManager} = require('../../utils/e2e-framework');
const {anyEtag, anyErrorId, anyContentVersion} = matchers;
const assert = require('assert');
const sinon = require('sinon');
// @TODO: factor out these requires
const ObjectId = require('bson-objectid').default;
const testUtils = require('../../utils');
const models = require('../../../core/server/models/index');
const logging = require('@tryghost/logging');
describe('Email Preview API', function () {
let agent;
@ -16,6 +18,7 @@ describe('Email Preview API', function () {
afterEach(function () {
mockManager.restore();
sinon.restore();
});
before(async function () {
@ -238,6 +241,7 @@ describe('Email Preview API', function () {
});
it('cannot send test email', async function () {
const loggingStub = sinon.stub(logging, 'error');
await agent
.post(`email_previews/posts/${fixtureManager.get('posts', 0).id}/`)
.body({
@ -253,6 +257,7 @@ describe('Email Preview API', function () {
id: anyErrorId
}]
});
sinon.assert.calledOnce(loggingStub);
});
});
@ -262,6 +267,7 @@ describe('Email Preview API', function () {
});
it('cannot send test email', async function () {
const loggingStub = sinon.stub(logging, 'error');
await agent
.post(`email_previews/posts/${fixtureManager.get('posts', 0).id}/`)
.body({
@ -277,6 +283,7 @@ describe('Email Preview API', function () {
id: anyErrorId
}]
});
sinon.assert.calledOnce(loggingStub);
});
});
});

View File

@ -1,6 +1,8 @@
const {agentProvider, fixtureManager, matchers, mockManager} = require('../../utils/e2e-framework');
const {nullable, anything, anyContentVersion, anyEtag, anyObjectId, anyUuid, anyISODateTime, anyErrorId, anyString} = matchers;
const assert = require('assert');
const sinon = require('sinon');
const logging = require('@tryghost/logging');
const matchEmail = {
id: anyObjectId,
@ -39,6 +41,7 @@ describe('Emails API', function () {
afterEach(function () {
mockManager.restore();
sinon.restore();
});
it('Can browse emails', async function () {
@ -83,6 +86,7 @@ describe('Emails API', function () {
});
it('Errors when retrying an email that was successful', async function () {
const loggingStub = sinon.stub(logging, 'error');
await agent
.put(`emails/${fixtureManager.get('emails', 0).id}/retry`)
.expectStatus(400)
@ -95,6 +99,7 @@ describe('Emails API', function () {
'content-version': anyContentVersion,
etag: anyEtag
});
sinon.assert.calledOnce(loggingStub);
});
it('Can browse email batches', async function () {

View File

@ -12,6 +12,7 @@ const sleep = require('../../utils/sleep');
const {anyErrorId} = matchers;
const {imageSize} = require('../../../core/server/lib/image');
const configUtils = require('../../utils/configUtils');
const logging = require('@tryghost/logging');
const images = [];
let agent, frontendAgent, ghostServer;
@ -188,6 +189,7 @@ describe('Images API', function () {
it('Can not upload a json file', async function () {
const originalFilePath = p.join(__dirname, '/../../utils/fixtures/data/redirects.json');
const fileContents = await fs.readFile(originalFilePath);
const loggingStub = sinon.stub(logging, 'error');
await uploadImageRequest({fileContents, filename: 'redirects.json', contentType: 'application/json'})
.expectStatus(415)
.matchBodySnapshot({
@ -195,11 +197,13 @@ describe('Images API', function () {
id: anyErrorId
}]
});
sinon.assert.calledOnce(loggingStub);
});
it('Can not upload a file without extension', async function () {
const originalFilePath = p.join(__dirname, '/../../utils/fixtures/data/redirects.json');
const fileContents = await fs.readFile(originalFilePath);
const loggingStub = sinon.stub(logging, 'error');
await uploadImageRequest({fileContents, filename: 'redirects', contentType: 'image/png'})
.expectStatus(415)
.matchBodySnapshot({
@ -207,11 +211,13 @@ describe('Images API', function () {
id: anyErrorId
}]
});
sinon.assert.calledOnce(loggingStub);
});
it('Can not upload a json file with image mime type', async function () {
const originalFilePath = p.join(__dirname, '/../../utils/fixtures/data/redirects.json');
const fileContents = await fs.readFile(originalFilePath);
const loggingStub = sinon.stub(logging, 'error');
await uploadImageRequest({fileContents, filename: 'redirects.json', contentType: 'image/gif'})
.expectStatus(415)
.matchBodySnapshot({
@ -219,11 +225,13 @@ describe('Images API', function () {
id: anyErrorId
}]
});
sinon.assert.calledOnce(loggingStub);
});
it('Can not upload a json file with image file extension', async function () {
const originalFilePath = p.join(__dirname, '/../../utils/fixtures/data/redirects.json');
const fileContents = await fs.readFile(originalFilePath);
const loggingStub = sinon.stub(logging, 'error');
await uploadImageRequest({fileContents, filename: 'redirects.png', contentType: 'application/json'})
.expectStatus(415)
.matchBodySnapshot({
@ -231,6 +239,7 @@ describe('Images API', function () {
id: anyErrorId
}]
});
sinon.assert.calledOnce(loggingStub);
});
it('Can upload multiple images with the same name', async function () {

View File

@ -4,6 +4,8 @@ const testUtils = require('../../utils');
const config = require('../../../core/shared/config');
const localUtils = require('./utils');
const configUtils = require('../../utils/configUtils');
const sinon = require('sinon');
const logging = require('@tryghost/logging');
describe('Admin API key authentication', function () {
let request;
@ -14,20 +16,28 @@ describe('Admin API key authentication', function () {
await testUtils.initFixtures('api_keys');
});
afterEach(function () {
sinon.restore();
});
it('Can not access endpoint without a token header', async function () {
const loggingStub = sinon.stub(logging, 'error');
await request.get(localUtils.API.getApiQuery('posts/'))
.set('Authorization', `Ghost`)
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(401);
sinon.assert.calledOnce(loggingStub);
});
it('Can not access endpoint with a wrong endpoint token', async function () {
const loggingStub = sinon.stub(logging, 'error');
await request.get(localUtils.API.getApiQuery('posts/'))
.set('Authorization', `Ghost ${localUtils.getValidAdminToken('https://wrong.com')}`)
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(401);
sinon.assert.calledOnce(loggingStub);
});
it('Can access browse endpoint with correct token', async function () {
@ -88,6 +98,8 @@ describe('Admin API key authentication', function () {
await testUtils.initFixtures('integrations');
await testUtils.initFixtures('api_keys');
const loggingStub = sinon.stub(logging, 'error');
const firstResponse = await request.get(localUtils.API.getApiQuery('posts/'))
.set('Authorization', `Ghost ${localUtils.getValidAdminToken('/admin/')}`)
.expect('Content-Type', /json/)
@ -96,6 +108,7 @@ describe('Admin API key authentication', function () {
firstResponse.body.errors[0].type.should.equal('HostLimitError');
firstResponse.body.errors[0].message.should.equal('Custom limit error message');
sinon.assert.calledOnce(loggingStub);
// CASE: Test with a different API key, related to a core integration
const secondResponse = await request.get(localUtils.API.getApiQuery('explore/'))

View File

@ -1,5 +1,7 @@
const logging = require('@tryghost/logging');
const {agentProvider, fixtureManager, matchers} = require('../../utils/e2e-framework');
const {anyContentVersion, anyObjectId, anyISODateTime, anyErrorId, anyEtag, anyLocationFor} = matchers;
const sinon = require('sinon');
const matchLabel = {
id: anyObjectId,
@ -16,6 +18,10 @@ describe('Labels API', function () {
await agent.loginAsOwner();
});
afterEach(function () {
sinon.restore();
});
it('Can browse with no labels', async function () {
await agent
.get('labels')
@ -45,6 +51,7 @@ describe('Labels API', function () {
});
it('Errors when adding label with the same name', async function () {
const loggingStub = sinon.stub(logging, 'error');
await agent
.post('labels')
.body({labels: [{
@ -61,6 +68,7 @@ describe('Labels API', function () {
'content-version': anyContentVersion,
etag: anyEtag
});
sinon.assert.calledOnce(loggingStub);
});
it('Can browse with member count', async function () {

View File

@ -2,9 +2,11 @@ const path = require('path');
const fs = require('fs-extra');
const should = require('should');
const supertest = require('supertest');
const sinon = require('sinon');
const localUtils = require('./utils');
const testUtils = require('../../utils');
const config = require('../../../core/shared/config');
const logging = require('@tryghost/logging');
describe('Media API', function () {
// NOTE: holds paths to media that need to be cleaned up after the tests are run
@ -23,6 +25,10 @@ describe('Media API', function () {
});
});
afterEach(function () {
sinon.restore();
});
describe('media/upload', function () {
it('Can upload a MP4', async function () {
const res = await request.post(localUtils.API.getApiQuery('media/upload'))
@ -114,6 +120,7 @@ describe('Media API', function () {
});
it('Rejects non-media file type', async function () {
const loggingStub = sinon.stub(logging, 'error');
const res = await request.post(localUtils.API.getApiQuery('media/upload'))
.set('Origin', config.get('url'))
.expect('Content-Type', /json/)
@ -122,6 +129,7 @@ describe('Media API', function () {
.expect(415);
res.body.errors[0].message.should.match(/select a valid media file/gi);
sinon.assert.calledOnce(loggingStub);
});
});

View File

@ -18,6 +18,7 @@ const urlService = require('../../../core/server/services/url');
const urlUtils = require('../../../core/shared/url-utils');
const settingsCache = require('../../../core/shared/settings-cache');
const DomainEvents = require('@tryghost/domain-events');
const logging = require('@tryghost/logging');
/**
* Assert that haystack and needles match, ignoring the order.
@ -726,10 +727,13 @@ describe('Members API', function () {
});
const newMember = body.members[0];
// Cannot add same member twice
const loggingStub = sinon.stub(logging, 'error');
await agent
.post(`/members/`)
.body({members: [member]})
.expectStatus(422);
sinon.assert.calledOnce(loggingStub);
await assertMemberEvents({
eventType: 'MemberStatusEvent',
@ -895,6 +899,7 @@ describe('Members API', function () {
const statusEventsBefore = await models.MemberStatusEvent.findAll();
sinon.stub(logging, 'error');
await agent
.post(`members/?send_email=true&email_type=lel`)
.body({members: [newMember]})
@ -1543,6 +1548,7 @@ describe('Members API', function () {
]
};
sinon.stub(logging, 'error');
await agent
.put(`/members/${memberWithPaidSubscription.id}/`)
.body({members: [compedPayload]})
@ -1561,6 +1567,7 @@ describe('Members API', function () {
tiers: []
};
sinon.stub(logging, 'error');
await agent
.put(`/members/${memberWithPaidSubscription.id}/`)
.body({members: [compedPayload]})
@ -2299,6 +2306,7 @@ describe('Members API', function () {
});
it('Errors when fetching stats with unknown days param value', async function () {
sinon.stub(logging, 'error');
await agent
.get('members/stats/?days=nope')
.expectStatus(422)
@ -2398,6 +2406,7 @@ describe('Members API', function () {
etag: anyEtag
});
sinon.stub(logging, 'error');
await agent
.post(`/members/`)
.body({members: [member]})

View File

@ -1,7 +1,9 @@
const assert = require('assert');
const sinon = require('sinon');
const {agentProvider, mockManager, fixtureManager, configUtils, dbUtils, matchers} = require('../../utils/e2e-framework');
const {anyContentVersion, anyEtag, anyObjectId, anyUuid, anyISODateTime, anyLocationFor, anyNumber} = matchers;
const models = require('../../../core/server/models');
const logging = require('@tryghost/logging');
const assertMemberRelationCount = async (newsletterId, expectedCount) => {
const relations = await dbUtils.knex('members_newsletters').where({newsletter_id: newsletterId}).pluck('id');
@ -40,6 +42,7 @@ describe('Newsletters API', function () {
afterEach(function () {
mockManager.restore();
sinon.restore();
});
it('Can browse newsletters', async function () {
@ -392,6 +395,7 @@ describe('Newsletters API', function () {
name: 'Naughty newsletter'
};
sinon.stub(logging, 'error');
await agent
.post(`newsletters/?opt_in_existing=true`)
.body({newsletters: [newsletter]})
@ -426,6 +430,7 @@ describe('Newsletters API', function () {
name: 'Naughty newsletter'
};
sinon.stub(logging, 'error');
await agent
.post(`newsletters/?opt_in_existing=true`)
.body({newsletters: [newsletter]})
@ -449,6 +454,7 @@ describe('Newsletters API', function () {
name: 'Naughty newsletter'
};
sinon.stub(logging, 'error');
// Note that ?opt_in_existing=true will trigger a transaction, so we explicitly test here without a
// transaction
await agent
@ -547,6 +553,7 @@ describe('Newsletters API', function () {
const archivedNewsletter = allNewsletters.find(n => n.get('status') !== 'active');
assert.ok(archivedNewsletter, 'This test expects to have an archived newsletter in the test fixtures');
sinon.stub(logging, 'error');
const id = archivedNewsletter.id;
await agent.put(`newsletters/${id}`)
.body({
@ -634,6 +641,7 @@ describe('Newsletters API', function () {
location: anyLocationFor('newsletters')
});
sinon.stub(logging, 'error');
await agent
.post(`newsletters/`)
.body({newsletters: [secondNewsletter]})
@ -697,6 +705,7 @@ describe('Newsletters API', function () {
it(`Can't edit multiple newsletters to existing name`, async function () {
const id = fixtureManager.get('newsletters', 0).id;
sinon.stub(logging, 'error');
await agent.put(`newsletters/${id}`)
.body({
newsletters: [{

View File

@ -38,6 +38,7 @@ describe('Posts API', function () {
afterEach(function () {
mockManager.restore();
sinon.restore();
nock.cleanAll();
});
@ -142,11 +143,13 @@ describe('Posts API', function () {
});
it('Returns a validation error when unknown filter key is used', async function () {
const loggingStub = sinon.stub(logging, 'error');
await request.get(localUtils.API.getApiQuery('posts/?filter=page:true'))
.set('Origin', config.get('url'))
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(400);
sinon.assert.calledOnce(loggingStub);
});
it('Can paginate posts', async function () {

View File

@ -5,6 +5,7 @@ const testUtils = require('../utils');
const configUtils = require('../utils/configUtils');
const urlUtils = require('../utils/urlUtils');
const settings = require('../../core/shared/settings-cache');
const {mockManager} = require('../utils/e2e-framework');
let request;
@ -22,6 +23,7 @@ describe('Advanced URL Configurations', function () {
before(async function () {
configUtils.set('url', 'http://localhost/blog/');
urlUtils.stubUrlUtilsFromConfig();
mockManager.mockMail();
await testUtils.startGhost({forceStart: true});
@ -31,6 +33,7 @@ describe('Advanced URL Configurations', function () {
after(async function () {
await configUtils.restore();
urlUtils.restore();
mockManager.restore();
});
afterEach(function () {

View File

@ -168,6 +168,7 @@ describe('Milestones Service', function () {
sinon.createSandbox();
configUtils.set('milestones', milestonesConfig);
mockManager.mockLabsEnabled('milestoneEmails');
mockManager.mockMail();
});
afterEach(async function () {