diff --git a/test/generators.ts b/test/generators.ts new file mode 100644 index 0000000..eac524a --- /dev/null +++ b/test/generators.ts @@ -0,0 +1,53 @@ +import { IEta, IOrderedStop, IRoute, IShuttle, IStop, ISystem } from "../src/entities/entities"; + +// Use a single set of generators in case any of the +// interfaces change in the future + +export function generateMockSystems(): ISystem[] { + return [ + { id: "1", name: "System A" }, + { id: "2", name: "System B" }, + { id: "3", name: "System C" }, + ]; +} + +export function generateMockShuttles(): IShuttle[] { + return [ + { id: "sh1", name: "Shuttle A", routeId: "r1", systemId: "sys1", coordinates: { latitude: 10, longitude: 20 } }, + { id: "sh2", name: "Shuttle B", routeId: "r2", systemId: "sys2", coordinates: { latitude: 15, longitude: 25 } }, + { id: "sh3", name: "Shuttle C", routeId: "r3", systemId: "sys3", coordinates: { latitude: 30, longitude: 40 } }, + ]; +} + +export function generateMockRoutes(): IRoute[] { + return [ + { id: "r1", name: "Route 1", color: "red", systemId: "sys1", polylineCoordinates: [] }, + { id: "r2", name: "Route 2", color: "blue", systemId: "sys2", polylineCoordinates: [] }, + { id: "r3", name: "Route 3", color: "green", systemId: "sys3", polylineCoordinates: [] }, + ]; +} + +export function generateMockStops(): IStop[] { + return [ + { id: "st1", name: "Stop A", systemId: "sys1", coordinates: { latitude: 10, longitude: 20 } }, + { id: "st2", name: "Stop B", systemId: "sys2", coordinates: { latitude: 15, longitude: 25 } }, + { id: "st3", name: "Stop C", systemId: "sys3", coordinates: { latitude: 30, longitude: 40 } }, + ]; +} + +export function generateMockOrderedStops(): IOrderedStop[] { + return [ + { stopId: "st1", routeId: "r1", position: 1 }, + { stopId: "st1", routeId: "r2", position: 2 }, + { stopId: "st2", routeId: "r1", position: 3 }, + { stopId: "st2", routeId: "r2", position: 4 }, + ]; +} + +export function generateMockEtas(): IEta[] { + return [ + { shuttleId: "sh1", stopId: "st1", secondsRemaining: 120 }, + { shuttleId: "sh1", stopId: "st2", secondsRemaining: 180 }, + { shuttleId: "sh2", stopId: "st3", secondsRemaining: 240 }, + ]; +} \ No newline at end of file diff --git a/test/repositories/UnoptimizedInMemoryRepositoryTests.test.ts b/test/repositories/UnoptimizedInMemoryRepositoryTests.test.ts new file mode 100644 index 0000000..e9e8166 --- /dev/null +++ b/test/repositories/UnoptimizedInMemoryRepositoryTests.test.ts @@ -0,0 +1,526 @@ +import { beforeEach, describe, expect, test } from "@jest/globals"; +import { UnoptimizedInMemoryRepository } from "../../src/repositories/UnoptimizedInMemoryRepository"; +import { + generateMockEtas, + generateMockOrderedStops, + generateMockRoutes, + generateMockShuttles, + generateMockStops, + generateMockSystems +} from "../generators"; + +// 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 GetterRepository +// or GetterSetterRepository instance + +describe("UnoptimizedInMemoryRepository", () => { + let repository: UnoptimizedInMemoryRepository; + + beforeEach(() => { + repository = new UnoptimizedInMemoryRepository(); + }); + + describe("getSystems", () => { + test("gets the systems stored in the repository", async () => { + const mockSystems = generateMockSystems(); + for (const system of mockSystems) { + await repository.addOrUpdateSystem(system); + } + + const result = await repository.getSystems(); + + expect(result).toEqual(mockSystems); + }); + + test("gets an empty list if there are no systems stored", async () => { + const result = await repository.getSystems(); + + expect(result).toEqual([]); + }); + }); + + describe("getSystemById", () => { + test("gets a system by the ID if it exists", async () => { + const mockSystems = generateMockSystems(); + for (const system of mockSystems) { + await repository.addOrUpdateSystem(system); + } + + const result = await repository.getSystemById("2"); + + expect(result).toEqual(mockSystems[1]); // Ensure it retrieves the correct system + }); + + test("returns null if the system doesn't exist", async () => { + const result = await repository.getSystemById("nonexistent-id"); + + expect(result).toBeNull(); + }); + }); + + 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("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("addOrUpdateSystem", () => { + test("adds a new system if nonexistent", async () => { + const mockSystems = generateMockSystems(); + const newSystem = mockSystems[0]; + + await repository.addOrUpdateSystem(newSystem); + + const result = await repository.getSystems(); + expect(result).toEqual([newSystem]); + }); + + test("updates an existing system if it exists", async () => { + const mockSystems = generateMockSystems(); + const existingSystem = mockSystems[0]; + const updatedSystem = structuredClone(existingSystem); + updatedSystem.name = "Updated System"; + + await repository.addOrUpdateSystem(existingSystem); + await repository.addOrUpdateSystem(updatedSystem); + + const result = await repository.getSystems(); + expect(result).toEqual([updatedSystem]); + }); + }); + + 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("clearSystemData", () => { + test("clears all systems from the repository", async () => { + const mockSystems = generateMockSystems(); + for (const system of mockSystems) { + await repository.addOrUpdateSystem(system); + } + + await repository.clearSystemData(); + + const result = await repository.getSystems(); + expect(result).toEqual([]); + }); + + test("clears system data when the repository has data", async () => { + const mockSystems = generateMockSystems(); + const system = mockSystems[0]; + await repository.addOrUpdateSystem(system); + + await repository.clearSystemData(); + + const result = await repository.getSystems(); + expect(result).toEqual([]); + }); + }); + + 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([]); + }); + }); +}); \ No newline at end of file