diff --git a/src/repositories/shuttle/eta/__tests__/ExternalSourceETARepositorySharedTests.test.ts b/src/repositories/shuttle/eta/__tests__/ExternalSourceETARepositorySharedTests.test.ts new file mode 100644 index 0000000..4dc4980 --- /dev/null +++ b/src/repositories/shuttle/eta/__tests__/ExternalSourceETARepositorySharedTests.test.ts @@ -0,0 +1,151 @@ +import { afterEach, beforeEach, describe, expect, test } from "@jest/globals"; +import { RepositoryHolder } from "../../../../../testHelpers/RepositoryHolder"; +import { ExternalSourceETARepository } from "../ExternalSourceETARepository"; +import { RedisExternalSourceETARepository } from "../RedisExternalSourceETARepository"; +import { generateMockEtas } from "../../../../../testHelpers/mockDataGenerators"; + +class RedisExternalSourceETARepositoryHolder implements RepositoryHolder { + repo: RedisExternalSourceETARepository | undefined; + + name = "RedisExternalSourceETARepository" + factory = async () => { + this.repo = new RedisExternalSourceETARepository(); + await this.repo.connect(); + return this.repo; + } + teardown = async () => { + if (this.repo) { + await this.repo.clearAllData(); + await this.repo.disconnect(); + } + } +} + +const repositoryImplementations = [ + new RedisExternalSourceETARepositoryHolder() +]; + +describe.each(repositoryImplementations)('$name', (holder) => { + let repository: ExternalSourceETARepository; + + beforeEach(async () => { + repository = await holder.factory(); + }); + + afterEach(async () => { + await holder.teardown(); + }); + + describe("addOrUpdateEtaFromExternalSource", () => { + test("adds a new ETA if nonexistent", async () => { + const mockEtas = generateMockEtas(); + const newEta = mockEtas[0]; + + await repository.addOrUpdateEtaFromExternalSource(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.addOrUpdateEtaFromExternalSource(existingEta); + await repository.addOrUpdateEtaFromExternalSource(updatedEta); + + const result = await repository.getEtasForShuttleId(existingEta.shuttleId); + expect(result).toEqual([updatedEta]); + }); + }); + + describe("getEtasForShuttleId", () => { + test("gets ETAs for a specific shuttle ID", async () => { + const mockEtas = generateMockEtas(); + for (const eta of mockEtas) { + await repository.addOrUpdateEtaFromExternalSource(eta); + } + + const result = await repository.getEtasForShuttleId("sh1"); + const expected = mockEtas.filter((eta) => eta.shuttleId === "sh1"); + expect(result).toHaveLength(expected.length); + expect(result).toEqual(expect.arrayContaining(expected)); + }); + + 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.addOrUpdateEtaFromExternalSource(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.addOrUpdateEtaFromExternalSource(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("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.addOrUpdateEtaFromExternalSource(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.addOrUpdateEtaFromExternalSource(eta); + })); + + await repository.removeEtaIfExists("nonexistent-shuttle-id", "nonexistent-stop-id"); + + const remainingEtas = await repository.getEtasForStopId(stopId); + expect(remainingEtas).toHaveLength(mockEtas.length); + }); + }); +}) +