diff --git a/services/EmailService.ts b/services/EmailService.ts index 21fc2baa..9b8c31af 100644 --- a/services/EmailService.ts +++ b/services/EmailService.ts @@ -9,7 +9,7 @@ import { Uuid } from '../types'; type EmailData = MailDataRequired; export default class EmailService { - private readonly mailer = new MailService(); + private mailer: MailService; private static readonly itemDisplayTemplate = EmailService.readTemplate('itemDisplay.ejs'); @@ -34,11 +34,12 @@ export default class EmailService { private static readonly orderPartiallyFulfilledTemplate = EmailService.readTemplate('orderPartiallyFulfilled.ejs'); - constructor() { + constructor(mailer = new MailService()) { + this.mailer = mailer; this.mailer.setApiKey(Config.email.apiKey); } - public async sendPasswordReset(email: string, firstName: string, code: string): Promise { + public async sendPasswordReset(email: string, firstName: string, code: string): Promise { try { const data = { to: email, @@ -50,12 +51,14 @@ export default class EmailService { }), }; await this.sendEmail(data); + return true; } catch (error) { log.warn(`Failed to send password reset email to ${email}`, { error }); + return false; } } - public async sendEmailVerification(email: string, firstName: string, code: string): Promise { + public async sendEmailVerification(email: string, firstName: string, code: string): Promise { try { const data = { to: email, @@ -67,12 +70,14 @@ export default class EmailService { }), }; await this.sendEmail(data); + return true; } catch (error) { log.warn(`Failed to send verification email to ${email}`, { error }); + return false; } } - public async sendOrderConfirmation(email: string, firstName: string, order: OrderInfo): Promise { + public async sendOrderConfirmation(email: string, firstName: string, order: OrderInfo): Promise { try { const data = { to: email, @@ -87,12 +92,14 @@ export default class EmailService { }), }; await this.sendEmail(data); + return true; } catch (error) { log.warn(`Failed to send order confirmation email to ${email}`, { error }); + return false; } } - public async sendOrderCancellation(email: string, firstName: string, order: OrderInfo) { + public async sendOrderCancellation(email: string, firstName: string, order: OrderInfo): Promise { try { const data = { to: email, @@ -105,12 +112,14 @@ export default class EmailService { }), }; await this.sendEmail(data); + return true; } catch (error) { log.warn(`Failed to send order cancellation email to ${email}`, { error }); + return false; } } - public async sendAutomatedOrderCancellation(email: string, firstName: string, order: OrderInfo) { + public async sendAutomatedOrderCancellation(email: string, firstName: string, order: OrderInfo): Promise { try { const data = { to: email, @@ -123,12 +132,14 @@ export default class EmailService { }), }; await this.sendEmail(data); + return true; } catch (error) { log.warn(`Failed to send automated order cancellation email to ${email}`, { error }); + return false; } } - public async sendOrderPickupMissed(email: string, firstName: string, order: OrderInfo) { + public async sendOrderPickupMissed(email: string, firstName: string, order: OrderInfo): Promise { try { const data = { to: email, @@ -142,12 +153,14 @@ export default class EmailService { }), }; await this.sendEmail(data); + return true; } catch (error) { log.warn(`Failed to send order pickup missed email to ${email}`, { error }); + return false; } } - public async sendOrderPickupCancelled(email: string, firstName: string, order: OrderInfo) { + public async sendOrderPickupCancelled(email: string, firstName: string, order: OrderInfo): Promise { try { const data = { to: email, @@ -161,12 +174,14 @@ export default class EmailService { }), }; await this.sendEmail(data); + return true; } catch (error) { log.warn(`Failed to send order pickup cancelled email to ${email}`, { error }); + return false; } } - public async sendOrderPickupUpdated(email: string, firstName: string, order: OrderInfo) { + public async sendOrderPickupUpdated(email: string, firstName: string, order: OrderInfo): Promise { try { const data = { to: email, @@ -180,12 +195,14 @@ export default class EmailService { }), }; await this.sendEmail(data); + return true; } catch (error) { log.warn(`Failed to send order pickup update email to ${email}`, { error }); + return false; } } - public async sendOrderFulfillment(email: string, firstName: string, order: OrderInfo) { + public async sendOrderFulfillment(email: string, firstName: string, order: OrderInfo): Promise { try { const data = { to: email, @@ -198,14 +215,16 @@ export default class EmailService { }), }; await this.sendEmail(data); + return true; } catch (error) { log.warn(`Failed to send order fulfillment email to ${email}`, { error }); + return false; } } public async sendPartialOrderFulfillment(email: string, firstName: string, fulfilledItems: OrderLineItem[], unfulfilledItems: OrderLineItem[], pickupEvent: OrderPickupEventInfo, - orderUuid: string) { + orderUuid: string): Promise { try { const data = { to: email, @@ -220,8 +239,10 @@ export default class EmailService { }), }; await this.sendEmail(data); + return true; } catch (error) { log.warn(`Failed to send partial order fulfillment email to ${email}`, { error }); + return false; } } diff --git a/tests/auth.test.ts b/tests/auth.test.ts index ef920873..f6d0c694 100644 --- a/tests/auth.test.ts +++ b/tests/auth.test.ts @@ -1,15 +1,15 @@ import * as jwt from 'jsonwebtoken'; import * as bcrypt from 'bcrypt'; import { NotFoundError } from 'routing-controllers'; -import { anyString, instance, mock, verify, when } from 'ts-mockito'; +import { anyString, instance, verify } from 'ts-mockito'; import { Config } from '../config'; import { UserModel } from '../models/UserModel'; -import EmailService from '../services/EmailService'; import UserAuthService from '../services/UserAuthService'; import { UserAccessType, UserState } from '../types'; import { ControllerFactory } from './controllers'; import { DatabaseConnection, PortalState, UserFactory } from './data'; import FactoryUtils from './data/FactoryUtils'; +import { MockEmailService } from './services'; beforeAll(async () => { await DatabaseConnection.connect(); @@ -43,9 +43,7 @@ describe('account registration', () => { }; // register member - const emailService = mock(EmailService); - when(emailService.sendEmailVerification(user.email, user.firstName, anyString())) - .thenResolve(); + const emailService = new MockEmailService().mock(); const authController = ControllerFactory.auth(conn, instance(emailService)); const registerRequest = { user }; const registerResponse = await authController.register(registerRequest, FactoryUtils.randomHexString()); @@ -86,9 +84,7 @@ describe('account registration', () => { graduationYear: UserFactory.graduationYear(), }; - const emailService = mock(EmailService); - when(emailService.sendEmailVerification(user.email, user.firstName, anyString())) - .thenResolve(); + const emailService = new MockEmailService().mock(); const authController = ControllerFactory.auth(conn, instance(emailService)); const registerRequest = { user }; await expect(authController.register(registerRequest, FactoryUtils.randomHexString())) @@ -108,7 +104,7 @@ describe('account login', () => { .createUsers(member) .write(); - const emailService = mock(EmailService); + const emailService = new MockEmailService().mock(); const authController = ControllerFactory.auth(conn, instance(emailService)); const loginRequest = { email: member.email, @@ -130,7 +126,7 @@ describe('account login', () => { .createUsers(member) .write(); - const emailService = mock(EmailService); + const emailService = new MockEmailService().mock(); const authController = ControllerFactory.auth(conn, instance(emailService)); const loginRequest = { email: member.email, @@ -154,9 +150,7 @@ describe('verifying email', () => { .createUsers(member) .write(); - const emailService = mock(EmailService); - when(emailService.sendEmailVerification(member.email, member.firstName, anyString())) - .thenResolve(); + const emailService = new MockEmailService().mock(); const authController = ControllerFactory.auth(conn, instance(emailService)); await authController.verifyEmail({ accessCode }); @@ -176,9 +170,7 @@ describe('verifying email', () => { .createUsers(admin, member) .write(); - const emailService = mock(EmailService); - when(emailService.sendEmailVerification(member.email, member.firstName, anyString())) - .thenResolve(); + const emailService = new MockEmailService().mock(); const authController = ControllerFactory.auth(conn, instance(emailService)); await expect(authController.verifyEmail({ accessCode: FactoryUtils.randomHexString() })) .rejects.toThrow(NotFoundError); @@ -197,9 +189,7 @@ describe('resending email verification', () => { .createUsers(member) .write(); - const emailService = mock(EmailService); - when(emailService.sendEmailVerification(member.email, member.firstName, anyString())) - .thenResolve(); + const emailService = new MockEmailService().mock(); const authController = ControllerFactory.auth(conn, instance(emailService)); const params = { email: member.email }; await authController.resendEmailVerification(params); @@ -213,7 +203,7 @@ describe('resending email verification', () => { const conn = await DatabaseConnection.get(); const member = UserFactory.fake(); - const emailService = mock(EmailService); + const emailService = new MockEmailService().mock(); const authController = ControllerFactory.auth(conn, instance(emailService)); const params = { email: member.email }; await expect(authController.resendEmailVerification(params)) @@ -233,14 +223,12 @@ describe('email modification', () => { .createUsers(member) .write(); - const emailService = mock(EmailService); + const emailService = new MockEmailService().mock(); const authController = ControllerFactory.auth(conn, instance(emailService)); // call route to change email const request = { email: 'someemail@example.com' }; - when(emailService.sendEmailVerification(request.email, member.firstName, anyString())) - .thenResolve(); const response = await authController.modifyEmail(request, member); expect(response.error).toBeNull(); @@ -264,7 +252,7 @@ describe('email modification', () => { .createUsers(member, otherMember) .write(); - const emailService = mock(EmailService); + const emailService = new MockEmailService().mock(); const authController = ControllerFactory.auth(conn, instance(emailService)); // call route to change email @@ -293,7 +281,7 @@ describe('password reset', () => { .createUsers(member) .write(); - const emailService = mock(EmailService); + const emailService = new MockEmailService().mock(); const authController = ControllerFactory.auth(conn, instance(emailService)); const params = { accessCode }; const newPassword = 'new-password'; @@ -322,7 +310,7 @@ describe('password reset', () => { .createUsers(member) .write(); - const emailService = mock(EmailService); + const emailService = new MockEmailService().mock(); const authController = ControllerFactory.auth(conn, instance(emailService)); const params = { accessCode }; const newPassword = 'new-password'; @@ -349,8 +337,7 @@ describe('resending password reset email', () => { .createUsers(member) .write(); - const emailService = mock(EmailService); - when(emailService.sendPasswordReset(member.email, member.firstName, anyString())); + const emailService = new MockEmailService().mock(); const authController = ControllerFactory.auth(conn, instance(emailService)); const params = { email: member.email }; await authController.sendPasswordResetEmail(params, FactoryUtils.randomHexString()); @@ -366,8 +353,7 @@ describe('resending password reset email', () => { const conn = await DatabaseConnection.get(); const member = UserFactory.fake(); - const emailService = mock(EmailService); - when(emailService.sendPasswordReset(member.email, member.firstName, anyString())); + const emailService = new MockEmailService().mock(); const authController = ControllerFactory.auth(conn, instance(emailService)); const params = { email: member.email }; await expect(authController.sendPasswordResetEmail(params, FactoryUtils.randomHexString())) diff --git a/tests/merchOrder.test.ts b/tests/merchOrder.test.ts index 45705222..314b27db 100644 --- a/tests/merchOrder.test.ts +++ b/tests/merchOrder.test.ts @@ -1,14 +1,14 @@ import * as faker from 'faker'; import * as moment from 'moment'; -import { mock, when, anything, instance, verify, anyString } from 'ts-mockito'; +import { anything, instance, verify } from 'ts-mockito'; import { ForbiddenError, NotFoundError } from 'routing-controllers'; -import EmailService from '../services/EmailService'; import { OrderModel } from '../models/OrderModel'; import { OrderPickupEventModel } from '../models/OrderPickupEventModel'; import { UserAccessType, OrderStatus, ActivityType, OrderPickupEventStatus } from '../types'; import { ControllerFactory } from './controllers'; import { DatabaseConnection, MerchFactory, PortalState, UserFactory } from './data'; import { MerchStoreControllerWrapper } from './controllers/MerchStoreControllerWrapper'; +import { MockEmailService } from './services'; import { UserModel } from '../models/UserModel'; beforeAll(async () => { @@ -47,9 +47,7 @@ describe('merch orders', () => { .createOrderPickupEvents(pickupEvent) .write(); - const emailService = mock(EmailService); - when(emailService.sendOrderConfirmation(member.email, member.firstName, anything())) - .thenResolve(); + const emailService = new MockEmailService().mock(); const order = [ { option: option.uuid, @@ -109,11 +107,7 @@ describe('merch orders', () => { .createOrderPickupEvents(pickupEvent) .write(); - const emailService = mock(EmailService); - when(emailService.sendOrderConfirmation(member.email, member.firstName, anything())) - .thenResolve(); - when(emailService.sendOrderFulfillment(member.email, member.firstName, anything())) - .thenResolve(); + const emailService = new MockEmailService().mock(); // place order const order = [ @@ -185,11 +179,7 @@ describe('merch orders', () => { .orderMerch(member, [{ option, quantity: 1 }], ongoingPickupEvent) .write(); - const emailService = mock(EmailService); - when(emailService.sendOrderConfirmation(member.email, member.firstName, anything())) - .thenResolve(); - when(emailService.sendOrderFulfillment(member.email, member.firstName, anything())) - .thenResolve(); + const emailService = new MockEmailService().mock(); const pastOrder = await conn.manager.findOne(OrderModel, { pickupEvent: pastPickupEvent }, @@ -208,7 +198,7 @@ describe('merch orders', () => { ], }; - const merchStoreController = ControllerFactory.merchStore(conn, emailService); + const merchStoreController = ControllerFactory.merchStore(conn, instance(emailService)); await merchStoreController.fulfillMerchOrderItems(pastOrderUuid, fulfillPastOrderItemsRequest, merchDistributor); await pastOrder.reload(); @@ -255,13 +245,7 @@ describe('merch orders', () => { .createOrderPickupEvents(pickupEvent) .write(); - const emailService = mock(EmailService); - when(emailService.sendOrderConfirmation(member.email, member.firstName, anything())) - .thenResolve(); - when(emailService.sendPartialOrderFulfillment( - member.email, member.firstName, anything(), anything(), anything(), anything(), - )) - .thenResolve(); + const emailService = new MockEmailService().mock(); // place order const order = [ @@ -337,13 +321,7 @@ describe('merch orders', () => { .createOrderPickupEvents(pickupEvent, anotherPickupEvent) .write(); - const emailService = mock(EmailService); - when(emailService.sendOrderConfirmation(member.email, member.firstName, anything())) - .thenResolve(); - when(emailService.sendPartialOrderFulfillment( - member.email, member.firstName, anything(), anything(), anything(), anything(), - )) - .thenResolve(); + const emailService = new MockEmailService().mock(); // place order const order = [ @@ -420,11 +398,7 @@ describe('merch orders', () => { .createOrderPickupEvents(pickupEvent) .write(); - const emailService = mock(EmailService); - when(emailService.sendOrderConfirmation(member.email, member.firstName, anything())) - .thenResolve(); - when(emailService.sendOrderCancellation(member.email, member.firstName, anything())) - .thenResolve(); + const emailService = new MockEmailService().mock(); // place order const order = [ @@ -483,15 +457,7 @@ describe('merch orders', () => { .createOrderPickupEvents(pickupEvent) .write(); - const emailService = mock(EmailService); - when(emailService.sendOrderConfirmation(member.email, member.firstName, anything())) - .thenResolve(); - when(emailService.sendPartialOrderFulfillment( - member.email, member.firstName, anything(), anything(), anything(), anything(), - )) - .thenResolve(); - when(emailService.sendOrderCancellation(member.email, member.firstName, anything())) - .thenResolve(); + const emailService = new MockEmailService().mock(); // place order const orderRequest = [ @@ -547,9 +513,7 @@ describe('merch orders', () => { .createOrderPickupEvents(pickupEvent) .write(); - const emailService = mock(EmailService); - when(emailService.sendOrderConfirmation(member.email, member.firstName, anything())) - .thenResolve(); + const emailService = new MockEmailService().mock(); // place order const order = [ @@ -588,9 +552,7 @@ describe('merch orders', () => { .createOrderPickupEvents(pickupEvent) .write(); - const emailService = mock(EmailService); - when(emailService.sendOrderConfirmation(member.email, member.firstName, anything())) - .thenResolve(); + const emailService = new MockEmailService().mock(); // place order const order = [ @@ -642,10 +604,7 @@ describe('merch orders', () => { .createOrderPickupEvents(orderPickupEvent) .write(); - const emailService = mock(EmailService); - when(emailService.sendOrderConfirmation(anything(), anything(), anything())) - .thenResolve(); - + const emailService = new MockEmailService().mock(); const merchController = ControllerFactory.merchStore(conn, instance(emailService)); const placeMerchOrderRequest = { order: [{ option: option.uuid, quantity: 1 }], @@ -692,9 +651,7 @@ describe('merch orders', () => { await member.reload(); - const emailService = mock(EmailService); - when(emailService.sendOrderConfirmation(anything(), anything(), anything())) - .thenResolve(); + const emailService = new MockEmailService().mock(); // Test Lifetime limit const merchController = ControllerFactory.merchStore(conn, instance(emailService)); @@ -759,9 +716,7 @@ describe('merch orders', () => { await conn.manager.update(OrderModel, { user: pickupCancelledMember }, { status: OrderStatus.PICKUP_CANCELLED }); await conn.manager.update(OrderModel, { user: pickupMissedMember }, { status: OrderStatus.PICKUP_MISSED }); - const emailService = mock(EmailService); - when(emailService.sendAutomatedOrderCancellation(anything(), anything(), anything())) - .thenResolve(); + const emailService = new MockEmailService().mock(); // cancel all pending orders const merchController = ControllerFactory.merchStore(conn, emailService); @@ -828,10 +783,7 @@ describe('merch orders', () => { .orderMerch(admin, order, pickupEvent) .write(); - const emailService = mock(EmailService); - when(emailService.sendOrderConfirmation(anything(), anything(), anything())) - .thenResolve(); - + const emailService = new MockEmailService().mock(); const merchStoreController = ControllerFactory.merchStore(conn, instance(emailService)); const order1 = await merchStoreController.getMerchOrdersForCurrentUser(member1); expect(order1.orders.length).toBe(1); @@ -880,11 +832,7 @@ describe('merch orders', () => { .orderMerch(member, order, pickupEvent) .write(); - const emailService = mock(EmailService); - when(emailService.sendOrderCancellation(anyString(), anyString(), anything())) - .thenResolve(); - when(emailService.sendOrderConfirmation(anyString(), anyString(), anything())) - .thenResolve(); + const emailService = new MockEmailService().mock(); // cancel order const merchController = ControllerFactory.merchStore(conn, instance(emailService)); @@ -923,9 +871,7 @@ describe('merch order pickup events', () => { .createOrderPickupEvents(pastPickupEvent, ongoingPickupEvent, futurePickupEvent) .write(); - const emailService = mock(EmailService); - when(emailService.sendOrderConfirmation(anything(), anything(), anything())) - .thenResolve(); + const emailService = new MockEmailService().mock(); const merchController = ControllerFactory.merchStore(conn, instance(emailService)); const getFuturePickupEventsResponse = await merchController.getFuturePickupEvents(merchDistributor); @@ -955,10 +901,7 @@ describe('merch order pickup events', () => { .createUsers(merchDistributor) .write(); - const emailService = mock(EmailService); - when(emailService.sendOrderConfirmation(anything(), anything(), anything())) - .thenResolve(); - + const emailService = new MockEmailService().mock(); const merchController = ControllerFactory.merchStore(conn, instance(emailService)); await merchController.createPickupEvent({ pickupEvent }, merchDistributor); @@ -1034,10 +977,7 @@ describe('merch order pickup events', () => { .createOrderPickupEvents(pickupEvent) .write(); - const emailService = mock(EmailService); - when(emailService.sendOrderConfirmation(anything(), anything(), anything())) - .thenResolve(); - + const emailService = new MockEmailService().mock(); const params = { uuid: pickupEvent.uuid }; const merchController = ControllerFactory.merchStore(conn, instance(emailService)); await merchController.deletePickupEvent(params, merchDistributor); @@ -1060,9 +1000,7 @@ describe('merch order pickup events', () => { .createOrderPickupEvents(pickupEvent) .write(); - const emailService = mock(EmailService); - when(emailService.sendOrderConfirmation(member.email, member.firstName, anything())) - .thenResolve(); + const emailService = new MockEmailService().mock(); // place order const order = [{ option: option.uuid, quantity: 1 }]; @@ -1113,11 +1051,7 @@ describe('merch order pickup events', () => { .createOrderPickupEvents(pickupEvent) .write(); - const emailService = mock(EmailService); - when(emailService.sendOrderConfirmation(anyString(), anyString(), anything())) - .thenResolve(); - when(emailService.sendOrderPickupCancelled(anyString(), anyString(), anything())) - .thenResolve(); + const emailService = new MockEmailService().mock(); // place order const order = [ @@ -1178,13 +1112,7 @@ describe('merch order pickup events', () => { .createOrderPickupEvents(pickupEvent) .write(); - const emailService = mock(EmailService); - when(emailService.sendOrderConfirmation(anything(), anything(), anything())) - .thenResolve(); - when(emailService.sendOrderFulfillment(member1.email, member1.firstName, anything())) - .thenResolve(); - when(emailService.sendOrderPickupMissed(member2.email, member2.firstName, anything())) - .thenResolve(); + const emailService = new MockEmailService().mock(); // place order const order = [ @@ -1248,9 +1176,7 @@ describe('merch order pickup events', () => { .createOrderPickupEvents(pickupEventToComplete) .write(); - const emailService = mock(EmailService); - when(emailService.sendOrderPickupCancelled(anything(), anything(), anything())) - .thenResolve(); + const emailService = new MockEmailService().mock(); // update the pickup events to have passed yesterday const completedPickupEventUuid = { uuid: pickupEventToComplete.uuid }; @@ -1280,9 +1206,7 @@ describe('merch order pickup events', () => { .createOrderPickupEvents(pickupEventToComplete) .write(); - const emailService = mock(EmailService); - when(emailService.sendOrderPickupMissed(anything(), anything(), anything())) - .thenResolve(); + const emailService = new MockEmailService().mock(); // update the pickup event to be 1 hour from now const completedPickupEventUuid = { uuid: pickupEventToComplete.uuid }; @@ -1310,9 +1234,7 @@ describe('merch order pickup events', () => { .createOrderPickupEvents(ongoingPickupEvent, pastPickupEvent) .write(); - const emailService = mock(EmailService); - when(emailService.sendOrderPickupMissed(anything(), anything(), anything())) - .thenResolve(); + const emailService = new MockEmailService().mock(); const merchController = ControllerFactory.merchStore(conn, instance(emailService)); @@ -1341,9 +1263,7 @@ describe('merch order pickup events', () => { .createOrderPickupEvents(pickupEventToComplete, pickupEventToCancel) .write(); - const emailService = mock(EmailService); - when(emailService.sendOrderPickupCancelled(anything(), anything(), anything())) - .thenResolve(); + const emailService = new MockEmailService().mock(); // update the pickup events to have passed const cancelledPickupEventUuid = { uuid: pickupEventToCancel.uuid }; @@ -1396,11 +1316,7 @@ describe('merch order pickup events', () => { .createOrderPickupEvents(pickupEvent, anotherPickupEvent) .write(); - const emailService = mock(EmailService); - when(emailService.sendOrderConfirmation(member.email, member.firstName, anything())) - .thenResolve(); - when(emailService.sendOrderPickupUpdated(member.email, member.firstName, anything())) - .thenResolve(); + const emailService = new MockEmailService().mock(); // place order const order = [ @@ -1444,11 +1360,7 @@ describe('merch order pickup events', () => { .createOrderPickupEvents(pickupEvent, anotherPickupEvent) .write(); - const emailService = mock(EmailService); - when(emailService.sendOrderConfirmation(member.email, member.firstName, anything())) - .thenResolve(); - when(emailService.sendOrderPickupUpdated(member.email, member.firstName, anything())) - .thenResolve(); + const emailService = new MockEmailService().mock(); // place order const order = [ @@ -1503,11 +1415,7 @@ describe('merch order pickup events', () => { .createOrderPickupEvents(pickupEvent, anotherPickupEvent) .write(); - const emailService = mock(EmailService); - when(emailService.sendOrderConfirmation(member.email, member.firstName, anything())) - .thenResolve(); - when(emailService.sendOrderPickupUpdated(member.email, member.firstName, anything())) - .thenResolve(); + const emailService = new MockEmailService().mock(); // place order const order = [ @@ -1565,11 +1473,7 @@ describe('merch order pickup events', () => { .createOrderPickupEvents(pickupEvent, moreRecentPickupEvent) .write(); - const emailService = mock(EmailService); - when(emailService.sendOrderConfirmation(member.email, member.firstName, anything())) - .thenResolve(); - when(emailService.sendOrderPickupUpdated(member.email, member.firstName, anything())) - .thenResolve(); + const emailService = new MockEmailService().mock(); // place order const order = [ @@ -1617,10 +1521,7 @@ describe('merch order pickup events', () => { .orderMerch(member, [{ option, quantity: 1 }], pickupEvent) .write(); - const emailService = mock(EmailService); - when(emailService.sendOrderConfirmation(anything(), anything(), anything())) - .thenResolve(); - + const emailService = new MockEmailService().mock(); const merchController = ControllerFactory.merchStore(conn, instance(emailService)); const placeMerchOrderRequest = { order: [{ option: option.uuid, quantity: 1 }], @@ -1656,10 +1557,7 @@ describe('merch order pickup events', () => { }, }; - const emailService = mock(EmailService); - when(emailService.sendOrderConfirmation(anything(), anything(), anything())) - .thenResolve(); - + const emailService = new MockEmailService().mock(); const merchController = ControllerFactory.merchStore(conn, instance(emailService)); const params = { uuid: pickupEvent.uuid }; await merchController.editPickupEvent(params, editPickupEventRequest, merchDistributor); @@ -1694,10 +1592,7 @@ describe('merch order pickup events', () => { .orderMerch(member, [{ option, quantity: 1 }], pickupEvent) .write(); - const emailService = mock(EmailService); - when(emailService.sendOrderConfirmation(anything(), anything(), anything())) - .thenResolve(); - + const emailService = new MockEmailService().mock(); const merchController = ControllerFactory.merchStore(conn, instance(emailService)); const placeMerchOrderRequest = { order: [{ option: option.uuid, quantity: 1 }], @@ -1750,11 +1645,7 @@ describe('merch order pickup events', () => { .orderMerch(member, [{ option, quantity: 1 }], pickupEvent) .write(); - const emailService = mock(EmailService); - when(emailService.sendOrderConfirmation(member.email, member.firstName, anything())) - .thenResolve(); - when(emailService.sendOrderCancellation(member.email, member.firstName, anything())) - .thenResolve(); + const emailService = new MockEmailService().mock(); // cancel order const order = await conn.manager.findOne(OrderModel, { user: member }); diff --git a/tests/merchStore.test.ts b/tests/merchStore.test.ts index 622ce66f..eaad7fbd 100644 --- a/tests/merchStore.test.ts +++ b/tests/merchStore.test.ts @@ -1,13 +1,13 @@ import * as faker from 'faker'; import { ForbiddenError } from 'routing-controllers'; import { zip } from 'underscore'; -import { anything, instance, mock, when } from 'ts-mockito'; +import { instance } from 'ts-mockito'; import { OrderModel } from '../models/OrderModel'; import { MerchandiseItemOptionModel } from '../models/MerchandiseItemOptionModel'; import { MerchItemEdit, UserAccessType } from '../types'; import { ControllerFactory } from './controllers'; import { DatabaseConnection, MerchFactory, PortalState, UserFactory } from './data'; -import EmailService from '../services/EmailService'; +import { MockEmailService } from './services'; beforeAll(async () => { await DatabaseConnection.connect(); @@ -279,9 +279,7 @@ describe('merch items with options', () => { ], pickupEvent) .write(); - const emailService = mock(EmailService); - when(emailService.sendOrderCancellation(member.email, member.firstName, anything())) - .thenResolve(); + const emailService = new MockEmailService().mock(); // make sure the item's remaining counts got updated const merchStoreController = ControllerFactory.merchStore(conn, instance(emailService)); @@ -326,12 +324,10 @@ describe('merch items with options', () => { ], pickupEvent) .write(); - const emailService = mock(EmailService); - when(emailService.sendOrderConfirmation(member.email, member.firstName, anything())) - .thenResolve(); + const emailService = new MockEmailService().mock(); // placing order fails - const merchController = ControllerFactory.merchStore(conn); + const merchController = ControllerFactory.merchStore(conn, instance(emailService)); const order = [{ option: option2.uuid, quantity: 1 }]; const placeOrderParams = { order, pickupEvent: pickupEvent.uuid }; await expect(merchController.placeMerchOrder(placeOrderParams, member)) diff --git a/tests/services/MockEmailService.ts b/tests/services/MockEmailService.ts new file mode 100644 index 00000000..0873a25f --- /dev/null +++ b/tests/services/MockEmailService.ts @@ -0,0 +1,80 @@ +import { MailService } from '@sendgrid/mail'; +import { mock, when, instance, anything, spy } from 'ts-mockito'; +import EmailService, { OrderInfo, OrderLineItem, OrderPickupEventInfo } from '../../services/EmailService'; + +export class MockEmailService extends EmailService { + constructor() { + const mailService = mock(MailService); + + when(mailService.setApiKey(anything())).thenReturn(); + when(mailService.send(anything())).thenResolve(); + + super(instance(mailService)); + } + + public async sendPasswordReset(email: string, firstName: string, code: string) { + const result = await super.sendPasswordReset(email, firstName, code); + expect(result).toEqual(true); + return result; + } + + public async sendEmailVerification(email: string, firstName: string, code: string) { + const result = await super.sendEmailVerification(email, firstName, code); + expect(result).toEqual(true); + return result; + } + + public async sendOrderConfirmation(email: string, firstName: string, order: OrderInfo) { + const result = await super.sendOrderConfirmation(email, firstName, order); + expect(result).toEqual(true); + return result; + } + + public async sendOrderCancellation(email: string, firstName: string, order: OrderInfo) { + const result = await super.sendOrderCancellation(email, firstName, order); + expect(result).toEqual(true); + return result; + } + + public async sendAutomatedOrderCancellation(email: string, firstName: string, order: OrderInfo) { + const result = await super.sendAutomatedOrderCancellation(email, firstName, order); + expect(result).toEqual(true); + return result; + } + + public async sendOrderPickupMissed(email: string, firstName: string, order: OrderInfo) { + const result = await super.sendOrderPickupMissed(email, firstName, order); + expect(result).toEqual(true); + return result; + } + + public async sendOrderPickupCancelled(email: string, firstName: string, order: OrderInfo) { + const result = await super.sendOrderPickupCancelled(email, firstName, order); + expect(result).toEqual(true); + return result; + } + + public async sendOrderPickupUpdated(email: string, firstName: string, order: OrderInfo) { + const result = await super.sendOrderPickupUpdated(email, firstName, order); + expect(result).toEqual(true); + return result; + } + + public async sendOrderFulfillment(email: string, firstName: string, order: OrderInfo) { + const result = await super.sendOrderFulfillment(email, firstName, order); + expect(result).toEqual(true); + return result; + } + + public async sendPartialOrderFulfillment(email: string, firstName: string, fulfilledItems: OrderLineItem[], + unfulfilledItems: OrderLineItem[], pickupEvent: OrderPickupEventInfo, orderUuid: string) { + const result = await super.sendPartialOrderFulfillment(email, firstName, fulfilledItems, + unfulfilledItems, pickupEvent, orderUuid); + expect(result).toEqual(true); + return result; + } + + public mock() { + return spy(this); + } +} diff --git a/tests/services/index.ts b/tests/services/index.ts new file mode 100644 index 00000000..f4b99d55 --- /dev/null +++ b/tests/services/index.ts @@ -0,0 +1 @@ +export * from './MockEmailService';