2023-02-07 13:47:35 +03:00
|
|
|
const assert = require('assert');
|
|
|
|
const {
|
2023-02-15 10:23:31 +03:00
|
|
|
MilestonesService,
|
2023-02-07 13:47:35 +03:00
|
|
|
InMemoryMilestoneRepository
|
|
|
|
} = require('../index');
|
|
|
|
const Milestone = require('../lib/Milestone');
|
2023-02-15 13:06:13 +03:00
|
|
|
const DomainEvents = require('@tryghost/domain-events');
|
|
|
|
const sinon = require('sinon');
|
2023-02-07 13:47:35 +03:00
|
|
|
|
2023-02-15 10:23:31 +03:00
|
|
|
describe('MilestonesService', function () {
|
2023-02-07 13:47:35 +03:00
|
|
|
let repository;
|
2023-02-17 13:59:18 +03:00
|
|
|
let domainEventSpy;
|
2023-02-15 13:06:13 +03:00
|
|
|
|
|
|
|
beforeEach(async function () {
|
2023-02-17 13:59:18 +03:00
|
|
|
domainEventSpy = sinon.spy(DomainEvents, 'dispatch');
|
2023-02-15 13:06:13 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
afterEach(function () {
|
|
|
|
sinon.restore();
|
|
|
|
});
|
2023-02-07 13:47:35 +03:00
|
|
|
|
2023-02-13 17:29:01 +03:00
|
|
|
const milestonesConfig = {
|
|
|
|
arr: [
|
|
|
|
{
|
|
|
|
currency: 'usd',
|
2023-03-13 20:01:11 +03:00
|
|
|
values: [0, 1000, 10000, 50000, 100000, 250000, 500000, 1000000]
|
2023-02-13 17:29:01 +03:00
|
|
|
},
|
|
|
|
{
|
|
|
|
currency: 'gbp',
|
2023-03-13 20:01:11 +03:00
|
|
|
values: [0, 500, 1000, 5000, 100000, 250000, 500000, 1000000]
|
2023-02-13 17:29:01 +03:00
|
|
|
},
|
|
|
|
{
|
|
|
|
currency: 'idr',
|
2023-03-13 20:01:11 +03:00
|
|
|
values: [0, 1000, 10000, 50000, 100000, 250000, 500000, 1000000]
|
2023-02-13 17:29:01 +03:00
|
|
|
},
|
|
|
|
{
|
|
|
|
currency: 'eur',
|
2023-03-13 20:01:11 +03:00
|
|
|
values: [0, 1000, 10000, 50000, 100000, 250000, 500000, 1000000]
|
2023-02-13 17:29:01 +03:00
|
|
|
}
|
|
|
|
],
|
2023-03-13 20:01:11 +03:00
|
|
|
members: [0, 100, 1000, 10000, 50000, 100000, 250000, 500000, 1000000],
|
2023-02-23 17:01:13 +03:00
|
|
|
minDaysSinceImported: 7,
|
2023-03-13 20:01:11 +03:00
|
|
|
minDaysSinceLastEmail: 14
|
2023-02-07 13:47:35 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
describe('ARR Milestones', function () {
|
2023-03-13 20:01:11 +03:00
|
|
|
it('Adds initial 0 ARR milestone without sending email', async function () {
|
2023-02-15 13:06:13 +03:00
|
|
|
repository = new InMemoryMilestoneRepository({DomainEvents});
|
2023-02-07 13:47:35 +03:00
|
|
|
|
2023-02-15 10:23:31 +03:00
|
|
|
const milestoneEmailService = new MilestonesService({
|
2023-02-07 13:47:35 +03:00
|
|
|
repository,
|
2023-02-13 17:29:01 +03:00
|
|
|
milestonesConfig,
|
2023-02-07 13:47:35 +03:00
|
|
|
queries: {
|
|
|
|
async getARR() {
|
2023-03-13 20:01:11 +03:00
|
|
|
return [{currency: 'usd', arr: 43}];
|
2023-02-07 13:47:35 +03:00
|
|
|
},
|
|
|
|
async hasImportedMembersInPeriod() {
|
|
|
|
return false;
|
2023-02-13 17:29:01 +03:00
|
|
|
},
|
|
|
|
async getDefaultCurrency() {
|
|
|
|
return 'usd';
|
2023-02-07 13:47:35 +03:00
|
|
|
}
|
2023-02-13 17:29:01 +03:00
|
|
|
}
|
2023-02-07 13:47:35 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
const arrResult = await milestoneEmailService.checkMilestones('arr');
|
|
|
|
assert(arrResult.type === 'arr');
|
|
|
|
assert(arrResult.currency === 'usd');
|
2023-03-13 20:01:11 +03:00
|
|
|
assert(arrResult.value === 0);
|
|
|
|
assert(arrResult.emailSentAt === null);
|
|
|
|
assert(arrResult.name === 'arr-0-usd');
|
2023-02-17 13:59:18 +03:00
|
|
|
|
|
|
|
const domainEventSpyResult = domainEventSpy.getCall(0).args[0];
|
|
|
|
assert(domainEventSpy.calledOnce === true);
|
|
|
|
assert(domainEventSpyResult.data.milestone);
|
2023-03-13 20:01:11 +03:00
|
|
|
assert(domainEventSpyResult.data.meta.currentValue === 43);
|
|
|
|
assert(domainEventSpyResult.data.meta.reason === 'initial');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('Adds first ARR milestones but does not send email if no previous milestones', async function () {
|
|
|
|
repository = new InMemoryMilestoneRepository({DomainEvents});
|
|
|
|
|
|
|
|
const milestoneEmailService = new MilestonesService({
|
|
|
|
repository,
|
|
|
|
milestonesConfig,
|
|
|
|
queries: {
|
|
|
|
async getARR() {
|
|
|
|
return [{currency: 'usd', arr: 1298}, {currency: 'nzd', arr: 600}];
|
|
|
|
},
|
|
|
|
async hasImportedMembersInPeriod() {
|
|
|
|
return false;
|
|
|
|
},
|
|
|
|
async getDefaultCurrency() {
|
|
|
|
return 'usd';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
const arrResult = await milestoneEmailService.checkMilestones('arr');
|
|
|
|
assert(arrResult.type === 'arr');
|
|
|
|
assert(arrResult.currency === 'usd');
|
|
|
|
assert(arrResult.value === 1000);
|
|
|
|
assert(arrResult.emailSentAt === null);
|
|
|
|
assert(arrResult.name === 'arr-1000-usd');
|
|
|
|
|
|
|
|
assert(domainEventSpy.calledTwice === true);
|
|
|
|
const firstDomainEventSpyCall = domainEventSpy.getCall(0).args[0];
|
|
|
|
const secondDomainEventSpyCall = domainEventSpy.getCall(1).args[0];
|
|
|
|
assert(firstDomainEventSpyCall.data.milestone);
|
|
|
|
assert(firstDomainEventSpyCall.data.meta.currentValue === 1298);
|
|
|
|
assert(firstDomainEventSpyCall.data.meta.reason === 'initial');
|
|
|
|
assert(secondDomainEventSpyCall.data.milestone);
|
|
|
|
assert(secondDomainEventSpyCall.data.meta.currentValue === 1298);
|
|
|
|
assert(secondDomainEventSpyCall.data.meta.reason === 'initial');
|
2023-02-07 13:47:35 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
it('Adds next ARR milestone and sends email', async function () {
|
2023-02-15 13:06:13 +03:00
|
|
|
repository = new InMemoryMilestoneRepository({DomainEvents});
|
2023-02-07 13:47:35 +03:00
|
|
|
|
|
|
|
const milestoneOne = await Milestone.create({
|
|
|
|
type: 'arr',
|
2023-02-13 17:29:01 +03:00
|
|
|
value: 100,
|
2023-02-07 13:47:35 +03:00
|
|
|
createdAt: '2023-01-01T00:00:00Z',
|
|
|
|
emailSentAt: '2023-01-01T00:00:00Z'
|
|
|
|
});
|
|
|
|
|
|
|
|
const milestoneTwo = await Milestone.create({
|
|
|
|
type: 'arr',
|
|
|
|
value: 500,
|
|
|
|
createdAt: '2023-01-02T00:00:00Z',
|
|
|
|
emailSentAt: '2023-01-02T00:00:00Z'
|
|
|
|
});
|
|
|
|
|
|
|
|
const milestoneThree = await Milestone.create({
|
|
|
|
type: 'arr',
|
|
|
|
value: 1000,
|
2023-02-13 17:29:01 +03:00
|
|
|
currency: 'eur',
|
2023-02-07 13:47:35 +03:00
|
|
|
createdAt: '2023-01-15T00:00:00Z',
|
|
|
|
emailSentAt: '2023-01-15T00:00:00Z'
|
|
|
|
});
|
|
|
|
|
|
|
|
await repository.save(milestoneOne);
|
|
|
|
await repository.save(milestoneTwo);
|
|
|
|
await repository.save(milestoneThree);
|
|
|
|
|
2023-02-17 13:59:18 +03:00
|
|
|
assert(domainEventSpy.callCount === 3);
|
2023-02-15 13:06:13 +03:00
|
|
|
|
2023-02-15 10:23:31 +03:00
|
|
|
const milestoneEmailService = new MilestonesService({
|
2023-02-07 13:47:35 +03:00
|
|
|
repository,
|
2023-02-13 17:29:01 +03:00
|
|
|
milestonesConfig,
|
2023-02-07 13:47:35 +03:00
|
|
|
queries: {
|
|
|
|
async getARR() {
|
2023-02-13 17:29:01 +03:00
|
|
|
// Same ARR values for both supported currencies
|
|
|
|
return [{currency: 'usd', arr: 10001}, {currency: 'eur', arr: 10001}];
|
2023-02-07 13:47:35 +03:00
|
|
|
},
|
|
|
|
async hasImportedMembersInPeriod() {
|
|
|
|
return false;
|
2023-02-13 17:29:01 +03:00
|
|
|
},
|
|
|
|
async getDefaultCurrency() {
|
|
|
|
return 'usd';
|
2023-02-07 13:47:35 +03:00
|
|
|
}
|
2023-02-13 17:29:01 +03:00
|
|
|
}
|
2023-02-07 13:47:35 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
const arrResult = await milestoneEmailService.checkMilestones('arr');
|
|
|
|
assert(arrResult.type === 'arr');
|
|
|
|
assert(arrResult.currency === 'usd');
|
2023-02-13 17:29:01 +03:00
|
|
|
assert(arrResult.value === 10000);
|
2023-02-07 13:47:35 +03:00
|
|
|
assert(arrResult.emailSentAt !== null);
|
2023-02-13 17:29:01 +03:00
|
|
|
assert(arrResult.name === 'arr-10000-usd');
|
2023-03-13 20:01:11 +03:00
|
|
|
assert(domainEventSpy.callCount === 6); // we have just created three new milestones, but we only sent the email for the last one
|
|
|
|
const firstDomainEventSpyResult = domainEventSpy.getCall(3).args[0];
|
|
|
|
assert(firstDomainEventSpyResult.data.milestone);
|
|
|
|
assert(firstDomainEventSpyResult.data.meta.reason === 'skipped');
|
|
|
|
const secondDomainEventSpyResult = domainEventSpy.getCall(4).args[0];
|
|
|
|
assert(secondDomainEventSpyResult.data.milestone);
|
|
|
|
assert(secondDomainEventSpyResult.data.meta.reason === 'skipped');
|
|
|
|
const thirdDomainEventSpyResult = domainEventSpy.getCall(5).args[0];
|
|
|
|
assert(thirdDomainEventSpyResult.data.milestone);
|
|
|
|
assert(thirdDomainEventSpyResult.data.meta.currentValue === 10001);
|
|
|
|
assert(thirdDomainEventSpyResult.data.meta.reason === undefined);
|
2023-02-07 13:47:35 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
it('Does not add ARR milestone for out of scope currency', async function () {
|
2023-02-15 13:06:13 +03:00
|
|
|
repository = new InMemoryMilestoneRepository({DomainEvents});
|
2023-02-07 13:47:35 +03:00
|
|
|
|
2023-02-15 10:23:31 +03:00
|
|
|
const milestoneEmailService = new MilestonesService({
|
2023-02-07 13:47:35 +03:00
|
|
|
repository,
|
2023-02-13 17:29:01 +03:00
|
|
|
milestonesConfig,
|
2023-02-07 13:47:35 +03:00
|
|
|
queries: {
|
|
|
|
async getARR() {
|
|
|
|
return [{currency: 'nzd', arr: 1005}];
|
|
|
|
},
|
|
|
|
async hasImportedMembersInPeriod() {
|
|
|
|
return false;
|
2023-02-13 17:29:01 +03:00
|
|
|
},
|
|
|
|
async getDefaultCurrency() {
|
|
|
|
return 'nzd';
|
2023-02-07 13:47:35 +03:00
|
|
|
}
|
2023-02-13 17:29:01 +03:00
|
|
|
}
|
2023-02-07 13:47:35 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
const arrResult = await milestoneEmailService.checkMilestones('arr');
|
|
|
|
assert(arrResult === undefined);
|
2023-02-17 13:59:18 +03:00
|
|
|
assert(domainEventSpy.callCount === 0);
|
2023-02-07 13:47:35 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
it('Does not add new ARR milestone if already achieved', async function () {
|
2023-02-15 13:06:13 +03:00
|
|
|
repository = new InMemoryMilestoneRepository({DomainEvents});
|
2023-02-07 13:47:35 +03:00
|
|
|
|
|
|
|
const milestone = await Milestone.create({
|
|
|
|
type: 'arr',
|
|
|
|
value: 5000,
|
2023-03-13 20:01:11 +03:00
|
|
|
currency: 'gbp',
|
|
|
|
emailSentAt: '2023-01-01T00:00:00Z'
|
2023-02-07 13:47:35 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
await repository.save(milestone);
|
|
|
|
|
2023-02-17 13:59:18 +03:00
|
|
|
assert(domainEventSpy.callCount === 1);
|
2023-02-15 13:06:13 +03:00
|
|
|
|
2023-02-15 10:23:31 +03:00
|
|
|
const milestoneEmailService = new MilestonesService({
|
2023-02-07 13:47:35 +03:00
|
|
|
repository,
|
2023-02-13 17:29:01 +03:00
|
|
|
milestonesConfig,
|
2023-02-07 13:47:35 +03:00
|
|
|
queries: {
|
|
|
|
async getARR() {
|
2023-02-13 17:29:01 +03:00
|
|
|
return [{currency: 'gbp', arr: 5005}, {currency: 'usd', arr: 100}];
|
2023-02-07 13:47:35 +03:00
|
|
|
},
|
|
|
|
async hasImportedMembersInPeriod() {
|
|
|
|
return false;
|
2023-02-13 17:29:01 +03:00
|
|
|
},
|
|
|
|
async getDefaultCurrency() {
|
|
|
|
return 'gbp';
|
2023-02-07 13:47:35 +03:00
|
|
|
}
|
2023-02-13 17:29:01 +03:00
|
|
|
}
|
2023-02-07 13:47:35 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
const arrResult = await milestoneEmailService.checkMilestones('arr');
|
2023-03-13 20:01:11 +03:00
|
|
|
assert(arrResult.type === 'arr');
|
|
|
|
assert(arrResult.currency === 'gbp');
|
|
|
|
assert(arrResult.value === 5000);
|
|
|
|
assert(arrResult.name === 'arr-5000-gbp');
|
|
|
|
assert(domainEventSpy.callCount === 4);
|
|
|
|
// Filled up missing milestones, but only if they don't exist already
|
|
|
|
const firstDomainEventSpyResult = domainEventSpy.getCall(1).args[0];
|
|
|
|
assert(firstDomainEventSpyResult.data.milestone);
|
|
|
|
assert(firstDomainEventSpyResult.data.meta.reason === 'skipped');
|
|
|
|
const secondDomainEventSpyResult = domainEventSpy.getCall(2).args[0];
|
|
|
|
assert(secondDomainEventSpyResult.data.milestone);
|
|
|
|
assert(secondDomainEventSpyResult.data.meta.reason === 'skipped');
|
|
|
|
const thirdDomainEventSpyResult = domainEventSpy.getCall(3).args[0];
|
|
|
|
assert(thirdDomainEventSpyResult.data.milestone);
|
|
|
|
assert(thirdDomainEventSpyResult.data.meta.reason === 'skipped');
|
|
|
|
assert(thirdDomainEventSpyResult.data.meta.currentValue === 5005);
|
2023-02-07 13:47:35 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
it('Adds ARR milestone but does not send email if imported members are detected', async function () {
|
2023-02-15 13:06:13 +03:00
|
|
|
repository = new InMemoryMilestoneRepository({DomainEvents});
|
2023-02-07 13:47:35 +03:00
|
|
|
|
2023-03-13 20:01:11 +03:00
|
|
|
const milestone = await Milestone.create({
|
|
|
|
type: 'arr',
|
|
|
|
value: 0,
|
|
|
|
currency: 'usd',
|
|
|
|
emailSentAt: '2023-01-01T00:00:00Z'
|
|
|
|
});
|
|
|
|
|
|
|
|
await repository.save(milestone);
|
|
|
|
|
|
|
|
assert(domainEventSpy.callCount === 1);
|
|
|
|
|
2023-02-15 10:23:31 +03:00
|
|
|
const milestoneEmailService = new MilestonesService({
|
2023-02-07 13:47:35 +03:00
|
|
|
repository,
|
2023-02-13 17:29:01 +03:00
|
|
|
milestonesConfig,
|
2023-02-07 13:47:35 +03:00
|
|
|
queries: {
|
|
|
|
async getARR() {
|
|
|
|
return [{currency: 'usd', arr: 100000}, {currency: 'idr', arr: 2600}];
|
|
|
|
},
|
|
|
|
async hasImportedMembersInPeriod() {
|
|
|
|
return true;
|
2023-02-13 17:29:01 +03:00
|
|
|
},
|
|
|
|
async getDefaultCurrency() {
|
|
|
|
return 'usd';
|
2023-02-07 13:47:35 +03:00
|
|
|
}
|
2023-02-13 17:29:01 +03:00
|
|
|
}
|
2023-02-07 13:47:35 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
const arrResult = await milestoneEmailService.checkMilestones('arr');
|
|
|
|
assert(arrResult.type === 'arr');
|
|
|
|
assert(arrResult.currency === 'usd');
|
|
|
|
assert(arrResult.value === 100000);
|
|
|
|
assert(arrResult.emailSentAt === null);
|
2023-03-13 20:01:11 +03:00
|
|
|
assert(domainEventSpy.callCount === 5);
|
|
|
|
const secondDomainEventSpyResult = domainEventSpy.getCall(1).args[0];
|
|
|
|
assert(secondDomainEventSpyResult.data.meta.reason === 'skipped');
|
|
|
|
const lastDomainEventSpyResult = domainEventSpy.getCall(4).args[0];
|
|
|
|
assert(lastDomainEventSpyResult.data.meta.reason === 'import');
|
2023-02-07 13:47:35 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
it('Adds ARR milestone but does not send email if last email was too recent', async function () {
|
2023-02-15 13:06:13 +03:00
|
|
|
repository = new InMemoryMilestoneRepository({DomainEvents});
|
2023-02-07 13:47:35 +03:00
|
|
|
|
|
|
|
const lessThanTwoWeeksAgo = new Date();
|
|
|
|
lessThanTwoWeeksAgo.setDate(lessThanTwoWeeksAgo.getDate() - 12);
|
|
|
|
|
|
|
|
const milestone = await Milestone.create({
|
|
|
|
type: 'arr',
|
|
|
|
value: 1000,
|
|
|
|
currency: 'idr',
|
|
|
|
emailSentAt: lessThanTwoWeeksAgo
|
|
|
|
});
|
|
|
|
|
|
|
|
await repository.save(milestone);
|
2023-02-17 13:59:18 +03:00
|
|
|
assert(domainEventSpy.callCount === 1);
|
2023-02-07 13:47:35 +03:00
|
|
|
|
2023-02-15 10:23:31 +03:00
|
|
|
const milestoneEmailService = new MilestonesService({
|
2023-02-07 13:47:35 +03:00
|
|
|
repository,
|
2023-02-13 17:29:01 +03:00
|
|
|
milestonesConfig,
|
2023-02-07 13:47:35 +03:00
|
|
|
queries: {
|
|
|
|
async getARR() {
|
|
|
|
return [{currency: 'idr', arr: 10000}];
|
|
|
|
},
|
|
|
|
async hasImportedMembersInPeriod() {
|
2023-02-17 13:59:18 +03:00
|
|
|
return false;
|
2023-02-13 17:29:01 +03:00
|
|
|
},
|
|
|
|
async getDefaultCurrency() {
|
|
|
|
return 'idr';
|
2023-02-07 13:47:35 +03:00
|
|
|
}
|
2023-02-13 17:29:01 +03:00
|
|
|
}
|
2023-02-07 13:47:35 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
const arrResult = await milestoneEmailService.checkMilestones('arr');
|
|
|
|
assert(arrResult.type === 'arr');
|
|
|
|
assert(arrResult.currency === 'idr');
|
|
|
|
assert(arrResult.value === 10000);
|
|
|
|
assert(arrResult.emailSentAt === null);
|
2023-03-13 20:01:11 +03:00
|
|
|
assert(domainEventSpy.callCount === 3); // two new milestones created
|
|
|
|
const lastDomainEventSpyResult = domainEventSpy.getCall(2).args[0];
|
|
|
|
assert(lastDomainEventSpyResult.data.meta.reason === 'email');
|
2023-02-07 13:47:35 +03:00
|
|
|
});
|
2023-03-13 20:01:11 +03:00
|
|
|
});
|
2023-02-23 17:01:13 +03:00
|
|
|
|
2023-03-13 20:01:11 +03:00
|
|
|
describe('Members Milestones', function () {
|
|
|
|
it('Adds initial 0 Members milestone without sending email', async function () {
|
2023-02-23 17:01:13 +03:00
|
|
|
repository = new InMemoryMilestoneRepository({DomainEvents});
|
|
|
|
|
|
|
|
const milestoneEmailService = new MilestonesService({
|
|
|
|
repository,
|
|
|
|
milestonesConfig,
|
|
|
|
queries: {
|
|
|
|
async getMembersCount() {
|
2023-03-13 20:01:11 +03:00
|
|
|
return 6;
|
2023-02-23 17:01:13 +03:00
|
|
|
},
|
|
|
|
async hasImportedMembersInPeriod() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
const membersResult = await milestoneEmailService.checkMilestones('members');
|
|
|
|
assert(membersResult.type === 'members');
|
2023-03-13 20:01:11 +03:00
|
|
|
assert(membersResult.value === 0);
|
2023-02-23 17:01:13 +03:00
|
|
|
assert(membersResult.emailSentAt === null);
|
2023-03-13 20:01:11 +03:00
|
|
|
assert(membersResult.name === 'members-0');
|
|
|
|
|
2023-02-23 17:01:13 +03:00
|
|
|
const domainEventSpyResult = domainEventSpy.getCall(0).args[0];
|
2023-03-13 20:01:11 +03:00
|
|
|
assert(domainEventSpy.calledOnce === true);
|
|
|
|
assert(domainEventSpyResult.data.milestone);
|
|
|
|
assert(domainEventSpyResult.data.meta.currentValue === 6);
|
|
|
|
assert(domainEventSpyResult.data.meta.reason === 'initial');
|
2023-02-23 17:01:13 +03:00
|
|
|
});
|
2023-02-07 13:47:35 +03:00
|
|
|
|
2023-03-13 20:01:11 +03:00
|
|
|
it('Adds first Members milestone but does not send email if no previous milestones', async function () {
|
2023-02-15 13:06:13 +03:00
|
|
|
repository = new InMemoryMilestoneRepository({DomainEvents});
|
2023-02-07 13:47:35 +03:00
|
|
|
|
2023-02-15 10:23:31 +03:00
|
|
|
const milestoneEmailService = new MilestonesService({
|
2023-02-07 13:47:35 +03:00
|
|
|
repository,
|
2023-02-13 17:29:01 +03:00
|
|
|
milestonesConfig,
|
2023-02-07 13:47:35 +03:00
|
|
|
queries: {
|
|
|
|
async getMembersCount() {
|
|
|
|
return 110;
|
|
|
|
},
|
|
|
|
async hasImportedMembersInPeriod() {
|
|
|
|
return false;
|
2023-02-13 17:29:01 +03:00
|
|
|
},
|
|
|
|
async getDefaultCurrency() {
|
|
|
|
return 'usd';
|
2023-02-07 13:47:35 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
const membersResult = await milestoneEmailService.checkMilestones('members');
|
|
|
|
assert(membersResult.type === 'members');
|
|
|
|
assert(membersResult.value === 100);
|
2023-03-13 20:01:11 +03:00
|
|
|
assert(membersResult.emailSentAt === null);
|
|
|
|
assert(domainEventSpy.callCount === 2);
|
|
|
|
|
|
|
|
assert(domainEventSpy.calledTwice === true);
|
|
|
|
const firstDomainEventSpyCall = domainEventSpy.getCall(0).args[0];
|
|
|
|
const secondDomainEventSpyCall = domainEventSpy.getCall(1).args[0];
|
|
|
|
assert(firstDomainEventSpyCall.data.milestone);
|
|
|
|
assert(firstDomainEventSpyCall.data.meta.currentValue === 110);
|
|
|
|
assert(firstDomainEventSpyCall.data.meta.reason === 'initial');
|
|
|
|
assert(secondDomainEventSpyCall.data.milestone);
|
|
|
|
assert(secondDomainEventSpyCall.data.meta.currentValue === 110);
|
|
|
|
assert(secondDomainEventSpyCall.data.meta.reason === 'initial');
|
2023-02-07 13:47:35 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
it('Adds next Members milestone and sends email', async function () {
|
2023-02-15 13:06:13 +03:00
|
|
|
repository = new InMemoryMilestoneRepository({DomainEvents});
|
2023-02-07 13:47:35 +03:00
|
|
|
|
|
|
|
const milestoneOne = await Milestone.create({
|
|
|
|
type: 'members',
|
|
|
|
value: 1000,
|
|
|
|
createdAt: '2023-01-01T00:00:00Z',
|
|
|
|
emailSentAt: '2023-01-01T00:00:00Z'
|
|
|
|
});
|
|
|
|
|
|
|
|
const milestoneTwo = await Milestone.create({
|
|
|
|
type: 'members',
|
|
|
|
value: 500,
|
|
|
|
createdAt: '2023-01-02T00:00:00Z',
|
|
|
|
emailSentAt: '2023-01-02T00:00:00Z'
|
|
|
|
});
|
|
|
|
|
|
|
|
const milestoneThree = await Milestone.create({
|
|
|
|
type: 'members',
|
|
|
|
value: 1000,
|
|
|
|
createdAt: '2023-01-15T00:00:00Z',
|
|
|
|
emailSentAt: '2023-01-15T00:00:00Z'
|
|
|
|
});
|
|
|
|
|
|
|
|
await repository.save(milestoneOne);
|
|
|
|
await repository.save(milestoneTwo);
|
|
|
|
await repository.save(milestoneThree);
|
|
|
|
|
2023-02-17 13:59:18 +03:00
|
|
|
assert(domainEventSpy.callCount === 3);
|
2023-02-15 13:06:13 +03:00
|
|
|
|
2023-02-15 10:23:31 +03:00
|
|
|
const milestoneEmailService = new MilestonesService({
|
2023-02-07 13:47:35 +03:00
|
|
|
repository,
|
2023-02-13 17:29:01 +03:00
|
|
|
milestonesConfig,
|
2023-02-07 13:47:35 +03:00
|
|
|
queries: {
|
|
|
|
async getMembersCount() {
|
|
|
|
return 50005;
|
|
|
|
},
|
|
|
|
async hasImportedMembersInPeriod() {
|
|
|
|
return false;
|
2023-02-13 17:29:01 +03:00
|
|
|
},
|
|
|
|
async getDefaultCurrency() {
|
|
|
|
return 'usd';
|
2023-02-07 13:47:35 +03:00
|
|
|
}
|
2023-02-13 17:29:01 +03:00
|
|
|
}
|
2023-02-07 13:47:35 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
const membersResult = await milestoneEmailService.checkMilestones('members');
|
|
|
|
assert(membersResult.type === 'members');
|
|
|
|
assert(membersResult.currency === null);
|
|
|
|
assert(membersResult.value === 50000);
|
|
|
|
assert(membersResult.emailSentAt !== null);
|
|
|
|
assert(membersResult.name === 'members-50000');
|
2023-03-13 20:01:11 +03:00
|
|
|
|
|
|
|
assert(domainEventSpy.callCount === 7); // we have just created three new milestones, but we only sent the email for the last one
|
|
|
|
const firstDomainEventSpyResult = domainEventSpy.getCall(3).args[0];
|
|
|
|
assert(firstDomainEventSpyResult.data.milestone);
|
|
|
|
assert(firstDomainEventSpyResult.data.meta.reason === 'skipped');
|
|
|
|
const secondDomainEventSpyResult = domainEventSpy.getCall(4).args[0];
|
|
|
|
assert(secondDomainEventSpyResult.data.milestone);
|
|
|
|
assert(secondDomainEventSpyResult.data.meta.reason === 'skipped');
|
|
|
|
const thirdDomainEventSpyResult = domainEventSpy.getCall(5).args[0];
|
|
|
|
assert(thirdDomainEventSpyResult.data.milestone);
|
|
|
|
assert(thirdDomainEventSpyResult.data.meta.reason === 'skipped');
|
|
|
|
const fourthDomainEventSpyResult = domainEventSpy.getCall(6).args[0];
|
|
|
|
assert(fourthDomainEventSpyResult.data.milestone);
|
|
|
|
assert(fourthDomainEventSpyResult.data.meta.currentValue === 50005);
|
|
|
|
assert(fourthDomainEventSpyResult.data.meta.reason === undefined);
|
2023-02-07 13:47:35 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
it('Does not add new Members milestone if already achieved', async function () {
|
2023-02-15 13:06:13 +03:00
|
|
|
repository = new InMemoryMilestoneRepository({DomainEvents});
|
2023-02-07 13:47:35 +03:00
|
|
|
|
|
|
|
const milestone = await Milestone.create({
|
|
|
|
type: 'members',
|
|
|
|
value: 50000
|
|
|
|
});
|
|
|
|
|
|
|
|
await repository.save(milestone);
|
|
|
|
|
2023-02-17 13:59:18 +03:00
|
|
|
assert(domainEventSpy.callCount === 1);
|
2023-02-15 13:06:13 +03:00
|
|
|
|
2023-02-15 10:23:31 +03:00
|
|
|
const milestoneEmailService = new MilestonesService({
|
2023-02-07 13:47:35 +03:00
|
|
|
repository,
|
2023-02-13 17:29:01 +03:00
|
|
|
milestonesConfig,
|
2023-02-07 13:47:35 +03:00
|
|
|
queries: {
|
|
|
|
async getMembersCount() {
|
|
|
|
return 50555;
|
|
|
|
},
|
|
|
|
async hasImportedMembersInPeriod() {
|
|
|
|
return false;
|
2023-02-13 17:29:01 +03:00
|
|
|
},
|
|
|
|
async getDefaultCurrency() {
|
|
|
|
return 'usd';
|
2023-02-07 13:47:35 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
const membersResult = await milestoneEmailService.checkMilestones('members');
|
2023-03-13 20:01:11 +03:00
|
|
|
assert(membersResult.type === 'members');
|
|
|
|
assert(membersResult.value === 50000);
|
|
|
|
assert(membersResult.name === 'members-50000');
|
|
|
|
assert(domainEventSpy.callCount === 5);
|
|
|
|
// Filled up missing milestones, but only if they don't exist already
|
|
|
|
const firstDomainEventSpyResult = domainEventSpy.getCall(1).args[0];
|
|
|
|
assert(firstDomainEventSpyResult.data.milestone);
|
|
|
|
assert(firstDomainEventSpyResult.data.meta.reason === 'skipped');
|
|
|
|
const secondDomainEventSpyResult = domainEventSpy.getCall(2).args[0];
|
|
|
|
assert(secondDomainEventSpyResult.data.milestone);
|
|
|
|
assert(secondDomainEventSpyResult.data.meta.reason === 'skipped');
|
|
|
|
const thirdDomainEventSpyResult = domainEventSpy.getCall(3).args[0];
|
|
|
|
assert(thirdDomainEventSpyResult.data.milestone);
|
|
|
|
assert(thirdDomainEventSpyResult.data.meta.reason === 'skipped');
|
|
|
|
assert(thirdDomainEventSpyResult.data.meta.currentValue === 50555);
|
|
|
|
assert(thirdDomainEventSpyResult.data.meta.reason === 'skipped');
|
2023-02-07 13:47:35 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
it('Adds Members milestone but does not send email if imported members are detected', async function () {
|
2023-02-15 13:06:13 +03:00
|
|
|
repository = new InMemoryMilestoneRepository({DomainEvents});
|
2023-02-07 13:47:35 +03:00
|
|
|
|
|
|
|
const milestone = await Milestone.create({
|
|
|
|
type: 'members',
|
|
|
|
value: 100
|
|
|
|
});
|
|
|
|
|
|
|
|
await repository.save(milestone);
|
|
|
|
|
2023-02-17 13:59:18 +03:00
|
|
|
assert(domainEventSpy.callCount === 1);
|
2023-02-15 13:06:13 +03:00
|
|
|
|
2023-02-15 10:23:31 +03:00
|
|
|
const milestoneEmailService = new MilestonesService({
|
2023-02-07 13:47:35 +03:00
|
|
|
repository,
|
2023-02-13 17:29:01 +03:00
|
|
|
milestonesConfig,
|
2023-02-07 13:47:35 +03:00
|
|
|
queries: {
|
|
|
|
async getMembersCount() {
|
|
|
|
return 1001;
|
|
|
|
},
|
|
|
|
async hasImportedMembersInPeriod() {
|
|
|
|
return true;
|
2023-02-13 17:29:01 +03:00
|
|
|
},
|
|
|
|
async getDefaultCurrency() {
|
|
|
|
return 'usd';
|
2023-02-07 13:47:35 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
const membersResult = await milestoneEmailService.checkMilestones('members');
|
|
|
|
assert(membersResult.type === 'members');
|
|
|
|
assert(membersResult.value === 1000);
|
|
|
|
assert(membersResult.emailSentAt === null);
|
2023-03-13 20:01:11 +03:00
|
|
|
assert(domainEventSpy.callCount === 3);
|
|
|
|
const lastDomainEventSpyResult = domainEventSpy.getCall(2).args[0];
|
|
|
|
assert(lastDomainEventSpyResult.data.meta.reason === 'import');
|
2023-02-07 13:47:35 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
it('Adds Members milestone but does not send email if last email was too recent', async function () {
|
2023-02-15 13:06:13 +03:00
|
|
|
repository = new InMemoryMilestoneRepository({DomainEvents});
|
2023-02-07 13:47:35 +03:00
|
|
|
|
|
|
|
const lessThanTwoWeeksAgo = new Date();
|
|
|
|
lessThanTwoWeeksAgo.setDate(lessThanTwoWeeksAgo.getDate() - 8);
|
|
|
|
|
|
|
|
const milestone = await Milestone.create({
|
|
|
|
type: 'members',
|
|
|
|
value: 100,
|
|
|
|
emailSentAt: lessThanTwoWeeksAgo
|
|
|
|
});
|
|
|
|
|
|
|
|
await repository.save(milestone);
|
|
|
|
|
2023-02-17 13:59:18 +03:00
|
|
|
assert(domainEventSpy.callCount === 1);
|
2023-02-15 13:06:13 +03:00
|
|
|
|
2023-02-15 10:23:31 +03:00
|
|
|
const milestoneEmailService = new MilestonesService({
|
2023-02-07 13:47:35 +03:00
|
|
|
repository,
|
2023-02-13 17:29:01 +03:00
|
|
|
milestonesConfig,
|
2023-02-07 13:47:35 +03:00
|
|
|
queries: {
|
|
|
|
async getMembersCount() {
|
|
|
|
return 50010;
|
|
|
|
},
|
|
|
|
async hasImportedMembersInPeriod() {
|
|
|
|
return false;
|
2023-02-13 17:29:01 +03:00
|
|
|
},
|
|
|
|
async getDefaultCurrency() {
|
|
|
|
return 'usd';
|
2023-02-07 13:47:35 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
const membersResult = await milestoneEmailService.checkMilestones('members');
|
|
|
|
assert(membersResult.type === 'members');
|
|
|
|
assert(membersResult.value === 50000);
|
|
|
|
assert(membersResult.emailSentAt === null);
|
2023-03-13 20:01:11 +03:00
|
|
|
assert(domainEventSpy.callCount === 5);
|
|
|
|
const lastDomainEventSpyResult = domainEventSpy.getCall(4).args[0];
|
|
|
|
assert(lastDomainEventSpyResult.data.meta.reason === 'email');
|
2023-02-07 13:47:35 +03:00
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|