Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/services/tiers.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,12 +196,23 @@ export class TiersService {
await this.usersService.disableVPNTier(userUuid, featureId);
}

/**
* Applies the mail configuration of the given tier to the user.
*
* The tier is the source of truth for the mail entitlement, so this both reactivates the account
* for a tier that includes mail and suspends it for one that does not. Keeping it symmetric is
* what covers a plan switch: `removeTier` only runs when a plan is cancelled or refunded, never
* when the price of a live subscription changes.
*/
async applyMailFeatures(userWithEmail: { email: string; uuid: User['uuid'] }, tier: Tier): Promise<void> {
const { enabled } = tier.featuresPerService[Service.Mail];

if (enabled) {
await this.mailService.reactivateAccount(userWithEmail.uuid);
return;
}

await this.removeMailFeatures(userWithEmail.uuid);
}

async removeMailFeatures(userUuid: User['uuid']): Promise<void> {
Expand Down
18 changes: 17 additions & 1 deletion tests/src/services/tiers.service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -392,17 +392,33 @@ describe('TiersService tests', () => {
expect(reactivateAccountSpy).toHaveBeenCalledWith(userWithEmail.uuid);
});

test('When Mail is disabled, then it does not send a request to reactivate the mail account', async () => {
test('When Mail is disabled, then the mail account is suspended instead of reactivated', async () => {
const userWithEmail = { ...getUser(), email: 'test@internxt.com' };
const tier = newTier();

const reactivateAccountSpy = jest
.spyOn(mailService, 'reactivateAccount')
.mockImplementation(() => Promise.resolve());
const suspendAccountSpy = jest.spyOn(mailService, 'suspendAccount').mockImplementation(() => Promise.resolve());

await tiersService.applyMailFeatures(userWithEmail, tier);

expect(reactivateAccountSpy).not.toHaveBeenCalled();
expect(suspendAccountSpy).toHaveBeenCalledWith(userWithEmail.uuid);
});

test('When Mail is enabled, then the mail account is not suspended', async () => {
const userWithEmail = { ...getUser(), email: 'test@internxt.com' };
const tier = newTier();

tier.featuresPerService[Service.Mail].enabled = true;

jest.spyOn(mailService, 'reactivateAccount').mockImplementation(() => Promise.resolve());
const suspendAccountSpy = jest.spyOn(mailService, 'suspendAccount').mockImplementation(() => Promise.resolve());

await tiersService.applyMailFeatures(userWithEmail, tier);

expect(suspendAccountSpy).not.toHaveBeenCalled();
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,7 @@ describe('Testing the handler when an invoice is completed', () => {
});
const applyDriveFeaturesSpy = jest.spyOn(tiersService, 'applyDriveFeatures').mockResolvedValue();
const applyVpnFeaturesSpy = jest.spyOn(tiersService, 'applyVpnFeatures').mockResolvedValue();
jest.spyOn(tiersService, 'applyMailFeatures').mockResolvedValue();

const handleNewProduct = invoiceCompletedHandler['handleNewProduct'].bind(invoiceCompletedHandler);
await handleNewProduct({
Expand Down Expand Up @@ -483,6 +484,7 @@ describe('Testing the handler when an invoice is completed', () => {
.mockRejectedValue(unexpectedError);
const applyDriveFeaturesSpy = jest.spyOn(tiersService, 'applyDriveFeatures').mockResolvedValue();
const applyVpnFeaturesSpy = jest.spyOn(tiersService, 'applyVpnFeatures').mockResolvedValue();
jest.spyOn(tiersService, 'applyMailFeatures').mockResolvedValue();

const handleNewProduct = invoiceCompletedHandler['handleNewProduct'].bind(invoiceCompletedHandler);
await handleNewProduct({
Expand Down
Loading