Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve Email Testing #283

Closed
wants to merge 4 commits into from
Closed
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
45 changes: 33 additions & 12 deletions services/EmailService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');

Expand All @@ -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<void> {
public async sendPasswordReset(email: string, firstName: string, code: string): Promise<boolean> {
try {
const data = {
to: email,
Expand All @@ -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<void> {
public async sendEmailVerification(email: string, firstName: string, code: string): Promise<boolean> {
try {
const data = {
to: email,
Expand All @@ -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<void> {
public async sendOrderConfirmation(email: string, firstName: string, order: OrderInfo): Promise<boolean> {
try {
const data = {
to: email,
Expand All @@ -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<boolean> {
try {
const data = {
to: email,
Expand All @@ -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<boolean> {
try {
const data = {
to: email,
Expand All @@ -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<boolean> {
try {
const data = {
to: email,
Expand All @@ -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<boolean> {
try {
const data = {
to: email,
Expand All @@ -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<boolean> {
try {
const data = {
to: email,
Expand All @@ -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<boolean> {
try {
const data = {
to: email,
Expand All @@ -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<boolean> {
try {
const data = {
to: email,
Expand All @@ -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;
}
}

Expand Down
46 changes: 16 additions & 30 deletions tests/auth.test.ts
Original file line number Diff line number Diff line change
@@ -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();
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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()))
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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 });

Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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))
Expand All @@ -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: '[email protected]' };

when(emailService.sendEmailVerification(request.email, member.firstName, anyString()))
.thenResolve();
const response = await authController.modifyEmail(request, member);
expect(response.error).toBeNull();

Expand All @@ -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
Expand Down Expand Up @@ -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';
Expand Down Expand Up @@ -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';
Expand All @@ -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());
Expand All @@ -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()))
Expand Down
Loading