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

Refactored milestones service to preserve API instance (#16272)

no issue

- For better testability with in-memory repository, refactor the
milestones service to preserve the API instance
- Fetching the information about Stripe live mode from Stripe service
was causing difficulties when testing. As a workaround we switched to
reading the live mode keys and determine it that way.

---------

Co-authored-by: Fabien "egg" O'Carroll <fabien@allou.is>
This commit is contained in:
Aileen Booker 2023-02-15 08:43:40 +02:00 committed by GitHub
parent d62eba6c7f
commit 41e4132a17
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 76 additions and 46 deletions

View file

@ -1,22 +1,28 @@
// Stubbing stripe in test was causing issues. Moved it
// into this function to be able to rewire and stub the
// expected return value.
const getStripeLiveEnabled = () => { const getStripeLiveEnabled = () => {
const stripeService = require('../stripe'); const settingsCache = require('../../../shared/settings-cache');
// This seems to be the only true way to check if Stripe is configured in live mode const stripeConnect = settingsCache.get('stripe_connect_publishable_key');
// settingsCache only cares if Stripe is enabled const stripeKey = settingsCache.get('stripe_publishable_key');
return stripeService.api.configured && stripeService.api.mode === 'live';
const stripeLiveRegex = /pk_live_/;
if (stripeConnect && stripeConnect.match(stripeLiveRegex)) {
return true;
} else if (stripeKey && stripeKey.match(stripeLiveRegex)) {
return true;
}
return false;
}; };
/**
*
* @returns {Promise<any>}
*/
module.exports = { module.exports = {
async initAndRun() { /** @type {import('@tryghost/milestone-emails/lib/MilestonesEmailService')} */
const labs = require('../../../shared/labs'); api: null,
if (labs.isSet('milestoneEmails')) { /**
* @returns {Promise<void>}
*/
async init() {
if (!this.api) {
const db = require('../../data/db'); const db = require('../../data/db');
const MilestoneQueries = require('./MilestoneQueries'); const MilestoneQueries = require('./MilestoneQueries');
@ -32,27 +38,42 @@ module.exports = {
const repository = new InMemoryMilestoneRepository(); const repository = new InMemoryMilestoneRepository();
const queries = new MilestoneQueries({db}); const queries = new MilestoneQueries({db});
const milestonesEmailService = new MilestonesEmailService({ this.api = new MilestonesEmailService({
mailer, mailer,
repository, repository,
milestonesConfig, // avoid using getters and pass as JSON milestonesConfig, // avoid using getters and pass as JSON
queries queries
}); });
}
},
let arrResult; /**
* @returns {Promise<object>}
*/
async run() {
const labs = require('../../../shared/labs');
// @TODO: schedule recurring jobs instead if (labs.isSet('milestoneEmails')) {
const membersResult = await milestonesEmailService.checkMilestones('members'); const members = await this.api.checkMilestones('members');
let arr;
const stripeLiveEnabled = getStripeLiveEnabled(); const stripeLiveEnabled = getStripeLiveEnabled();
if (stripeLiveEnabled) { if (stripeLiveEnabled) {
arrResult = await milestonesEmailService.checkMilestones('arr'); arr = await this.api.checkMilestones('arr');
} }
return { return {
members: membersResult, members,
arr: arrResult arr
}; };
} }
},
/**
* @returns {Promise<object>}
*/
async initAndRun() {
await this.init();
return await this.run();
} }
}; };

View file

@ -5,7 +5,7 @@ const sinon = require('sinon');
const models = require('../../../core/server/models'); const models = require('../../../core/server/models');
const moment = require('moment'); const moment = require('moment');
const milestoneEmailsService = require('../../../core/server/services/milestone-emails/service'); const milestoneEmailsService = require('../../../core/server/services/milestone-emails');
let agent; let agent;
let counter = 0; let counter = 0;
@ -138,11 +138,9 @@ async function createFreeMembers(amount, amountImported = 0) {
} }
describe('Milestone Emails Service', function () { describe('Milestone Emails Service', function () {
// let stripeModeStub;
const milestonesConfig = { const milestonesConfig = {
arr: [{currency: 'usd', values: [100]}], arr: [{currency: 'usd', values: [100, 150]}],
members: [10, 100] members: [10, 20, 30]
}; };
before(async function () { before(async function () {
@ -166,46 +164,57 @@ describe('Milestone Emails Service', function () {
sinon.restore(); sinon.restore();
}); });
it('Inits milestone service', async function () {
await milestoneEmailsService.init();
assert.ok(milestoneEmailsService.api);
});
it('Runs ARR and Members milestone jobs', async function () { it('Runs ARR and Members milestone jobs', async function () {
mockManager.mockSetting('stripe_connect_publishable_key', 'pk_live_89843uihsidfh98832uo8ri');
// No ARR and no members // No ARR and no members
const firstRun = await milestoneEmailsService.initAndRun(); const firstRun = await milestoneEmailsService.initAndRun();
assert(firstRun.members === undefined); assert(firstRun.members === undefined);
// assert(firstRun.arr === undefined); assert(firstRun.arr === undefined);
await createFreeMembers(7); await createFreeMembers(7);
await createMemberWithSubscription('year', 5000, 'usd', '2000-01-10'); await createMemberWithSubscription('year', 5000, 'usd', '2000-01-10');
await createMemberWithSubscription('month', 100, 'usd', '2000-01-10'); await createMemberWithSubscription('month', 100, 'usd', '2000-01-10');
const secondRun = await milestoneEmailsService.initAndRun(); const secondRun = await milestoneEmailsService.initAndRun();
assert(secondRun.members === undefined); assert(secondRun.members === undefined);
// assert(secondRun.arr === undefined); assert(secondRun.arr === undefined);
// Reached the first milestone for members // Reached the first milestone for members
await createFreeMembers(1); await createFreeMembers(1);
const thirdRun = await milestoneEmailsService.initAndRun(); const thirdRun = await milestoneEmailsService.initAndRun();
assert(thirdRun.members.value === 10); assert(thirdRun.members.value === 10);
assert(thirdRun.members.emailSentAt !== undefined); assert(thirdRun.members.emailSentAt !== undefined);
// assert(thirdRun.arr === undefined); assert(thirdRun.arr === undefined);
// Reached the first milestone for ARR // Reached the first milestone for ARR
// but has already reached members milestone, so no new one
// will be created
await createMemberWithSubscription('month', 500, 'usd', '2000-01-10'); await createMemberWithSubscription('month', 500, 'usd', '2000-01-10');
await createMemberWithSubscription('month', 500, 'eur', '2000-01-10'); await createMemberWithSubscription('month', 500, 'eur', '2000-01-10');
const fourthRun = await milestoneEmailsService.initAndRun(); const fourthRun = await milestoneEmailsService.initAndRun();
// This will be false once we hook up to the DB assert(fourthRun.members === undefined);
assert(fourthRun.members.value === 10); assert(fourthRun.arr.value === 100);
assert(fourthRun.members.emailSentAt !== undefined); assert(fourthRun.arr.emailSentAt !== undefined);
// assert(fourthRun.arr.value === 100);
// assert(fourthRun.arr.emailSentAt !== undefined);
}); });
it('Does not send emails for milestones when imported members present', async function () { it('Does not send emails for milestones when imported members present', async function () {
mockManager.mockSetting('stripe_publishable_key', 'pk_live_89843uihsidfh98832uo8ri');
mockManager.mockSetting('stripe_connect_publishable_key', 'pk_test_89843uihsidfh98832uo8ri');
await createFreeMembers(10, 1); await createFreeMembers(10, 1);
await createMemberWithSubscription('month', 1000, 'usd', '2023-01-10'); await createMemberWithSubscription('month', 1000, 'usd', '2023-01-10');
const result = await milestoneEmailsService.initAndRun(); const result = await milestoneEmailsService.initAndRun();
assert(result.members.value === 10); assert(result.members.value === 20);
assert(result.members.emailSentAt === null); assert(result.members.emailSentAt === null);
// assert(result.arr.value === 100); assert(result.arr.value === 150);
// assert(result.arr.emailSentAt === null); assert(result.arr.emailSentAt === null);
}); });
it('Does not run when milestoneEmails labs flag is not set', async function () { it('Does not run when milestoneEmails labs flag is not set', async function () {
@ -215,14 +224,14 @@ describe('Milestone Emails Service', function () {
assert(result === undefined); assert(result === undefined);
}); });
// it('Does not run ARR milestones when Stripe is not live enabled', async function () { it('Does not run ARR milestones when Stripe is not live enabled', async function () {
// stripeModeStub = sinon.stub().returns(false); mockManager.mockSetting('stripe_publishable_key', 'pk_test_89843uihsidfh98832uo8ri');
// milestoneEmailsService.__set__('getStripeLiveEnabled', stripeModeStub); mockManager.mockSetting('stripe_connect_publishable_key', 'pk_test_89843uihsidfh98832uo8ri');
// await createFreeMembers(10); await createFreeMembers(10);
// const result = await milestoneEmailsService.initAndRun(); const result = await milestoneEmailsService.initAndRun();
// assert(result.members.value === 10); assert(result.members.value === 30);
// assert(result.members.emailSentAt !== undefined); assert(result.members.emailSentAt !== undefined);
// assert(result.arr === undefined); assert(result.arr === undefined);
// }); });
}); });