mirror of
https://github.com/brendan-ch/project-inter-server.git
synced 2026-04-17 07:50:31 +00:00
update notification scheduler tests with mock notification sender
This commit is contained in:
@@ -0,0 +1,210 @@
|
||||
import { beforeEach, describe, expect, it, jest } from "@jest/globals";
|
||||
import { ETANotificationScheduler } from "../../../src/notifications/schedulers/ETANotificationScheduler";
|
||||
import { UnoptimizedInMemoryRepository } from "../../../src/repositories/UnoptimizedInMemoryRepository";
|
||||
import http2 from "http2";
|
||||
import { IEta, IShuttle, IStop } from "../../../src/entities/entities";
|
||||
import { addMockShuttleToRepository, addMockStopToRepository } from "../../testHelpers/repositorySetupHelpers";
|
||||
import { AppleNotificationSender } from "../../../src/notifications/senders/AppleNotificationSender";
|
||||
|
||||
jest.mock("http2");
|
||||
jest.mock("../../../src/notifications/senders/AppleNotificationSender");
|
||||
|
||||
const MockAppleNotificationSender = AppleNotificationSender as jest.MockedClass<typeof AppleNotificationSender>;
|
||||
|
||||
function mockNotificationSenderMethods(shouldSimulateNotificationSend: boolean) {
|
||||
MockAppleNotificationSender.prototype.sendNotificationImmediately = jest.fn(async () => shouldSimulateNotificationSend);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for a condition to become true until the timeout
|
||||
* is hit.
|
||||
* @param condition
|
||||
* @param timeoutMilliseconds
|
||||
* @param intervalMilliseconds
|
||||
*/
|
||||
async function waitForCondition(condition: () => boolean, timeoutMilliseconds = 5000, intervalMilliseconds = 500) {
|
||||
const startTime = Date.now();
|
||||
while (!condition()) {
|
||||
if (Date.now() - startTime > timeoutMilliseconds) {
|
||||
throw new Error("Timeout waiting for condition");
|
||||
}
|
||||
await new Promise((resolve) => setTimeout(resolve, intervalMilliseconds));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for a specified number of milliseconds.
|
||||
* @param ms
|
||||
*/
|
||||
async function waitForMilliseconds(ms: number): Promise<void> {
|
||||
await new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
|
||||
describe("ETANotificationScheduler", () => {
|
||||
let repository: UnoptimizedInMemoryRepository
|
||||
let notificationService: ETANotificationScheduler;
|
||||
|
||||
beforeEach(() => {
|
||||
repository = new UnoptimizedInMemoryRepository();
|
||||
|
||||
mockNotificationSenderMethods(true);
|
||||
|
||||
const appleNotificationSender = new MockAppleNotificationSender(false);
|
||||
notificationService = new ETANotificationScheduler(repository, appleNotificationSender);
|
||||
});
|
||||
|
||||
function generateNotificationDataAndEta(shuttle: IShuttle, stop: IStop) {
|
||||
const eta: IEta = {
|
||||
shuttleId: shuttle.id,
|
||||
stopId: stop.id,
|
||||
secondsRemaining: 120,
|
||||
};
|
||||
|
||||
const notificationData1 = {
|
||||
deviceId: "1",
|
||||
shuttleId: eta.shuttleId,
|
||||
stopId: eta.stopId,
|
||||
}
|
||||
const notificationData2 = {
|
||||
...notificationData1,
|
||||
deviceId: "2",
|
||||
}
|
||||
return { eta, notificationData1, notificationData2 };
|
||||
}
|
||||
|
||||
describe("scheduleNotification", () => {
|
||||
it("schedules the notification", async () => {
|
||||
// arrange
|
||||
const notificationData = {
|
||||
deviceId: "1",
|
||||
shuttleId: "1",
|
||||
stopId: "1"
|
||||
};
|
||||
|
||||
await notificationService.scheduleNotification(notificationData);
|
||||
|
||||
const isNotificationScheduled = notificationService.isNotificationScheduled(notificationData);
|
||||
expect(isNotificationScheduled).toEqual(true);
|
||||
});
|
||||
|
||||
it("sends and clears correct notification after ETA changed", async () => {
|
||||
// Arrange
|
||||
const shuttle = await addMockShuttleToRepository(repository, "1");
|
||||
const stop = await addMockStopToRepository(repository, "1");
|
||||
|
||||
const { eta, notificationData1, notificationData2 } = generateNotificationDataAndEta(shuttle, stop);
|
||||
|
||||
// Act
|
||||
await notificationService.scheduleNotification(notificationData1);
|
||||
await notificationService.scheduleNotification(notificationData2);
|
||||
await repository.addOrUpdateEta(eta);
|
||||
|
||||
// Assert
|
||||
// Because repository publisher calls subscriber without await
|
||||
// wait for the change to occur first
|
||||
await waitForCondition(() => !notificationService.isNotificationScheduled(notificationData1));
|
||||
|
||||
const isFirstNotificationScheduled = notificationService.isNotificationScheduled(notificationData1);
|
||||
const isSecondNotificationScheduled = notificationService.isNotificationScheduled(notificationData2);
|
||||
// No longer scheduled after being sent
|
||||
expect(isFirstNotificationScheduled).toBe(false);
|
||||
expect(isSecondNotificationScheduled).toBe(false);
|
||||
});
|
||||
|
||||
it("doesn't send notification if seconds threshold not exceeded", async () => {
|
||||
// Arrange
|
||||
const shuttle = await addMockShuttleToRepository(repository, "1");
|
||||
const stop = await addMockStopToRepository(repository, "1");
|
||||
const { eta, notificationData1 } = generateNotificationDataAndEta(shuttle, stop);
|
||||
eta.secondsRemaining = notificationService.secondsThresholdForNotificationToFire + 100;
|
||||
|
||||
// Act
|
||||
await notificationService.scheduleNotification(notificationData1);
|
||||
await repository.addOrUpdateEta(eta);
|
||||
|
||||
// Assert
|
||||
await waitForMilliseconds(500);
|
||||
const isNotificationScheduled = notificationService.isNotificationScheduled(notificationData1);
|
||||
expect(isNotificationScheduled).toBe(true);
|
||||
});
|
||||
|
||||
it("leaves notification in array if delivery unsuccessful", async () => {
|
||||
// Arrange
|
||||
const shuttle = await addMockShuttleToRepository(repository, "1");
|
||||
const stop = await addMockStopToRepository(repository, "1");
|
||||
const { eta, notificationData1 } = generateNotificationDataAndEta(shuttle, stop)
|
||||
|
||||
mockNotificationSenderMethods(false);
|
||||
notificationService = new ETANotificationScheduler(
|
||||
repository,
|
||||
new MockAppleNotificationSender(),
|
||||
)
|
||||
|
||||
// Act
|
||||
await notificationService.scheduleNotification(notificationData1);
|
||||
await repository.addOrUpdateEta(eta);
|
||||
|
||||
// Assert
|
||||
// The notification should stay scheduled to be retried once
|
||||
// the ETA updates again
|
||||
await waitForMilliseconds(500);
|
||||
const isNotificationScheduled = notificationService.isNotificationScheduled(notificationData1);
|
||||
expect(isNotificationScheduled).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe("cancelNotification", () => {
|
||||
it("stops notification from sending to given shuttle/stop ID", async () => {
|
||||
// Arrange
|
||||
const shuttle = await addMockShuttleToRepository(repository, "1");
|
||||
const stop = await addMockStopToRepository(repository, "1");
|
||||
const { eta, notificationData1 } = generateNotificationDataAndEta(shuttle, stop);
|
||||
|
||||
await notificationService.scheduleNotification(notificationData1);
|
||||
|
||||
// Act
|
||||
await notificationService.cancelNotificationIfExists(notificationData1);
|
||||
await repository.addOrUpdateEta(eta);
|
||||
|
||||
// Assert
|
||||
await waitForMilliseconds(500);
|
||||
expect(http2.connect as jest.Mock).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getAllScheduledNotificationsForDevice", () => {
|
||||
it("returns scheduled notifications for the device ID", async () => {
|
||||
// Arrange
|
||||
const shuttle1 = await addMockShuttleToRepository(repository, "1");
|
||||
const stop = await addMockStopToRepository(repository, "1");
|
||||
const { notificationData1 } = generateNotificationDataAndEta(shuttle1, stop);
|
||||
await notificationService.scheduleNotification(notificationData1);
|
||||
|
||||
const shuttle2 = {
|
||||
...shuttle1,
|
||||
id: "2",
|
||||
}
|
||||
await repository.addOrUpdateShuttle(shuttle2);
|
||||
|
||||
const notificationData2 = {
|
||||
...notificationData1,
|
||||
shuttleId: shuttle2.id,
|
||||
}
|
||||
await notificationService.scheduleNotification(notificationData2);
|
||||
|
||||
// Act
|
||||
const notifications = await notificationService.getAllScheduledNotificationsForDevice(notificationData1.deviceId);
|
||||
|
||||
// Assert
|
||||
expect(notifications.length).toBe(2);
|
||||
});
|
||||
|
||||
it("returns an empty array if there are no notifications", async () => {
|
||||
// Act
|
||||
const notifications = await notificationService.getAllScheduledNotificationsForDevice("1");
|
||||
expect(notifications.length).toBe(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user