import { beforeEach, describe, expect, jest, test } from "@jest/globals"; import { UnoptimizedInMemoryShuttleRepository } from "../../src/repositories/UnoptimizedInMemoryShuttleRepository"; import { generateMockEtas, generateMockOrderedStops, generateMockRoutes, generateMockShuttles, generateMockStops, } from "../testHelpers/mockDataGenerators"; // For repositories created in the future, reuse core testing // logic from here and differentiate setup (e.g. creating mocks) // Do this by creating a function which takes a ShuttleGetterRepository // or ShuttleGetterSetterRepository instance describe("UnoptimizedInMemoryRepository", () => { let repository: UnoptimizedInMemoryShuttleRepository; beforeEach(() => { repository = new UnoptimizedInMemoryShuttleRepository(); }); describe("getStopsBySystemId", () => { test("gets stops by system ID", async () => { const mockStops = generateMockStops(); for (const stop of mockStops) { await repository.addOrUpdateStop(stop); } const result = await repository.getStopsBySystemId("sys1"); expect(result).toEqual(mockStops.filter((stop) => stop.systemId === "sys1")); }); test("returns an empty list if there are no stops for the given system ID", async () => { const result = await repository.getStopsBySystemId("nonexistent-system"); expect(result).toEqual([]); }); }); describe("getStopById", () => { test("gets a stop by ID if it exists", async () => { const mockStops = generateMockStops(); const mockStop = mockStops[0]; await repository.addOrUpdateStop(mockStop); const result = await repository.getStopById("st1"); expect(result).toEqual(mockStop); }); test("returns null if the stop does not exist", async () => { const result = await repository.getStopById("nonexistent-stop"); expect(result).toBeNull(); }); }); describe("getRoutesBySystemId", () => { test("gets all routes for a specific system ID", async () => { const mockRoutes = generateMockRoutes(); for (const route of mockRoutes) { await repository.addOrUpdateRoute(route); } const result = await repository.getRoutesBySystemId("sys1"); expect(result).toEqual(mockRoutes.filter((route) => route.systemId === "sys1")); }); test("returns an empty list if there are no routes for the system ID", async () => { const result = await repository.getRoutesBySystemId("nonexistent-system"); expect(result).toEqual([]); }); }); describe("getRouteById", () => { test("gets a route by ID if it exists", async () => { const mockRoutes = generateMockRoutes(); const mockRoute = mockRoutes[0]; await repository.addOrUpdateRoute(mockRoute); const result = await repository.getRouteById("r1"); expect(result).toEqual(mockRoute); }); test("returns null if the route does not exist", async () => { const result = await repository.getRouteById("nonexistent-route"); expect(result).toBeNull(); }); }); describe("getShuttlesBySystemId", () => { test("gets all shuttles for a specific system ID", async () => { const mockShuttles = generateMockShuttles(); for (const shuttle of mockShuttles) { await repository.addOrUpdateShuttle(shuttle); } const result = await repository.getShuttlesBySystemId("sys1"); expect(result).toEqual(mockShuttles.filter((sh) => sh.systemId === "sys1")); }); test("returns an empty list if there are no shuttles for the system ID", async () => { const result = await repository.getShuttlesBySystemId("nonexistent-system"); expect(result).toEqual([]); }); }); describe("getShuttlesByRouteId", () => { test("gets all shuttles for a specific route ID", async () => { const mockShuttles = generateMockShuttles(); for (const shuttle of mockShuttles) { await repository.addOrUpdateShuttle(shuttle); } const result = await repository.getShuttlesByRouteId("r1"); expect(result).toEqual(mockShuttles.filter((sh) => sh.routeId === "r1")); }); test("returns an empty list if there are no shuttles for the route ID", async () => { const result = await repository.getShuttlesByRouteId("nonexistent-route"); expect(result).toEqual([]); }); }); describe("getShuttleById", () => { test("gets a shuttle by ID if it exists", async () => { const mockShuttles = generateMockShuttles(); for (const shuttle of mockShuttles) { await repository.addOrUpdateShuttle(shuttle); } const result = await repository.getShuttleById("sh2"); expect(result).toEqual(mockShuttles[1]); }); test("returns null if the shuttle doesn't exist", async () => { const result = await repository.getShuttleById("nonexistent-id"); expect(result).toBeNull(); }); }); describe("getEtasForShuttleId", () => { test("gets ETAs for a specific shuttle ID", async () => { const mockEtas = generateMockEtas(); for (const eta of mockEtas) { await repository.addOrUpdateEta(eta); } const result = await repository.getEtasForShuttleId("sh1"); expect(result).toEqual(mockEtas.filter((eta) => eta.shuttleId === "sh1")); }); test("returns an empty list if there are no ETAs for the shuttle ID", async () => { const result = await repository.getEtasForShuttleId("nonexistent-shuttle"); expect(result).toEqual([]); }); }); describe("getEtasForStopId", () => { test("gets ETAs for a specific stop ID", async () => { const mockEtas = generateMockEtas(); for (const eta of mockEtas) { await repository.addOrUpdateEta(eta); } const result = await repository.getEtasForStopId("st1"); expect(result).toEqual(mockEtas.filter((eta) => eta.stopId === "st1")); }); test("returns an empty list if there are no ETAs for the stop ID", async () => { const result = await repository.getEtasForStopId("nonexistent-stop"); expect(result).toEqual([]); }); }); describe("getEtaForShuttleAndStopId", () => { test("gets a single ETA for a specific shuttle and stop ID", async () => { const mockEtas = generateMockEtas(); const mockEta = mockEtas[0]; await repository.addOrUpdateEta(mockEta); const result = await repository.getEtaForShuttleAndStopId("sh1", "st1"); expect(result).toEqual(mockEta); }); test("returns null if no ETA matches the shuttle and stop ID", async () => { const result = await repository.getEtaForShuttleAndStopId("nonexistent-shuttle", "nonexistent-stop"); expect(result).toBeNull(); }); }); describe("subscribeToEtaChanges", () => { test("notifies listeners if etas have been added or changed", async () => { const mockCallback = jest.fn(); // Jest mock function to simulate a listener repository.subscribeToEtaUpdates(mockCallback); const mockEtas = generateMockEtas(); for (const eta of mockEtas) { await repository.addOrUpdateEta(eta); // Trigger changes in ETAs } expect(mockCallback).toHaveBeenCalledTimes(mockEtas.length); expect(mockCallback).toHaveBeenCalledWith(mockEtas[0]); // First notification expect(mockCallback).toHaveBeenCalledWith(mockEtas[mockEtas.length - 1]); // Last notification }); }); describe("unsubscribeFromEtaChanges", () => { test("stops notifying listeners after etas have stopped changing", async () => { const mockCallback = jest.fn(); // Jest mock function to simulate a listener repository.subscribeToEtaUpdates(mockCallback); const mockEtas = generateMockEtas(); await repository.addOrUpdateEta(mockEtas[0]); repository.unsubscribeFromEtaUpdates(mockCallback); await repository.addOrUpdateEta(mockEtas[mockEtas.length - 1]); expect(mockCallback).toHaveBeenCalledTimes(1); expect(mockCallback).toHaveBeenCalledWith(mockEtas[0]); // First notification expect(mockCallback).not.toHaveBeenCalledWith(mockEtas[mockEtas.length - 1]); // Last notification }); test("does nothing if the listener doesn't exist", async () => { const mockCallback = jest.fn(); repository.subscribeToEtaUpdates(mockCallback); const mockEtas = generateMockEtas(); repository.unsubscribeFromEtaUpdates(() => {}); await repository.addOrUpdateEta(mockEtas[0]); expect(mockCallback).toHaveBeenCalledTimes(1); }); }); describe("getOrderedStopByRouteAndStopId", () => { test("gets an ordered stop by route ID and stop ID", async () => { const mockOrderedStops = generateMockOrderedStops(); for (const orderedStop of mockOrderedStops) { await repository.addOrUpdateOrderedStop(orderedStop); } const mockOrderedStop = mockOrderedStops[0]; const { routeId, stopId } = mockOrderedStop; const result = await repository.getOrderedStopByRouteAndStopId(routeId, stopId); expect(result).toEqual(mockOrderedStop); }); test("returns null if no ordered stop matches the given route ID and stop ID", async () => { const result = await repository.getOrderedStopByRouteAndStopId("nonexistent-route", "nonexistent-stop"); expect(result).toBeNull(); }); }); describe("getOrderedStopsByStopId", () => { test("gets all ordered stops for a specific stop ID", async () => { const mockOrderedStops = generateMockOrderedStops(); for (const orderedStop of mockOrderedStops) { await repository.addOrUpdateOrderedStop(orderedStop); } const result = await repository.getOrderedStopsByStopId("st1"); expect(result).toEqual(mockOrderedStops.filter((os) => os.stopId === "st1")); }); test("returns an empty list if there are no ordered stops for the stop ID", async () => { const result = await repository.getOrderedStopsByStopId("nonexistent-stop"); expect(result).toEqual([]); }); }); describe("getOrderedStopsByRouteId", () => { test("gets all ordered stops for a specific route ID", async () => { const mockOrderedStops = generateMockOrderedStops(); for (const orderedStop of mockOrderedStops) { await repository.addOrUpdateOrderedStop(orderedStop); } const result = await repository.getOrderedStopsByRouteId("r1"); expect(result).toEqual(mockOrderedStops.filter((os) => os.routeId === "r1")); }); test("returns an empty list if there are no ordered stops for the route ID", async () => { const result = await repository.getOrderedStopsByRouteId("nonexistent-route"); expect(result).toEqual([]); }); }); describe("addOrUpdateRoute", () => { test("adds a new route if nonexistent", async () => { const mockRoutes = generateMockRoutes(); const newRoute = mockRoutes[0]; await repository.addOrUpdateRoute(newRoute); const result = await repository.getRoutesBySystemId("sys1"); expect(result).toEqual([newRoute]); }); test("updates an existing route if it exists", async () => { const mockRoutes = generateMockRoutes(); const existingRoute = mockRoutes[0]; const updatedRoute = structuredClone(existingRoute); updatedRoute.name = "Updated Route"; await repository.addOrUpdateRoute(existingRoute); await repository.addOrUpdateRoute(updatedRoute); const result = await repository.getRoutesBySystemId("sys1"); expect(result).toEqual([updatedRoute]); }); }); describe("addOrUpdateShuttle", () => { test("adds a new shuttle if nonexistent", async () => { const mockShuttles = generateMockShuttles(); const newShuttle = mockShuttles[0]; await repository.addOrUpdateShuttle(newShuttle); const result = await repository.getShuttlesBySystemId("sys1"); expect(result).toEqual([newShuttle]); }); test("updates an existing shuttle if it exists", async () => { const mockShuttles = generateMockShuttles(); const existingShuttle = mockShuttles[0]; const updatedShuttle = structuredClone(existingShuttle); updatedShuttle.name = "Updated Shuttle"; await repository.addOrUpdateShuttle(existingShuttle); await repository.addOrUpdateShuttle(updatedShuttle); const result = await repository.getShuttlesBySystemId("sys1"); expect(result).toEqual([updatedShuttle]); }); }); describe("addOrUpdateStop", () => { test("adds a new stop if nonexistent", async () => { const mockStops = generateMockStops(); const newStop = mockStops[0]; await repository.addOrUpdateStop(newStop); const result = await repository.getStopsBySystemId("sys1"); expect(result).toEqual([newStop]); }); test("updates an existing stop if it exists", async () => { const mockStops = generateMockStops(); const existingStop = mockStops[0]; const updatedStop = structuredClone(existingStop); updatedStop.name = "Updated Stop"; await repository.addOrUpdateStop(existingStop); await repository.addOrUpdateStop(updatedStop); const result = await repository.getStopsBySystemId("sys1"); expect(result).toEqual([updatedStop]); }); }); describe("addOrUpdateOrderedStop", () => { test("adds a new ordered stop if nonexistent", async () => { const mockOrderedStops = generateMockOrderedStops(); const newOrderedStop = mockOrderedStops[0]; await repository.addOrUpdateOrderedStop(newOrderedStop); const result = await repository.getOrderedStopsByRouteId(newOrderedStop.routeId); expect(result).toEqual([newOrderedStop]); }); test("updates an existing ordered stop if it exists", async () => { const mockOrderedStops = generateMockOrderedStops(); const existingOrderedStop = mockOrderedStops[0]; const updatedOrderedStop = structuredClone(existingOrderedStop); updatedOrderedStop.position = 5; await repository.addOrUpdateOrderedStop(existingOrderedStop); await repository.addOrUpdateOrderedStop(updatedOrderedStop); const result = await repository.getOrderedStopsByRouteId(existingOrderedStop.routeId); expect(result).toEqual([updatedOrderedStop]); }); }); describe("addOrUpdateEta", () => { test("adds a new ETA if nonexistent", async () => { const mockEtas = generateMockEtas(); const newEta = mockEtas[0]; await repository.addOrUpdateEta(newEta); const result = await repository.getEtasForShuttleId(newEta.shuttleId); expect(result).toEqual([newEta]); }); test("updates an existing ETA if it exists", async () => { const mockEtas = generateMockEtas(); const existingEta = mockEtas[0]; const updatedEta = structuredClone(existingEta); updatedEta.secondsRemaining = existingEta.secondsRemaining + 60; await repository.addOrUpdateEta(existingEta); await repository.addOrUpdateEta(updatedEta); const result = await repository.getEtasForShuttleId(existingEta.shuttleId); expect(result).toEqual([updatedEta]); }); }); describe("removeRouteIfExists", () => { test("removes route given ID", async () => { const systemId = "1"; const mockRoutes = generateMockRoutes(); await Promise.all(mockRoutes.map(async (route) => { route.systemId = systemId; await repository.addOrUpdateRoute(route); })); const routeToRemove = mockRoutes[0]; await repository.removeRouteIfExists(routeToRemove.id); const remainingRoutes = await repository.getRoutesBySystemId(systemId); expect(remainingRoutes).toHaveLength(mockRoutes.length - 1); }); test("does nothing if route doesn't exist", async () => { const systemId = "1"; const mockRoutes = generateMockRoutes(); await Promise.all(mockRoutes.map(async (route) => { route.systemId = systemId; await repository.addOrUpdateRoute(route); })); await repository.removeRouteIfExists("nonexistent-id"); const remainingRoutes = await repository.getRoutesBySystemId(systemId); expect(remainingRoutes).toHaveLength(mockRoutes.length); }); }); describe("removeShuttleIfExists", () => { test("removes shuttle given ID", async () => { const systemId = "1"; const mockShuttles = generateMockShuttles(); await Promise.all(mockShuttles.map(async (shuttle) => { shuttle.systemId = systemId; await repository.addOrUpdateShuttle(shuttle); })); const shuttleToRemove = mockShuttles[0]; await repository.removeShuttleIfExists(shuttleToRemove.id); const remainingShuttles = await repository.getShuttlesBySystemId(systemId); expect(remainingShuttles).toHaveLength(mockShuttles.length - 1); }); test("does nothing if shuttle doesn't exist", async () => { const systemId = "1"; const mockShuttles = generateMockShuttles(); await Promise.all(mockShuttles.map(async (shuttle) => { shuttle.systemId = systemId; await repository.addOrUpdateShuttle(shuttle); })); await repository.removeShuttleIfExists("nonexistent-id"); const remainingShuttles = await repository.getShuttlesBySystemId(systemId); expect(remainingShuttles).toHaveLength(mockShuttles.length); }); }); describe("removeStopIfExists", () => { test("removes stop given ID", async () => { const systemId = "1"; const mockStops = generateMockStops(); await Promise.all(mockStops.map(async (stop) => { stop.systemId = systemId; await repository.addOrUpdateStop(stop); })); const stopToRemove = mockStops[0]; await repository.removeStopIfExists(stopToRemove.id); const remainingStops = await repository.getStopsBySystemId(systemId); expect(remainingStops).toHaveLength(mockStops.length - 1); }); test("does nothing if stop doesn't exist", async () => { const systemId = "1"; const mockStops = generateMockStops(); await Promise.all(mockStops.map(async (stop) => { stop.systemId = systemId; await repository.addOrUpdateStop(stop); })); await repository.removeStopIfExists("nonexistent-id"); const remainingStops = await repository.getStopsBySystemId(systemId); expect(remainingStops).toHaveLength(mockStops.length); }); }); describe("removeOrderedStopIfExists", () => { test("removes ordered stop given stop ID and route ID", async () => { let mockOrderedStops = generateMockOrderedStops(); const routeId = mockOrderedStops[0].routeId; mockOrderedStops = mockOrderedStops.filter((orderedStop) => orderedStop.routeId === routeId); await Promise.all(mockOrderedStops.map(async (stop) => { stop.routeId = routeId; await repository.addOrUpdateOrderedStop(stop); })); const orderedStopToRemove = mockOrderedStops[0]; await repository.removeOrderedStopIfExists(orderedStopToRemove.stopId, orderedStopToRemove.routeId); const remainingOrderedStops = await repository.getOrderedStopsByRouteId(routeId); expect(remainingOrderedStops).toHaveLength(mockOrderedStops.length - 1); }); test("does nothing if ordered stop doesn't exist", async () => { let mockOrderedStops = generateMockOrderedStops(); const routeId = mockOrderedStops[0].routeId; mockOrderedStops = mockOrderedStops.filter((orderedStop) => orderedStop.routeId === routeId); await Promise.all(mockOrderedStops.map(async (stop) => { stop.routeId = routeId; await repository.addOrUpdateOrderedStop(stop); })); await repository.removeOrderedStopIfExists("nonexistent-stop-id", "nonexistent-route-id"); const remainingOrderedStops = await repository.getOrderedStopsByRouteId(routeId); expect(remainingOrderedStops).toHaveLength(mockOrderedStops.length); }); }); describe("removeEtaIfExists", () => { test("removes eta given shuttle ID and stop ID", async () => { let mockEtas = generateMockEtas(); const stopId = mockEtas[0].stopId; mockEtas = mockEtas.filter((eta) => eta.stopId === stopId); await Promise.all(mockEtas.map(async (eta) => { eta.stopId = stopId; await repository.addOrUpdateEta(eta); })); const etaToRemove = mockEtas[0]; await repository.removeEtaIfExists(etaToRemove.shuttleId, etaToRemove.stopId); const remainingEtas = await repository.getEtasForStopId(stopId); expect(remainingEtas).toHaveLength(mockEtas.length - 1); }); test("does nothing if eta doesn't exist", async () => { let mockEtas = generateMockEtas(); const stopId = mockEtas[0].stopId; mockEtas = mockEtas.filter((eta) => eta.stopId === stopId); await Promise.all(mockEtas.map(async (eta) => { eta.stopId = stopId; await repository.addOrUpdateEta(eta); })); await repository.removeEtaIfExists("nonexistent-shuttle-id", "nonexistent-stop-id"); const remainingEtas = await repository.getEtasForStopId(stopId); expect(remainingEtas).toHaveLength(mockEtas.length); }); }); describe("clearShuttleData", () => { test("clears all shuttles from the repository", async () => { const mockShuttles = generateMockShuttles(); for (const shuttle of mockShuttles) { await repository.addOrUpdateShuttle(shuttle); } await repository.clearShuttleData(); const result = await repository.getShuttlesBySystemId("sys1"); expect(result).toEqual([]); }); }); describe("clearEtaData", () => { test("clears all ETAs from the repository", async () => { const mockEtas = generateMockEtas(); for (const eta of mockEtas) { await repository.addOrUpdateEta(eta); } await repository.clearEtaData(); const result = await repository.getEtasForShuttleId("shuttle1"); expect(result).toEqual([]); }); }); describe("clearOrderedStopData", () => { test("clears all ordered stops from the repository", async () => { const mockOrderedStops = await generateMockOrderedStops(); for (const system of mockOrderedStops) { await repository.addOrUpdateOrderedStop(system); } await repository.clearOrderedStopData(); const result = await repository.getOrderedStopsByRouteId("route1"); expect(result).toEqual([]); }); }); describe("clearRouteData", () => { test("clears all routes from the repository", async () => { const mockRoutes = generateMockRoutes(); for (const route of mockRoutes) { await repository.addOrUpdateRoute(route); } await repository.clearRouteData(); const result = await repository.getRoutesBySystemId("sys1"); expect(result).toEqual([]); }); }); describe("clearStopData", () => { test("clears all stops from the repository", async () => { const mockStops = generateMockStops(); for (const stop of mockStops) { await repository.addOrUpdateStop(stop); } await repository.clearStopData(); const result = await repository.getStopsBySystemId("sys1"); expect(result).toEqual([]); }); }); });