0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-03 23:00:14 -05:00

Improved network access and mocking in tests (#16371)

refs https://github.com/TryGhost/Team/issues/2667

Some tests still accessed the internet. Now network access is disabled
by default. This change also introduces two helper methods related to
networking (mocking Slack and Mailgun).

This fixes two unreliable tests:
- Staff service was accessing a Slack test API -> timeout possible
- MentionSendingService was trying to send webmentions for every post
publish/change -> possible timeouts and job issues
This commit is contained in:
Simon Backx 2023-03-07 13:20:28 +01:00 committed by GitHub
parent 9fde77a95c
commit f50224f445
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 78 additions and 40 deletions

View file

@ -5,6 +5,7 @@ const supertest = require('supertest');
const testUtils = require('../../utils/index'); const testUtils = require('../../utils/index');
const config = require('../../../core/shared/config/index'); const config = require('../../../core/shared/config/index');
const localUtils = require('./utils'); const localUtils = require('./utils');
const {mockManager} = require('../../utils/e2e-framework');
// for sinon stubs // for sinon stubs
const dnsPromises = require('dns').promises; const dnsPromises = require('dns').promises;
@ -20,9 +21,7 @@ describe('Oembed API', function () {
beforeEach(function () { beforeEach(function () {
// ensure sure we're not network dependent // ensure sure we're not network dependent
sinon.stub(dnsPromises, 'lookup').callsFake(function () { mockManager.disableNetwork();
return Promise.resolve({address: '123.123.123.123'});
});
}); });
afterEach(function () { afterEach(function () {

View file

@ -1,9 +1,7 @@
const {agentProvider, fixtureManager, mockManager} = require('../../utils/e2e-framework'); const {agentProvider, fixtureManager, mockManager} = require('../../utils/e2e-framework');
const sinon = require('sinon');
const nock = require('nock'); const nock = require('nock');
const assert = require('assert'); const assert = require('assert');
const markdownToMobiledoc = require('../../utils/fixtures/data-generator').markdownToMobiledoc; const markdownToMobiledoc = require('../../utils/fixtures/data-generator').markdownToMobiledoc;
const dnsPromises = require('dns').promises;
const jobsService = require('../../../core/server/services/mentions-jobs'); const jobsService = require('../../../core/server/services/mentions-jobs');
let agent; let agent;
@ -30,9 +28,7 @@ describe('Mentions Service', function () {
beforeEach(async function () { beforeEach(async function () {
// externalRequest does dns lookup; stub to make sure we don't fail with fake domain names // externalRequest does dns lookup; stub to make sure we don't fail with fake domain names
sinon.stub(dnsPromises, 'lookup').callsFake(function () { mockManager.disableNetwork();
return Promise.resolve({address: '123.123.123.123'});
});
// mock response from website mentioned by post to provide endpoint // mock response from website mentioned by post to provide endpoint
mentionMock = nock(mentionUrl.href) mentionMock = nock(mentionUrl.href)

View file

@ -181,9 +181,14 @@ describe('Batch sending tests', function () {
stubbedSend = sinon.fake.resolves({ stubbedSend = sinon.fake.resolves({
id: 'stubbed-email-id' id: 'stubbed-email-id'
}); });
mockManager.mockMail();
mockManager.mockMailgun(function () {
return stubbedSend.call(this, ...arguments);
});
}); });
afterEach(async function () { afterEach(async function () {
mockManager.restore();
await configUtils.restore(); await configUtils.restore();
await models.Settings.edit([{ await models.Settings.edit([{
key: 'email_verification_required', key: 'email_verification_required',
@ -192,21 +197,6 @@ describe('Batch sending tests', function () {
}); });
before(async function () { before(async function () {
mockManager.mockSetting('mailgun_api_key', 'test');
mockManager.mockSetting('mailgun_domain', 'example.com');
mockManager.mockSetting('mailgun_base_url', 'test');
mockManager.mockMail();
// We need to stub the Mailgun client before starting Ghost
sinon.stub(MailgunClient.prototype, 'getInstance').returns({
// @ts-ignore
messages: {
create: async function () {
return await stubbedSend.call(this, ...arguments);
}
}
});
const agents = await agentProvider.getAgentsWithFrontend(); const agents = await agentProvider.getAgentsWithFrontend();
agent = agents.adminAgent; agent = agents.adminAgent;
frontendAgent = agents.frontendAgent; frontendAgent = agents.frontendAgent;
@ -223,7 +213,6 @@ describe('Batch sending tests', function () {
}); });
after(async function () { after(async function () {
mockManager.restore();
await ghostServer.stop(); await ghostServer.stop();
}); });
@ -240,7 +229,7 @@ describe('Batch sending tests', function () {
await completedPromise; await completedPromise;
await emailModel.refresh(); await emailModel.refresh();
assert.equal(emailModel.get('status'), 'submitted'); assert.equal(emailModel.get('status'), 'submitted', 'This email should be in submitted state: ' + (emailModel.get('error') ?? 'No error'));
assert.equal(emailModel.get('email_count'), 4); assert.equal(emailModel.get('email_count'), 4);
// Did we create batches? // Did we create batches?

View file

@ -3,6 +3,7 @@ const TwitterOEmbedProvider = require('../../../../../core/server/services/oembe
const externalRequest = require('../../../../../core/server/lib/request-external'); const externalRequest = require('../../../../../core/server/lib/request-external');
const nock = require('nock'); const nock = require('nock');
const sinon = require('sinon'); const sinon = require('sinon');
const {mockManager} = require('../../../../utils/e2e-framework');
const dnsPromises = require('dns').promises; const dnsPromises = require('dns').promises;
describe('TwitterOEmbedProvider', function () { describe('TwitterOEmbedProvider', function () {
@ -12,14 +13,11 @@ describe('TwitterOEmbedProvider', function () {
beforeEach(function () { beforeEach(function () {
// external requests will attempt dns lookup // external requests will attempt dns lookup
sinon.stub(dnsPromises, 'lookup').callsFake(function () { mockManager.disableNetwork();
return Promise.resolve({address: '123.123.123.123'});
});
}); });
afterEach(async function () { afterEach(async function () {
nock.cleanAll(); mockManager.restore();
sinon.restore();
}); });
after(async function () { after(async function () {

View file

@ -18,6 +18,7 @@ describe('Staff Service:', function () {
beforeEach(function () { beforeEach(function () {
mockManager.mockMail(); mockManager.mockMail();
mockManager.mockSlack();
userModelStub = sinon.stub(models.User, 'getEmailAlertUsers').resolves([{ userModelStub = sinon.stub(models.User, 'getEmailAlertUsers').resolves([{
email: 'owner@ghost.org', email: 'owner@ghost.org',
slug: 'ghost' slug: 'ghost'

View file

@ -2,6 +2,7 @@ const errors = require('@tryghost/errors');
const sinon = require('sinon'); const sinon = require('sinon');
const assert = require('assert'); const assert = require('assert');
const nock = require('nock'); const nock = require('nock');
const MailgunClient = require('@tryghost/mailgun-client/lib/mailgun-client');
// Helper services // Helper services
const configUtils = require('./configUtils'); const configUtils = require('./configUtils');
@ -35,17 +36,31 @@ const disableStripe = async () => {
await stripeService.disconnect(); await stripeService.disconnect();
}; };
const mockStripe = () => {
nock.disableNetConnect();
};
const disableNetwork = () => { const disableNetwork = () => {
nock.disableNetConnect(); nock.disableNetConnect();
// externalRequest does dns lookup; stub to make sure we don't fail with fake domain names // externalRequest does dns lookup; stub to make sure we don't fail with fake domain names
if (!dnsPromises.lookup.restore) {
sinon.stub(dnsPromises, 'lookup').callsFake(() => { sinon.stub(dnsPromises, 'lookup').callsFake(() => {
return Promise.resolve({address: '123.123.123.123', family: 4}); return Promise.resolve({address: '123.123.123.123', family: 4});
}); });
}
// Allow localhost
nock.enableNetConnect('127.0.0.1');
};
const mockStripe = () => {
disableNetwork();
};
const mockSlack = () => {
disableNetwork();
nock(/hooks.slack.com/)
.persist()
.post('/')
.reply(200, 'ok');
}; };
/** /**
@ -68,6 +83,26 @@ const mockMail = (response = 'Mail is disabled') => {
return mockMailReceiver; return mockMailReceiver;
}; };
const mockMailgun = (customStubbedSend) => {
mockSetting('mailgun_api_key', 'test');
mockSetting('mailgun_domain', 'example.com');
mockSetting('mailgun_base_url', 'test');
const stubbedSend = customStubbedSend ?? sinon.fake.resolves({
id: `<${new Date().getTime()}.${0}.5817@samples.mailgun.org>`
});
// We need to stub the Mailgun client before starting Ghost
sinon.stub(MailgunClient.prototype, 'getInstance').returns({
// @ts-ignore
messages: {
create: async function () {
return await stubbedSend.call(this, ...arguments);
}
}
});
};
const mockWebhookRequests = () => { const mockWebhookRequests = () => {
mocks.webhookMockReceiver = new WebhookMockReceiver({snapshotManager}); mocks.webhookMockReceiver = new WebhookMockReceiver({snapshotManager});
@ -210,6 +245,9 @@ const restore = () => {
} }
mailService.GhostMailer.prototype.send = originalMailServiceSend; mailService.GhostMailer.prototype.send = originalMailServiceSend;
// Disable network again after restoring sinon
disableNetwork();
}; };
module.exports = { module.exports = {
@ -217,6 +255,8 @@ module.exports = {
mockMail, mockMail,
disableStripe, disableStripe,
mockStripe, mockStripe,
mockSlack,
mockMailgun,
mockLabsEnabled, mockLabsEnabled,
mockLabsDisabled, mockLabsDisabled,
mockWebhookRequests, mockWebhookRequests,

View file

@ -82,6 +82,9 @@ const startGhost = async (options = {}) => {
await urlServiceUtils.isFinished(); await urlServiceUtils.isFinished();
} }
// Disable network in tests at the start
mockManager.disableNetwork();
return ghostServer; return ghostServer;
}; };

View file

@ -5,3 +5,15 @@ require('../../core/server/overrides');
const {mochaHooks} = require('@tryghost/express-test').snapshot; const {mochaHooks} = require('@tryghost/express-test').snapshot;
exports.mochaHooks = mochaHooks; exports.mochaHooks = mochaHooks;
const mockManager = require('./e2e-framework-mock-manager');
const originalBeforeAll = mochaHooks.beforeAll;
mochaHooks.beforeAll = async function () {
if (originalBeforeAll) {
await originalBeforeAll();
}
// Disable network in tests to prevent any accidental requests
mockManager.disableNetwork();
};