diff --git a/ghost/core/test/e2e-server/services/stats/mrr-stats-service.test.js b/ghost/core/test/e2e-server/services/stats/mrr-stats-service.test.js index 59b45d1af2..67b179cbeb 100644 --- a/ghost/core/test/e2e-server/services/stats/mrr-stats-service.test.js +++ b/ghost/core/test/e2e-server/services/stats/mrr-stats-service.test.js @@ -1,71 +1,26 @@ const statsService = require('../../../../core/server/services/stats'); const {agentProvider, fixtureManager, mockManager} = require('../../../utils/e2e-framework'); -const moment = require('moment'); require('should'); -const nock = require('nock'); +const {stripeMocker} = require('../../../utils/e2e-framework-mock-manager'); +const moment = require('moment'); let agent; -let counter = 0; async function createMemberWithSubscription(interval, amount, currency, date) { - counter += 1; - - const fakePrice = { - id: 'price_' + counter, - product: '', - active: true, - nickname: 'Paid', - unit_amount: amount, + const tier = await stripeMocker.createTier({ currency, - type: 'recurring', - recurring: { - interval - } - }; - - const fakeSubscription = { - id: 'sub_' + counter, - customer: 'cus_' + counter, - status: 'active', - cancel_at_period_end: false, - metadata: {}, - current_period_end: Date.now() / 1000 + 1000, - start_date: moment(date).unix(), - plan: fakePrice, - items: { - data: [{ - price: fakePrice - }] - } - }; - - const fakeCustomer = { - id: 'cus_' + counter, - name: 'Test Member', - email: 'create-member-subscription-' + counter + '@email.com', - subscriptions: { - type: 'list', - data: [fakeSubscription] - } - }; - nock('https://api.stripe.com') - .persist() - .get(/v1\/.*/) - .reply((uri) => { - const [match, resource] = uri.match(/\/?v1\/(\w+)\/?(\w+)/) || [null]; - - if (!match) { - return [500]; - } - - if (resource === 'customers') { - return [200, fakeCustomer]; - } - - if (resource === 'subscriptions') { - return [200, fakeSubscription]; - } - }); + monthly_price: amount, + yearly_price: amount + }); + const price = await stripeMocker.getPriceForTier(tier.get('slug'), interval); + const fakeCustomer = stripeMocker.createCustomer({}); + await stripeMocker.createSubscription({ + customer: fakeCustomer, + price, + start_date: moment(date).unix() + }, { + sendWebhook: false + }); const initialMember = { name: fakeCustomer.name, @@ -78,8 +33,6 @@ async function createMemberWithSubscription(interval, amount, currency, date) { .post(`/members/`) .body({members: [initialMember]}) .expectStatus(201); - - nock.cleanAll(); } describe('MRR Stats Service', function () { @@ -87,15 +40,15 @@ describe('MRR Stats Service', function () { agent = await agentProvider.getAdminAPIAgent(); await fixtureManager.init(); await agent.loginAsOwner(); - mockManager.mockMail(); }); - after(async function () { - mockManager.restore(); + beforeEach(function () { + mockManager.mockMail(); + mockManager.mockStripe(); }); afterEach(function () { - nock.cleanAll(); + mockManager.restore(); }); describe('getCurrentMrr', function () { @@ -103,7 +56,7 @@ describe('MRR Stats Service', function () { const result = await statsService.mrr.getCurrentMrr(); result.should.eql([ { - currency: 'usd', + currency: 'usd', // need to check capital usage here! mrr: 0 } ]); @@ -114,7 +67,7 @@ describe('MRR Stats Service', function () { const result = await statsService.mrr.getCurrentMrr(); result.should.eql([ { - currency: 'eur', + currency: 'EUR', mrr: 500 } ]); @@ -125,11 +78,11 @@ describe('MRR Stats Service', function () { const result = await statsService.mrr.getCurrentMrr(); result.should.eql([ { - currency: 'eur', + currency: 'EUR', mrr: 500 }, { - currency: 'usd', + currency: 'USD', mrr: 1 } ]); @@ -140,11 +93,11 @@ describe('MRR Stats Service', function () { const result = await statsService.mrr.getCurrentMrr(); result.should.eql([ { - currency: 'eur', + currency: 'EUR', mrr: 500 }, { - currency: 'usd', + currency: 'USD', mrr: 2 } ]); @@ -155,11 +108,11 @@ describe('MRR Stats Service', function () { let result = await statsService.mrr.getCurrentMrr(); result.should.eql([ { - currency: 'eur', + currency: 'EUR', mrr: 500 }, { - currency: 'usd', + currency: 'USD', mrr: 3 } ]); @@ -169,11 +122,11 @@ describe('MRR Stats Service', function () { result = await statsService.mrr.getCurrentMrr(); result.should.eql([ { - currency: 'eur', + currency: 'EUR', mrr: 500 }, { - currency: 'usd', + currency: 'USD', mrr: 3 } ]); @@ -183,11 +136,11 @@ describe('MRR Stats Service', function () { result = await statsService.mrr.getCurrentMrr(); result.should.eql([ { - currency: 'eur', + currency: 'EUR', mrr: 500 }, { - currency: 'usd', + currency: 'USD', mrr: 3 } ]); @@ -197,11 +150,11 @@ describe('MRR Stats Service', function () { result = await statsService.mrr.getCurrentMrr(); result.should.eql([ { - currency: 'eur', + currency: 'EUR', mrr: 500 }, { - currency: 'usd', + currency: 'USD', mrr: 4 } ]); @@ -216,22 +169,22 @@ describe('MRR Stats Service', function () { { date: '2000-01-10', delta: 500, - currency: 'eur' + currency: 'EUR' }, { date: '2000-01-10', delta: 1, - currency: 'usd' + currency: 'USD' }, { date: '2000-01-11', delta: 1, - currency: 'usd' + currency: 'USD' }, { date: '2000-01-12', delta: 2, - currency: 'usd' + currency: 'USD' } ]); }); diff --git a/ghost/core/test/utils/overrides.js b/ghost/core/test/utils/overrides.js index cfbc6f9220..50a29c7798 100644 --- a/ghost/core/test/utils/overrides.js +++ b/ghost/core/test/utils/overrides.js @@ -17,3 +17,21 @@ mochaHooks.beforeAll = async function () { // Disable network in tests to prevent any accidental requests mockManager.disableNetwork(); }; + +const originalAfterEach = mochaHooks.afterEach; +mochaHooks.afterEach = async function () { + const domainEvents = require('@tryghost/domain-events'); + const mentionsJobsService = require('../../core/server/services/mentions-jobs'); + const jobsService = require('../../core/server/services/jobs'); + + await domainEvents.allSettled(); + await mentionsJobsService.allSettled(); + await jobsService.allSettled(); + + // Last time for events emitted during jobs + await domainEvents.allSettled(); + + if (originalAfterEach) { + await originalAfterEach(); + } +}; diff --git a/ghost/core/test/utils/stripe-mocker.js b/ghost/core/test/utils/stripe-mocker.js index a7d2e4fb8b..7f0acc4ee2 100644 --- a/ghost/core/test/utils/stripe-mocker.js +++ b/ghost/core/test/utils/stripe-mocker.js @@ -19,6 +19,8 @@ class StripeMocker { prices = []; products = []; + nockInterceptors = []; + constructor(data = {}) { this.customers = data.customers ?? []; this.subscriptions = data.subscriptions ?? []; @@ -80,6 +82,25 @@ class StripeMocker { return this.#getData(this.prices, id)[1]; } + /** + * + * @param {object} data + * @param {string} [data.name] + * @param {string} data.currency + * @param {number} data.monthly_price + * @param {number} data.yearly_price + * @returns + */ + async createTier({name, currency, monthly_price, yearly_price}) { + return await models.Product.add({ + name: name ?? ('Tier ' + this.#generateRandomId()), + type: 'paid', + currency: currency.toUpperCase(), + monthly_price, + yearly_price + }); + } + async createTrialSubscription({customer, price, ...overrides}) { return await this.createSubscription({ customer, @@ -113,7 +134,7 @@ class StripeMocker { await DomainEvents.allSettled(); } - async createSubscription({customer, price, ...overrides}) { + async createSubscription({customer, price, ...overrides}, options = {sendWebhook: true}) { const subscriptionId = `sub_${this.#generateRandomId()}`; const subscription = { @@ -140,18 +161,20 @@ class StripeMocker { customer.subscriptions.data.push(subscription); // Announce - await this.sendWebhook({ - type: 'checkout.session.completed', - data: { - object: { - mode: 'subscription', - customer: customer.id, - metadata: { - checkoutType: 'signup' + if (options.sendWebhook) { + await this.sendWebhook({ + type: 'checkout.session.completed', + data: { + object: { + mode: 'subscription', + customer: customer.id, + metadata: { + checkoutType: 'signup' + } } } - } - }); + }); + } return subscription; } @@ -266,10 +289,21 @@ class StripeMocker { return [200, subscription]; } + remove() { + for (const interceptor of this.nockInterceptors) { + nock.removeInterceptor(interceptor); + } + this.nockInterceptors = []; + } + stub() { - nock('https://api.stripe.com') + this.remove(); + + let interceptor = nock('https://api.stripe.com') .persist() - .get(/v1\/.*/) + .get(/v1\/.*/); + this.nockInterceptors.push(interceptor); + interceptor .reply((uri) => { const [match, resource, id] = uri.match(/\/?v1\/(\w+)\/?(\w+)/) || [null]; @@ -308,9 +342,11 @@ class StripeMocker { return [500]; }); - nock('https://api.stripe.com') + interceptor = nock('https://api.stripe.com') .persist() - .post(/v1\/.*/) + .post(/v1\/.*/); + this.nockInterceptors.push(interceptor); + interceptor .reply((uri, body) => { const [match, resource, id] = uri.match(/\/?v1\/(\w+)(?:\/?(\w+)){0,2}/) || [null]; @@ -345,9 +381,11 @@ class StripeMocker { return [500]; }); - nock('https://api.stripe.com') + interceptor = nock('https://api.stripe.com') .persist() - .delete(/v1\/.*/) + .delete(/v1\/.*/); + this.nockInterceptors.push(interceptor); + interceptor .reply((uri) => { const [match, resource, id] = uri.match(/\/?v1\/(\w+)(?:\/?(\w+)){0,2}/) || [null];