mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-29 22:01:49 +03:00
13b0f51b13
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.
311 lines
11 KiB
JavaScript
311 lines
11 KiB
JavaScript
const {agentProvider, fixtureManager, mockManager, configUtils} = require('../../utils/e2e-framework');
|
|
const assert = require('assert');
|
|
const nock = require('nock');
|
|
const sinon = require('sinon');
|
|
const models = require('../../../core/server/models');
|
|
const moment = require('moment');
|
|
const logging = require('@tryghost/logging');
|
|
|
|
const milestonesService = require('../../../core/server/services/milestones');
|
|
|
|
let agent;
|
|
let counter = 0;
|
|
let membersCounter = 0;
|
|
const fifteenDays = 1000 * 60 * 60 * 24 * 15;
|
|
|
|
async function createMemberWithSubscription(interval, amount, currency, date) {
|
|
counter += 1;
|
|
membersCounter += 1;
|
|
|
|
const fakePrice = {
|
|
id: 'price_' + counter,
|
|
product: '',
|
|
active: true,
|
|
nickname: 'Paid',
|
|
unit_amount: amount,
|
|
currency,
|
|
type: 'recurring',
|
|
recurring: {
|
|
interval
|
|
}
|
|
};
|
|
|
|
const fakeSubscription = {
|
|
id: 'sub_' + counter,
|
|
customer: 'cus_' + counter,
|
|
status: 'active',
|
|
cancel_at_period_end: false,
|
|
metadata: {},
|
|
current_period_end: Math.floor(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, body) => {
|
|
const [match, resource, id] = uri.match(/\/?v1\/(\w+)\/?(\w+)/) || [null];
|
|
|
|
if (!match) {
|
|
return [500];
|
|
}
|
|
|
|
if (resource === 'customers') {
|
|
return [200, fakeCustomer];
|
|
}
|
|
|
|
if (resource === 'subscriptions') {
|
|
return [200, fakeSubscription];
|
|
}
|
|
});
|
|
|
|
const initialMember = {
|
|
name: fakeCustomer.name,
|
|
email: fakeCustomer.email,
|
|
subscribed: true,
|
|
stripe_customer_id: fakeCustomer.id
|
|
};
|
|
|
|
await agent
|
|
.post(`/members/`)
|
|
.body({members: [initialMember]})
|
|
.expectStatus(201);
|
|
|
|
nock.cleanAll();
|
|
}
|
|
|
|
async function createFreeMembers(amount, amountImported = 0) {
|
|
const members = [];
|
|
|
|
const newsletters = await agent.get(`/newsletters/`);
|
|
const newsletter = newsletters.body?.newsletters?.[0];
|
|
|
|
for (let index = 0; index < amount; index++) {
|
|
let membersAddRequest;
|
|
membersCounter += 1;
|
|
|
|
const member = {
|
|
name: 'Test Member',
|
|
email: 'free-member-' + membersCounter + '@email.com',
|
|
status: 'free',
|
|
uuid: `f6f91461-d7d8-4a3f-aa5d-8e582c40b99${membersCounter}`
|
|
};
|
|
|
|
if (amountImported > 0) {
|
|
member.subscribed = true;
|
|
member.newsletters = [newsletter];
|
|
|
|
const createMemberEvent = await agent
|
|
.post(`/members/`)
|
|
.body({members: [member]})
|
|
.expectStatus(201);
|
|
|
|
const id = createMemberEvent.body.members[0].id;
|
|
|
|
// Manually add the members_subscribe_event so we can test imported members
|
|
const editedEvent = await models.MemberSubscribeEvent.add({
|
|
newsletter_id: newsletter.id,
|
|
member_id: id,
|
|
subscribed: true,
|
|
source: index < amountImported ? 'import' : 'member'
|
|
});
|
|
|
|
membersAddRequest = Promise.all([createMemberEvent, editedEvent]);
|
|
} else {
|
|
membersAddRequest = await agent
|
|
.post(`/members/`)
|
|
.body({members: [member]})
|
|
.expectStatus(201);
|
|
}
|
|
|
|
members.push(membersAddRequest);
|
|
}
|
|
|
|
await Promise.all(members);
|
|
}
|
|
|
|
describe('Milestones Service', function () {
|
|
let clock;
|
|
let loggingStub;
|
|
|
|
const milestonesConfig = {
|
|
arr: [{currency: 'usd', values: [100, 150]}],
|
|
members: [10, 20, 30],
|
|
minDaysSinceImported: 7,
|
|
minDaysSinceLastEmail: 14,
|
|
maxPercentageFromMilestone: 0.35
|
|
};
|
|
|
|
before(async function () {
|
|
agent = await agentProvider.getAdminAPIAgent();
|
|
await fixtureManager.init('newsletters');
|
|
await agent.loginAsOwner();
|
|
});
|
|
|
|
beforeEach(async function () {
|
|
loggingStub = sinon.stub(logging, 'info');
|
|
const threeMonthsAgo = new Date();
|
|
threeMonthsAgo.setMonth(threeMonthsAgo.getMonth() - 3);
|
|
clock = sinon.useFakeTimers({
|
|
now: threeMonthsAgo.getTime(),
|
|
toFake: ['setTimeout']
|
|
});
|
|
sinon.createSandbox();
|
|
configUtils.set('milestones', milestonesConfig);
|
|
mockManager.mockLabsEnabled('milestoneEmails');
|
|
mockManager.mockMail();
|
|
});
|
|
|
|
afterEach(async function () {
|
|
await configUtils.restore();
|
|
mockManager.restore();
|
|
clock.restore();
|
|
clock = undefined;
|
|
sinon.restore();
|
|
});
|
|
|
|
it('Inits milestone service', async function () {
|
|
await milestonesService.init();
|
|
|
|
assert.ok(milestonesService.api);
|
|
});
|
|
|
|
it('Runs ARR and Members milestone jobs', async function () {
|
|
mockManager.mockSetting('stripe_connect_publishable_key', 'pk_live_89843uihsidfh98832uo8ri');
|
|
|
|
// No ARR and no members
|
|
const firstResultPromise = milestonesService.initAndRun(fifteenDays);
|
|
await clock.tickAsync(fifteenDays);
|
|
const firstRun = await firstResultPromise;
|
|
assert(firstRun.members === undefined);
|
|
assert(firstRun.arr === undefined);
|
|
|
|
await createFreeMembers(7);
|
|
await createMemberWithSubscription('year', 5000, 'usd', '2000-01-10');
|
|
await createMemberWithSubscription('month', 100, 'usd', '2000-01-10');
|
|
const secondResultPromise = milestonesService.initAndRun(fifteenDays);
|
|
await clock.tickAsync(fifteenDays);
|
|
const secondRun = await secondResultPromise;
|
|
assert(secondRun.members === undefined);
|
|
assert(secondRun.arr === undefined);
|
|
|
|
// Reached the first milestone for members
|
|
await createFreeMembers(1);
|
|
const thirdResultPromise = milestonesService.initAndRun(fifteenDays);
|
|
await clock.tickAsync(fifteenDays);
|
|
const thirdRun = await thirdResultPromise;
|
|
assert(thirdRun.members.value === 10);
|
|
assert(thirdRun.members.emailSentAt !== undefined);
|
|
assert(thirdRun.arr === undefined);
|
|
|
|
const memberMilestoneModel = await models.Milestone.findOne({value: 10, type: 'members'});
|
|
|
|
assert.ok(memberMilestoneModel.get('id'));
|
|
assert.equal(memberMilestoneModel.get('type'), 'members');
|
|
assert.equal(memberMilestoneModel.get('value'), 10);
|
|
assert.ok(memberMilestoneModel.get('created_at'));
|
|
assert.ok(memberMilestoneModel.get('email_sent_at'));
|
|
|
|
// Reached the first milestone for ARR
|
|
// - Members already reached, no new one will be created
|
|
// - Less than two weeks since last milestone email, no email will be sent
|
|
await createMemberWithSubscription('month', 500, 'usd', '2000-01-10');
|
|
await createMemberWithSubscription('month', 500, 'eur', '2000-01-10');
|
|
await createMemberWithSubscription('year', 1000, 'usd', '2000-01-10');
|
|
const fourthResultPromise = milestonesService.initAndRun(100);
|
|
await clock.tickAsync(100);
|
|
const fourthRun = await fourthResultPromise;
|
|
assert(fourthRun.members === undefined);
|
|
assert(fourthRun.arr.value === 100);
|
|
assert(fourthRun.arr.emailSentAt !== undefined);
|
|
|
|
const arrMilestoneModel = await models.Milestone.findOne({value: 100, type: 'arr'});
|
|
|
|
assert.ok(arrMilestoneModel.get('id'));
|
|
assert.equal(arrMilestoneModel.get('type'), 'arr');
|
|
assert.equal(arrMilestoneModel.get('value'), 100);
|
|
assert.ok(arrMilestoneModel.get('created_at'));
|
|
assert.equal(arrMilestoneModel.get('email_sent_at'), null);
|
|
|
|
assert(loggingStub.called);
|
|
});
|
|
|
|
it('Does not run ARR milestones when Stripe is not live enabled', async function () {
|
|
mockManager.mockSetting('stripe_publishable_key', 'pk_test_89843uihsidfh98832uo8ri');
|
|
mockManager.mockSetting('stripe_connect_publishable_key', 'pk_test_89843uihsidfh98832uo8ri');
|
|
await createFreeMembers(12);
|
|
|
|
const resultPromise = milestonesService.initAndRun(fifteenDays);
|
|
await clock.tickAsync(fifteenDays);
|
|
const result = await resultPromise;
|
|
assert(result.members.value === 20);
|
|
assert(result.members.emailSentAt !== undefined);
|
|
assert(result.arr === undefined);
|
|
|
|
const memberMilestoneModel = await models.Milestone.findOne({value: 20, type: 'members'});
|
|
|
|
assert.ok(memberMilestoneModel.get('id'));
|
|
assert.equal(memberMilestoneModel.get('type'), 'members');
|
|
assert.equal(memberMilestoneModel.get('value'), 20);
|
|
assert.ok(memberMilestoneModel.get('created_at'));
|
|
assert.equal(memberMilestoneModel.get('email_sent_at'), null);
|
|
|
|
assert(loggingStub.called);
|
|
});
|
|
|
|
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);
|
|
const resultPromise = milestonesService.initAndRun(fifteenDays);
|
|
|
|
await clock.tickAsync(fifteenDays);
|
|
const result = await resultPromise;
|
|
|
|
assert(result.members.value === 30);
|
|
assert(result.members.emailSentAt === null);
|
|
assert(result.arr === undefined);
|
|
|
|
const memberMilestoneModel = await models.Milestone.findOne({value: 30, type: 'members'});
|
|
|
|
assert.ok(memberMilestoneModel.get('id'));
|
|
assert.equal(memberMilestoneModel.get('type'), 'members');
|
|
assert.equal(memberMilestoneModel.get('value'), 30);
|
|
assert.ok(memberMilestoneModel.get('created_at'));
|
|
assert.equal(memberMilestoneModel.get('email_sent_at'), null);
|
|
|
|
const arrMilestoneModel = await models.Milestone.findOne({value: 150, type: 'arr'});
|
|
|
|
assert.equal(arrMilestoneModel, null);
|
|
|
|
assert(loggingStub.called);
|
|
});
|
|
|
|
it('Does not run when milestoneEmails labs flag is not set', async function () {
|
|
mockManager.mockLabsDisabled('milestoneEmails');
|
|
|
|
const resultPromise = milestonesService.initAndRun(fifteenDays);
|
|
await clock.tickAsync(fifteenDays);
|
|
const result = await resultPromise;
|
|
assert(result === undefined);
|
|
|
|
assert(loggingStub.called);
|
|
});
|
|
});
|