diff --git a/src/repositories/shuttle/__tests__/ShuttleRepositorySharedTests.test.ts b/src/repositories/shuttle/__tests__/ShuttleRepositorySharedTests.test.ts index cb606c9..cbf9555 100644 --- a/src/repositories/shuttle/__tests__/ShuttleRepositorySharedTests.test.ts +++ b/src/repositories/shuttle/__tests__/ShuttleRepositorySharedTests.test.ts @@ -11,6 +11,7 @@ import { } from "../../../../testHelpers/mockDataGenerators"; import { RepositoryHolder } from "../../../../testHelpers/RepositoryHolder"; import { setupRouteAndOrderedStopsForShuttleRepository } from "../../../../testHelpers/setupRouteAndOrderedStopsForShuttleRepository"; +import { ShuttleRepositoryEvent } from "../ShuttleGetterRepository"; class UnoptimizedInMemoryShuttleRepositoryHolder implements RepositoryHolder { name = 'UnoptimizedInMemoryShuttleRepository'; @@ -647,4 +648,152 @@ describe.each(repositoryImplementations)('$name', (holder) => { }); }); + describe("SHUTTLE_UPDATED event", () => { + test("emits SHUTTLE_UPDATED event when shuttles are added or updated", async () => { + const mockListener = jest.fn(); + repository.on(ShuttleRepositoryEvent.SHUTTLE_UPDATED, mockListener); + + const mockShuttles = generateMockShuttles(); + for (const shuttle of mockShuttles) { + await repository.addOrUpdateShuttle(shuttle); + } + + expect(mockListener).toHaveBeenCalledTimes(mockShuttles.length); + expect(mockListener).toHaveBeenCalledWith(mockShuttles[0]); + expect(mockListener).toHaveBeenCalledWith(mockShuttles[mockShuttles.length - 1]); + }); + + test("does not notify listener after it has been removed", async () => { + const mockListener = jest.fn(); + repository.on(ShuttleRepositoryEvent.SHUTTLE_UPDATED, mockListener); + + const mockShuttles = generateMockShuttles(); + + repository.off(ShuttleRepositoryEvent.SHUTTLE_UPDATED, mockListener); + await repository.addOrUpdateShuttle(mockShuttles[0]); + expect(mockListener).toHaveBeenCalledTimes(0); + }); + + test("stops notifying specific listener after removal but continues for others", async () => { + const mockListener1 = jest.fn(); + const mockListener2 = jest.fn(); + repository.on(ShuttleRepositoryEvent.SHUTTLE_UPDATED, mockListener1); + repository.on(ShuttleRepositoryEvent.SHUTTLE_UPDATED, mockListener2); + + const mockShuttles = generateMockShuttles(); + await repository.addOrUpdateShuttle(mockShuttles[0]); + + repository.off(ShuttleRepositoryEvent.SHUTTLE_UPDATED, mockListener1); + + await repository.addOrUpdateShuttle(mockShuttles[mockShuttles.length - 1]); + + expect(mockListener1).toHaveBeenCalledTimes(1); + expect(mockListener1).toHaveBeenCalledWith(mockShuttles[0]); + expect(mockListener1).not.toHaveBeenCalledWith(mockShuttles[mockShuttles.length - 1]); + + expect(mockListener2).toHaveBeenCalledTimes(2); + expect(mockListener2).toHaveBeenCalledWith(mockShuttles[0]); + expect(mockListener2).toHaveBeenCalledWith(mockShuttles[mockShuttles.length - 1]); + }); + }); + + describe("SHUTTLE_REMOVED event", () => { + test("emits SHUTTLE_REMOVED event when a shuttle is removed", async () => { + const mockShuttles = generateMockShuttles(); + const shuttleToRemove = mockShuttles[0]; + const listener = jest.fn(); + repository.on(ShuttleRepositoryEvent.SHUTTLE_REMOVED, listener); + + await repository.addOrUpdateShuttle(shuttleToRemove); + await repository.removeShuttleIfExists(shuttleToRemove.id); + + expect(listener).toHaveBeenCalledTimes(1); + expect(listener).toHaveBeenCalledWith(shuttleToRemove); + }); + }); + + describe("SHUTTLE_ARRIVED_AT_STOP event", () => { + test("emits SHUTTLE_ARRIVED_AT_STOP event when shuttle arrives at a stop", async () => { + const { route, systemId, stop1 } = await setupRouteAndOrderedStops(); + + const listener = jest.fn(); + repository.on(ShuttleRepositoryEvent.SHUTTLE_ARRIVED_AT_STOP, listener); + + const shuttle = { + id: "sh1", + name: "Shuttle 1", + routeId: route.id, + systemId: systemId, + coordinates: stop1.coordinates, + orientationInDegrees: 0, + updatedTime: new Date(), + }; + + const arrivalTime = new Date("2024-01-15T10:30:00Z"); + await repository.addOrUpdateShuttle(shuttle, arrivalTime.getTime()); + + expect(listener).toHaveBeenCalledTimes(1); + const emittedPayload = listener.mock.calls[0][0] as any; + expect(emittedPayload.shuttleId).toBe(shuttle.id); + expect(emittedPayload.stopId).toBe(stop1.id); + expect(emittedPayload.timestamp.getTime()).toBe(arrivalTime); + }); + + test("does not emit event when shuttle is not at a stop", async () => { + const { route, systemId } = await setupRouteAndOrderedStops(); + + const listener = jest.fn(); + repository.on(ShuttleRepositoryEvent.SHUTTLE_ARRIVED_AT_STOP, listener); + + const shuttle = { + id: "sh1", + name: "Shuttle 1", + routeId: route.id, + systemId: systemId, + coordinates: { latitude: 12.5, longitude: 22.5 }, // Not at any stop + orientationInDegrees: 0, + updatedTime: new Date(), + }; + + await repository.addOrUpdateShuttle(shuttle); + + expect(listener).toHaveBeenCalledTimes(0); + }); + + test("emits multiple events as shuttle visits multiple stops", async () => { + const { route, systemId, stop1, stop2 } = await setupRouteAndOrderedStops(); + + const listener = jest.fn(); + repository.on(ShuttleRepositoryEvent.SHUTTLE_ARRIVED_AT_STOP, listener); + + const shuttle = { + id: "sh1", + name: "Shuttle 1", + routeId: route.id, + systemId: systemId, + coordinates: stop1.coordinates, + orientationInDegrees: 0, + updatedTime: new Date(), + }; + + const firstArrivalTime = new Date("2024-01-15T10:30:00Z"); + await repository.addOrUpdateShuttle(shuttle, firstArrivalTime.getTime()); + + shuttle.coordinates = stop2.coordinates; + const secondArrivalTime = new Date("2024-01-15T10:35:00Z"); + await repository.addOrUpdateShuttle(shuttle, secondArrivalTime.getTime()); + + expect(listener).toHaveBeenCalledTimes(2); + + const firstPayload = listener.mock.calls[0][0] as any; + expect(firstPayload.shuttleId).toBe(shuttle.id); + expect(firstPayload.stopId).toBe(stop1.id); + expect(firstPayload.timestamp.getTime()).toBe(firstArrivalTime.getTime()); + + const secondPayload = listener.mock.calls[1][0] as any; + expect(secondPayload.shuttleId).toBe(shuttle.id); + expect(secondPayload.stopId).toBe(stop2.id); + expect(secondPayload.timestamp.getTime()).toBe(secondArrivalTime.getTime()); + }); + }); });