diff --git a/schema.graphqls b/schema.graphqls index 6db52e7..8449ef8 100644 --- a/schema.graphqls +++ b/schema.graphqls @@ -13,6 +13,7 @@ type System { type Route { name: String! id: ID! + systemId: ID! orderedStop(forStopId: ID): OrderedStop shuttles: [Shuttle!] polylineCoordinates: [Coordinates!]! @@ -26,10 +27,12 @@ type OrderedStop { routeId: ID! stop: Stop stopId: ID! + systemId: ID! } type Stop { id: ID! + systemId: ID! name: String! coordinates: Coordinates! etas: [ETA!] @@ -47,10 +50,12 @@ type ETA { shuttle: Shuttle shuttleId: ID! secondsRemaining: Float! + systemId: ID! } type Shuttle { name: String! + systemId: ID! id: ID!, coordinates: Coordinates! route: Route diff --git a/src/ServerContext.ts b/src/ServerContext.ts index 732f7d8..b55a8b1 100644 --- a/src/ServerContext.ts +++ b/src/ServerContext.ts @@ -1,8 +1,6 @@ -import { ETANotificationScheduler } from "./notifications/schedulers/ETANotificationScheduler"; -import { ShuttleGetterSetterRepository } from "./repositories/ShuttleGetterSetterRepository"; -import { NotificationRepository } from "./repositories/NotificationRepository"; +import { InterchangeSystem } from "./entities/InterchangeSystem"; export interface ServerContext { - shuttleRepository: ShuttleGetterSetterRepository; - notificationRepository: NotificationRepository; + systems: InterchangeSystem[]; + findSystemById: (id: string) => InterchangeSystem | null; } diff --git a/src/entities/InterchangeSystem.ts b/src/entities/InterchangeSystem.ts new file mode 100644 index 0000000..abd045f --- /dev/null +++ b/src/entities/InterchangeSystem.ts @@ -0,0 +1,105 @@ +import { ShuttleRepositoryLoader } from "../loaders/ShuttleRepositoryLoader"; +import { ETANotificationScheduler } from "../notifications/schedulers/ETANotificationScheduler"; +import { TimedApiBasedShuttleRepositoryLoader } from "../loaders/TimedApiBasedShuttleRepositoryLoader"; +import { UnoptimizedInMemoryShuttleRepository } from "../repositories/UnoptimizedInMemoryShuttleRepository"; +import { RedisNotificationRepository } from "../repositories/RedisNotificationRepository"; +import { NotificationRepository } from "../repositories/NotificationRepository"; +import { ShuttleGetterSetterRepository } from "../repositories/ShuttleGetterSetterRepository"; +import { InMemoryNotificationRepository } from "../repositories/InMemoryNotificationRepository"; +import { AppleNotificationSender } from "../notifications/senders/AppleNotificationSender"; +import { ApiBasedShuttleRepositoryLoader } from "../loaders/ApiBasedShuttleRepositoryLoader"; + +export interface InterchangeSystemBuilderArguments { + name: string; + + /** + * ID to identify the system internally and in the API. + */ + id: string; + + /** + * ID for fetching shuttle data from the Passio GO! system. + */ + passioSystemId: string; +} + +export class InterchangeSystem { + constructor( + public name: string, + public id: string, + public shuttleDataLoader: ShuttleRepositoryLoader, + public shuttleRepository: ShuttleGetterSetterRepository, + public notificationScheduler: ETANotificationScheduler, + public notificationRepository: NotificationRepository, + ) { + } + + /** + * Construct an instance of the class where all composited + * classes are correctly linked, meant for use in development and production. + * @param args + */ + static async build( + args: InterchangeSystemBuilderArguments, + ) { + const shuttleRepository = new UnoptimizedInMemoryShuttleRepository(); + const shuttleDataLoader = new TimedApiBasedShuttleRepositoryLoader( + args.passioSystemId, + args.id, + shuttleRepository + ); + await shuttleDataLoader.start(); + + const notificationRepository = new RedisNotificationRepository(); + await notificationRepository.connect(); + const notificationScheduler = new ETANotificationScheduler( + shuttleRepository, + notificationRepository, + new AppleNotificationSender(), + ); + notificationScheduler.startListeningForUpdates(); + + return new InterchangeSystem( + args.name, + args.id, + shuttleDataLoader, + shuttleRepository, + notificationScheduler, + notificationRepository, + ); + } + + /** + * Construct an instance of the class where all composited + * classes are correctly linked, meant for unit tests, and server/app + * integration tests. + * @param args + */ + static buildForTesting( + args: InterchangeSystemBuilderArguments, + ) { + const shuttleRepository = new UnoptimizedInMemoryShuttleRepository(); + const shuttleDataLoader = new ApiBasedShuttleRepositoryLoader( + args.passioSystemId, + args.id, + shuttleRepository + ); + + const notificationRepository = new InMemoryNotificationRepository(); + const notificationScheduler = new ETANotificationScheduler( + shuttleRepository, + notificationRepository, + new AppleNotificationSender(false), + ); + notificationScheduler.startListeningForUpdates(); + + return new InterchangeSystem( + args.name, + args.id, + shuttleDataLoader, + shuttleRepository, + notificationScheduler, + notificationRepository, + ); + } +} diff --git a/src/entities/entities.ts b/src/entities/entities.ts index 2766595..f98faa5 100644 --- a/src/entities/entities.ts +++ b/src/entities/entities.ts @@ -6,7 +6,7 @@ export interface IEntityWithId { id: string; } -export interface ISystem extends IEntityWithId, IEntityWithOptionalTimestamp { +export interface IPassioSystem extends IEntityWithId, IEntityWithOptionalTimestamp { name: string; } @@ -40,6 +40,7 @@ export interface IEta extends IEntityWithOptionalTimestamp { secondsRemaining: number; shuttleId: string; stopId: string; + systemId: string; } export interface IOrderedStop extends IEntityWithOptionalTimestamp { @@ -48,5 +49,6 @@ export interface IOrderedStop extends IEntityWithOptionalTimestamp { routeId: string; stopId: string; position: number; + systemId: string; } diff --git a/src/index.ts b/src/index.ts index de49b21..a370258 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,17 +3,20 @@ import { ApolloServer } from "@apollo/server"; import { startStandaloneServer } from "@apollo/server/standalone"; import { MergedResolvers } from "./MergedResolvers"; import { ServerContext } from "./ServerContext"; -import { UnoptimizedInMemoryShuttleRepository } from "./repositories/UnoptimizedInMemoryShuttleRepository"; -import { TimedApiBasedShuttleRepositoryLoader } from "./loaders/TimedApiBasedShuttleRepositoryLoader"; -import { ETANotificationScheduler } from "./notifications/schedulers/ETANotificationScheduler"; -import { loadShuttleTestData } from "./loaders/loadShuttleTestData"; -import { AppleNotificationSender } from "./notifications/senders/AppleNotificationSender"; -import { InMemoryNotificationRepository } from "./repositories/InMemoryNotificationRepository"; -import { NotificationRepository } from "./repositories/NotificationRepository"; -import { RedisNotificationRepository } from "./repositories/RedisNotificationRepository"; +import { loadShuttleTestData, supportedIntegrationTestSystems } from "./loaders/loadShuttleTestData"; +import { InterchangeSystem, InterchangeSystemBuilderArguments } from "./entities/InterchangeSystem"; const typeDefs = readFileSync("./schema.graphqls", "utf8"); +// In the future this can be stored as a separate file +const supportedSystems: InterchangeSystemBuilderArguments[] = [ + { + id: "1", + passioSystemId: "263", + name: "Chapman University", + } +] + async function main() { const server = new ApolloServer({ typeDefs, @@ -21,39 +24,27 @@ async function main() { introspection: process.env.NODE_ENV !== "production", }); - const shuttleRepository = new UnoptimizedInMemoryShuttleRepository(); - - let notificationRepository: NotificationRepository; - let notificationService: ETANotificationScheduler; + let systems: InterchangeSystem[]; if (process.argv.length > 2 && process.argv[2] == "integration-testing") { console.log("Using integration testing setup") - await loadShuttleTestData(shuttleRepository); - const appleNotificationSender = new AppleNotificationSender(false); - notificationRepository = new InMemoryNotificationRepository(); + systems = await Promise.all(supportedIntegrationTestSystems.map( + async (systemArguments) => { + const system = InterchangeSystem.buildForTesting(systemArguments); - notificationService = new ETANotificationScheduler( - shuttleRepository, - notificationRepository, - appleNotificationSender - ); - notificationService.startListeningForUpdates(); + // TODO: Have loading of different data for different systems in the future + await loadShuttleTestData(system.shuttleRepository); + + return system; + } + )); } else { - const repositoryDataUpdater = new TimedApiBasedShuttleRepositoryLoader( - shuttleRepository, - ); - await repositoryDataUpdater.start(); - - const redisNotificationRepository = new RedisNotificationRepository(); - await redisNotificationRepository.connect(); - - notificationRepository = redisNotificationRepository; - notificationService = new ETANotificationScheduler( - shuttleRepository, - notificationRepository - ); - notificationService.startListeningForUpdates(); + systems = await Promise.all(supportedSystems.map( + async (systemArguments) => { + return await InterchangeSystem.build(systemArguments); + }, + )); } const { url } = await startStandaloneServer(server, { @@ -62,8 +53,14 @@ async function main() { }, context: async () => { return { - shuttleRepository, - notificationRepository, + systems, + findSystemById: (id: string) => { + const system = systems.find((system) => system.id === id); + if (!system) { + return null; + } + return system; + }, } }, }); diff --git a/src/loaders/ApiBasedShuttleRepositoryLoader.ts b/src/loaders/ApiBasedShuttleRepositoryLoader.ts index b8a6285..49031ef 100644 --- a/src/loaders/ApiBasedShuttleRepositoryLoader.ts +++ b/src/loaders/ApiBasedShuttleRepositoryLoader.ts @@ -1,5 +1,5 @@ import { ShuttleGetterSetterRepository } from "../repositories/ShuttleGetterSetterRepository"; -import { IEntityWithId, IEta, IRoute, IShuttle, IStop, ISystem } from "../entities/entities"; +import { IEntityWithId, IEta, IRoute, IShuttle, IStop } from "../entities/entities"; import { ShuttleRepositoryLoader } from "./ShuttleRepositoryLoader"; export class ApiResponseError extends Error { @@ -15,10 +15,11 @@ export class ApiResponseError extends Error { * which inherit from `IEntityWithId`. */ export class ApiBasedShuttleRepositoryLoader implements ShuttleRepositoryLoader { - supportedSystemIds = ["263"]; baseUrl = "https://passiogo.com/mapGetData.php"; constructor( + public passioSystemId: string, + public systemIdForConstructedData: string, public repository: ShuttleGetterSetterRepository, ) { } @@ -32,59 +33,10 @@ export class ApiBasedShuttleRepositoryLoader implements ShuttleRepositoryLoader return ids; } - public async fetchAndUpdateSystemData() { - const params = { - getSystems: "2", - }; - const query = new URLSearchParams(params).toString(); - - const systemIds = await this.constructExistingEntityIdSet(async () => { - return await this.repository.getSystems(); - }) - - try { - const response = await fetch(`${this.baseUrl}?${query}`); - const json = await response.json(); - - if (!response.ok) { - throw new Error(`HTTP error with status ${response.status}`) - } - - if (typeof json.all === "object") { - // filter down to supported systems - const filteredSystems = json.all.filter((jsonSystem: any) => this.supportedSystemIds.includes(jsonSystem.id)); - await Promise.all(filteredSystems.map(async (system: any) => { - const constructedSystem: ISystem = { - id: system.id, - name: system.fullname, - }; - - await this.repository.addOrUpdateSystem(constructedSystem); - systemIds.delete(constructedSystem.id); - })); - } else { - throw new Error("Received JSON object does not contain `all` field") - } - - // Prune systems - await Promise.all(Array.from(systemIds).map(async (systemId) => { - await this.repository.removeSystemIfExists(systemId); - })); - } catch(e: any) { - throw new ApiResponseError(e.message); - } - } - - public async fetchAndUpdateRouteDataForExistingSystemsInRepository() { - const systems = await this.repository.getSystems(); - await Promise.all(systems.map(async (system) => { - await this.fetchAndUpdateRouteDataForSystemId(system.id); - })); - } - - public async fetchAndUpdateRouteDataForSystemId(systemId: string) { + public async fetchAndUpdateRouteDataForSystem() { + const systemId = this.passioSystemId; const routeIdsToPrune = await this.constructExistingEntityIdSet(async () => { - return await this.repository.getRoutesBySystemId(systemId); + return await this.repository.getRoutes(); }); const params = { @@ -114,7 +66,7 @@ export class ApiBasedShuttleRepositoryLoader implements ShuttleRepositoryLoader color: jsonRoute.color, id: jsonRoute.myid, polylineCoordinates: [], - systemId: systemId, + systemId: this.systemIdForConstructedData, }; await this.repository.addOrUpdateRoute(constructedRoute); @@ -131,18 +83,13 @@ export class ApiBasedShuttleRepositoryLoader implements ShuttleRepositoryLoader } } - public async fetchAndUpdateStopAndPolylineDataForRoutesInExistingSystemsInRepository() { - const systems = await this.repository.getSystems(); - await Promise.all(systems.map(async (system: ISystem) => { - await this.fetchAndUpdateStopAndPolylineDataForRoutesWithSystemId(system.id); - })); - } + public async fetchAndUpdateStopAndPolylineDataForRoutesInSystem() { + const passioSystemId = this.passioSystemId; - public async fetchAndUpdateStopAndPolylineDataForRoutesWithSystemId(systemId: string) { // Fetch from the API // Pass JSON output into two different methods to update repository const stopIdsToPrune = await this.constructExistingEntityIdSet(async () => { - return await this.repository.getStopsBySystemId(systemId); + return await this.repository.getStops(); }); const params = { @@ -150,7 +97,7 @@ export class ApiBasedShuttleRepositoryLoader implements ShuttleRepositoryLoader }; const formDataJsonObject = { - "s0": systemId, + "s0": passioSystemId, "sA": 1 }; const formData = new FormData(); @@ -165,7 +112,7 @@ export class ApiBasedShuttleRepositoryLoader implements ShuttleRepositoryLoader }); const json = await response.json(); - await this.updateStopDataForSystemAndApiResponse(systemId, json, stopIdsToPrune); + await this.updateStopDataForSystemAndApiResponse(json, stopIdsToPrune); await this.updateOrderedStopDataForExistingStops(json); await this.updatePolylineDataForExistingRoutesAndApiResponse(json); @@ -177,17 +124,10 @@ export class ApiBasedShuttleRepositoryLoader implements ShuttleRepositoryLoader } } - public async fetchAndUpdateShuttleDataForExistingSystemsInRepository() { - const systems = await this.repository.getSystems(); - await Promise.all(systems.map(async (system: ISystem) => { - const systemId = system.id; - await this.fetchAndUpdateShuttleDataForSystemId(systemId); - })); - } - - public async fetchAndUpdateShuttleDataForSystemId(systemId: string) { + public async fetchAndUpdateShuttleDataForSystem() { + const systemId = this.passioSystemId; const shuttleIdsToPrune = await this.constructExistingEntityIdSet(async () => { - return await this.repository.getShuttlesBySystemId(systemId); + return await this.repository.getShuttles(); }); const params = { @@ -224,7 +164,7 @@ export class ApiBasedShuttleRepositoryLoader implements ShuttleRepositoryLoader longitude: parseFloat(jsonBus.longitude), }, routeId: jsonBus.routeId, - systemId: systemId, + systemId: this.systemIdForConstructedData, id: `${jsonBus.busId}`, orientationInDegrees: parseFloat(jsonBus.calculatedCourse) } @@ -243,16 +183,8 @@ export class ApiBasedShuttleRepositoryLoader implements ShuttleRepositoryLoader } } - public async fetchAndUpdateEtaDataForExistingStopsForSystemsInRepository() { - const systems = await this.repository.getSystems() - await Promise.all(systems.map(async (system: ISystem) => { - const systemId = system.id; - await this.fetchAndUpdateEtaDataForExistingStopsForSystemId(systemId); - })) - } - - public async fetchAndUpdateEtaDataForExistingStopsForSystemId(systemId: string) { - const stops = await this.repository.getStopsBySystemId(systemId); + public async fetchAndUpdateEtaDataForExistingStopsForSystem() { + const stops = await this.repository.getStops(); await Promise.all(stops.map(async (stop) => { let stopId = stop.id; await this.fetchAndUpdateEtaDataForStopId(stopId); @@ -284,6 +216,7 @@ export class ApiBasedShuttleRepositoryLoader implements ShuttleRepositoryLoader shuttleId: `${shuttleId}`, stopId: stopId, millisecondsSinceEpoch: Date.now(), + systemId: this.systemIdForConstructedData, }; this.repository.addOrUpdateEta(eta); @@ -295,7 +228,6 @@ export class ApiBasedShuttleRepositoryLoader implements ShuttleRepositoryLoader } protected async updateStopDataForSystemAndApiResponse( - systemId: string, json: any, setOfIdsToPrune: Set = new Set(), ) { @@ -306,7 +238,7 @@ export class ApiBasedShuttleRepositoryLoader implements ShuttleRepositoryLoader const constructedStop: IStop = { name: stop.name, id: stop.id, - systemId, + systemId: this.systemIdForConstructedData, coordinates: { latitude: parseFloat(stop.latitude), longitude: parseFloat(stop.longitude), @@ -341,6 +273,7 @@ export class ApiBasedShuttleRepositoryLoader implements ShuttleRepositoryLoader routeId, stopId, position: index + 1, + systemId: this.systemIdForConstructedData, }; } @@ -349,6 +282,7 @@ export class ApiBasedShuttleRepositoryLoader implements ShuttleRepositoryLoader routeId, stopId: jsonOrderedStopData[index - 1][1], position: index, + systemId: this.systemIdForConstructedData, }; } if (index < jsonOrderedStopData.length - 1) { @@ -356,6 +290,7 @@ export class ApiBasedShuttleRepositoryLoader implements ShuttleRepositoryLoader routeId, stopId: jsonOrderedStopData[index + 1][1], position: index + 2, + systemId: this.systemIdForConstructedData, }; } diff --git a/src/loaders/ShuttleRepositoryLoader.ts b/src/loaders/ShuttleRepositoryLoader.ts index 9ffa137..588d06f 100644 --- a/src/loaders/ShuttleRepositoryLoader.ts +++ b/src/loaders/ShuttleRepositoryLoader.ts @@ -1,12 +1,7 @@ export interface ShuttleRepositoryLoader { - fetchAndUpdateSystemData(): Promise; - fetchAndUpdateRouteDataForExistingSystemsInRepository(): Promise; - fetchAndUpdateRouteDataForSystemId(systemId: string): Promise; - fetchAndUpdateStopAndPolylineDataForRoutesInExistingSystemsInRepository(): Promise; - fetchAndUpdateStopAndPolylineDataForRoutesWithSystemId(systemId: string): Promise; - fetchAndUpdateShuttleDataForExistingSystemsInRepository(): Promise; - fetchAndUpdateShuttleDataForSystemId(systemId: string): Promise; - fetchAndUpdateEtaDataForExistingStopsForSystemsInRepository(): Promise; - fetchAndUpdateEtaDataForExistingStopsForSystemId(systemId: string): Promise; + fetchAndUpdateRouteDataForSystem(): Promise; + fetchAndUpdateStopAndPolylineDataForRoutesInSystem(): Promise; + fetchAndUpdateShuttleDataForSystem(): Promise; + fetchAndUpdateEtaDataForExistingStopsForSystem(): Promise; fetchAndUpdateEtaDataForStopId(stopId: string): Promise; } diff --git a/src/loaders/TimedApiBasedShuttleRepositoryLoader.ts b/src/loaders/TimedApiBasedShuttleRepositoryLoader.ts index 1d15273..15a7d59 100644 --- a/src/loaders/TimedApiBasedShuttleRepositoryLoader.ts +++ b/src/loaders/TimedApiBasedShuttleRepositoryLoader.ts @@ -22,9 +22,11 @@ export class TimedApiBasedShuttleRepositoryLoader extends ApiBasedShuttleReposit readonly timeout = 10000; constructor( + public passioSystemId: string, + public systemIdForConstructedData: string, repository: ShuttleGetterSetterRepository, ) { - super(repository); + super(passioSystemId, systemIdForConstructedData, repository); this.startFetchDataAndUpdate = this.startFetchDataAndUpdate.bind(this); } @@ -46,15 +48,14 @@ export class TimedApiBasedShuttleRepositoryLoader extends ApiBasedShuttleReposit if (!this.shouldBeRunning) return; try { - await this.fetchAndUpdateSystemData(); - await this.fetchAndUpdateRouteDataForExistingSystemsInRepository(); - await this.fetchAndUpdateStopAndPolylineDataForRoutesInExistingSystemsInRepository(); - await this.fetchAndUpdateShuttleDataForExistingSystemsInRepository(); + await this.fetchAndUpdateRouteDataForSystem(); + await this.fetchAndUpdateStopAndPolylineDataForRoutesInSystem(); + await this.fetchAndUpdateShuttleDataForSystem(); // Because ETA method doesn't support pruning yet, // add a call to the clear method here await this.repository.clearEtaData(); - await this.fetchAndUpdateEtaDataForExistingStopsForSystemsInRepository(); + await this.fetchAndUpdateEtaDataForExistingStopsForSystem(); } catch (e) { console.error(e); } diff --git a/src/loaders/loadShuttleTestData.ts b/src/loaders/loadShuttleTestData.ts index 76cce74..c59b4bf 100644 --- a/src/loaders/loadShuttleTestData.ts +++ b/src/loaders/loadShuttleTestData.ts @@ -1,11 +1,13 @@ // Mock data -import { IEta, IOrderedStop, IRoute, IShuttle, IStop, ISystem } from "../entities/entities"; +import { IEta, IOrderedStop, IRoute, IShuttle, IStop, IPassioSystem } from "../entities/entities"; import { ShuttleGetterSetterRepository } from "../repositories/ShuttleGetterSetterRepository"; +import { InterchangeSystemBuilderArguments } from "../entities/InterchangeSystem"; -const systems: ISystem[] = [ +export const supportedIntegrationTestSystems: InterchangeSystemBuilderArguments[] = [ { id: "1", name: "Chapman University", + passioSystemId: "263", }, ]; @@ -4327,14 +4329,14 @@ const routes: IRoute[] = [ { name: "Red Route", id: "1", - systemId: systems[0].id, + systemId: supportedIntegrationTestSystems[0].id, polylineCoordinates: redRoutePolylineCoordinates, color: "#db2316", }, { name: "Teal Route", id: "2", - systemId: systems[0].id, + systemId: supportedIntegrationTestSystems[0].id, polylineCoordinates: tealRoutePolylineCoordinates, color: "#21bdd1", }, @@ -4348,7 +4350,7 @@ const stops: IStop[] = [ latitude: 33.796001, longitude: -117.8892805, }, - systemId: systems[0].id, + systemId: supportedIntegrationTestSystems[0].id, }, { id: "2", @@ -4357,7 +4359,7 @@ const stops: IStop[] = [ latitude: 33.804433, longitude: -117.895966, }, - systemId: systems[0].id, + systemId: supportedIntegrationTestSystems[0].id, }, { id: "3", @@ -4366,7 +4368,7 @@ const stops: IStop[] = [ "latitude": 33.793325, "longitude": -117.85281 }, - systemId: systems[0].id, + systemId: supportedIntegrationTestSystems[0].id, } ]; @@ -4375,11 +4377,13 @@ const orderedStopsForRedRoute: IOrderedStop[] = [ routeId: routes[0].id, stopId: stops[0].id, position: 1, + systemId: "1", }, { routeId: routes[0].id, stopId: stops[2].id, position: 2, + systemId: "1", }, ]; @@ -4388,16 +4392,19 @@ const orderedStopsForTealRoute: IOrderedStop[] = [ routeId: routes[1].id, stopId: stops[0].id, position: 1, + systemId: supportedIntegrationTestSystems[0].id, }, { routeId: routes[1].id, stopId: stops[1].id, position: 2, + systemId: supportedIntegrationTestSystems[0].id, }, { routeId: routes[1].id, stopId: stops[2].id, position: 2, + systemId: supportedIntegrationTestSystems[0].id, }, ] @@ -4415,7 +4422,7 @@ const shuttles: IShuttle[] = [ longitude: -117.883698, }, routeId: routes[0].id, - systemId: systems[0].id, + systemId: supportedIntegrationTestSystems[0].id, orientationInDegrees: 45.91, }, { @@ -4426,7 +4433,7 @@ const shuttles: IShuttle[] = [ longitude: -117.862825, }, routeId: routes[0].id, - systemId: systems[0].id, + systemId: supportedIntegrationTestSystems[0].id, orientationInDegrees: 90.24, } ]; @@ -4436,28 +4443,29 @@ const etas: IEta[] = [ stopId: stops[0].id, shuttleId: shuttles[0].id, secondsRemaining: 12.023, + systemId: supportedIntegrationTestSystems[0].id, }, { stopId: stops[2].id, shuttleId: shuttles[0].id, secondsRemaining: 600.123, + systemId: supportedIntegrationTestSystems[0].id, }, { stopId: stops[2].id, shuttleId: shuttles[1].id, secondsRemaining: 172.015, + systemId: supportedIntegrationTestSystems[0].id, }, { stopId: stops[0].id, shuttleId: shuttles[1].id, secondsRemaining: 710.152, + systemId: supportedIntegrationTestSystems[0].id, } ]; export async function loadShuttleTestData(repository: ShuttleGetterSetterRepository) { - await Promise.all(systems.map(async (system) => { - await repository.addOrUpdateSystem(system); - })); await Promise.all(routes.map(async (route) => { await repository.addOrUpdateRoute(route); })); diff --git a/src/repositories/ShuttleGetterRepository.ts b/src/repositories/ShuttleGetterRepository.ts index 664328f..8f59578 100644 --- a/src/repositories/ShuttleGetterRepository.ts +++ b/src/repositories/ShuttleGetterRepository.ts @@ -1,16 +1,16 @@ -import { IEta, IOrderedStop, IRoute, IShuttle, IStop, ISystem } from "../entities/entities"; +import { IEta, IOrderedStop, IRoute, IShuttle, IStop } from "../entities/entities"; +/** + * Shuttle getter repository to be linked to a system. + */ export interface ShuttleGetterRepository { - getSystems(): Promise; - getSystemById(systemId: string): Promise; - - getStopsBySystemId(systemId: string): Promise; + getStops(): Promise; getStopById(stopId: string): Promise; - getRoutesBySystemId(systemId: string): Promise; + getRoutes(): Promise; getRouteById(routeId: string): Promise; - getShuttlesBySystemId(systemId: string): Promise; + getShuttles(): Promise; getShuttleById(shuttleId: string): Promise; getShuttlesByRouteId(routeId: string): Promise; diff --git a/src/repositories/ShuttleGetterSetterRepository.ts b/src/repositories/ShuttleGetterSetterRepository.ts index 0c0bf90..5e5a925 100644 --- a/src/repositories/ShuttleGetterSetterRepository.ts +++ b/src/repositories/ShuttleGetterSetterRepository.ts @@ -2,7 +2,7 @@ // to convert from data repo to GraphQL schema import { ShuttleGetterRepository } from "./ShuttleGetterRepository"; -import { IEta, IOrderedStop, IRoute, IShuttle, IStop, ISystem } from "../entities/entities"; +import { IEta, IOrderedStop, IRoute, IShuttle, IStop } from "../entities/entities"; /** * ShuttleGetterRepository interface for data derived from Passio API. @@ -12,21 +12,18 @@ import { IEta, IOrderedStop, IRoute, IShuttle, IStop, ISystem } from "../entitie */ export interface ShuttleGetterSetterRepository extends ShuttleGetterRepository { // Setter methods - addOrUpdateSystem(system: ISystem): Promise; addOrUpdateRoute(route: IRoute): Promise; addOrUpdateShuttle(shuttle: IShuttle): Promise; addOrUpdateStop(stop: IStop): Promise; addOrUpdateOrderedStop(orderedStop: IOrderedStop): Promise; addOrUpdateEta(eta: IEta): Promise; - removeSystemIfExists(systemId: string): Promise; removeRouteIfExists(routeId: string): Promise; removeShuttleIfExists(shuttleId: string): Promise; removeStopIfExists(stopId: string): Promise; removeOrderedStopIfExists(stopId: string, routeId: string): Promise; removeEtaIfExists(shuttleId: string, stopId: string): Promise; - clearSystemData(): Promise; clearRouteData(): Promise; clearShuttleData(): Promise; clearStopData(): Promise; diff --git a/src/repositories/UnoptimizedInMemoryShuttleRepository.ts b/src/repositories/UnoptimizedInMemoryShuttleRepository.ts index 59afac9..b8dda6f 100644 --- a/src/repositories/UnoptimizedInMemoryShuttleRepository.ts +++ b/src/repositories/UnoptimizedInMemoryShuttleRepository.ts @@ -1,5 +1,5 @@ import { ShuttleGetterSetterRepository } from "./ShuttleGetterSetterRepository"; -import { IEntityWithId, IEta, IOrderedStop, IRoute, IShuttle, IStop, ISystem } from "../entities/entities"; +import { IEntityWithId, IEta, IOrderedStop, IRoute, IShuttle, IStop } from "../entities/entities"; /** * An unoptimized in memory repository. @@ -7,7 +7,6 @@ import { IEntityWithId, IEta, IOrderedStop, IRoute, IShuttle, IStop, ISystem } f * switching to another data store later anyways) */ export class UnoptimizedInMemoryShuttleRepository implements ShuttleGetterSetterRepository { - private systems: ISystem[] = []; private stops: IStop[] = []; private routes: IRoute[] = []; private shuttles: IShuttle[] = []; @@ -16,32 +15,24 @@ export class UnoptimizedInMemoryShuttleRepository implements ShuttleGetterSetter private subscribers: ((eta: IEta) => void)[] = []; - public async getSystems() { - return this.systems; - } - - public async getSystemById(systemId: string) { - return this.findEntityById(systemId, this.systems); - } - - public async getStopsBySystemId(systemId: string) { - return this.stops.filter(stop => stop.systemId === systemId); + public async getStops(): Promise { + return this.stops; } public async getStopById(stopId: string) { return this.findEntityById(stopId, this.stops); } - public async getRoutesBySystemId(systemId: string) { - return this.routes.filter(route => route.systemId === systemId); + public async getRoutes(): Promise { + return this.routes; } public async getRouteById(routeId: string) { return this.findEntityById(routeId, this.routes); } - public async getShuttlesBySystemId(systemId: string) { - return this.shuttles.filter(shuttle => shuttle.systemId === systemId); + public async getShuttles(): Promise { + return this.shuttles; } public async getShuttlesByRouteId(routeId: string) { @@ -52,7 +43,7 @@ export class UnoptimizedInMemoryShuttleRepository implements ShuttleGetterSetter return this.findEntityById(shuttleId, this.shuttles); } - public async getEtasForShuttleId(shuttleId: string) { + public async getEtasForShuttleId(shuttleId: string): Promise { return this.etas.filter(eta => eta.shuttleId === shuttleId); } @@ -99,15 +90,6 @@ export class UnoptimizedInMemoryShuttleRepository implements ShuttleGetterSetter return entity; } - public async addOrUpdateSystem(system: ISystem): Promise { - const index = this.systems.findIndex((s) => s.id === system.id); - if (index !== -1) { - this.systems[index] = system; // Update existing - } else { - this.systems.push(system); // Add new - } - } - public async addOrUpdateRoute(route: IRoute): Promise { const index = this.routes.findIndex((r) => r.id === route.id); if (index !== -1) { @@ -175,10 +157,6 @@ export class UnoptimizedInMemoryShuttleRepository implements ShuttleGetterSetter return await this.removeEntityByMatcherIfExists((value) => value.id === entityId, arrayToSearchIn); } - public async removeSystemIfExists(systemId: string): Promise { - return await this.removeEntityByIdIfExists(systemId, this.systems); - } - public async removeRouteIfExists(routeId: string): Promise { return await this.removeEntityByIdIfExists(routeId, this.routes); } @@ -205,10 +183,6 @@ export class UnoptimizedInMemoryShuttleRepository implements ShuttleGetterSetter }, this.etas); } - public async clearSystemData() { - this.systems = []; - } - public async clearShuttleData(): Promise { this.shuttles = []; } diff --git a/src/resolvers/EtaResolvers.ts b/src/resolvers/EtaResolvers.ts index b5e7c65..0a2219f 100644 --- a/src/resolvers/EtaResolvers.ts +++ b/src/resolvers/EtaResolvers.ts @@ -4,10 +4,16 @@ import { ServerContext } from "../ServerContext"; export const EtaResolvers: Resolvers = { ETA: { stop: async (parent, args, contextValue, info) => { - return await contextValue.shuttleRepository.getStopById(parent.stopId); + const system = contextValue.findSystemById(parent.systemId); + if (!system) return null; + + return await system.shuttleRepository.getStopById(parent.stopId); }, shuttle: async (parent, args, contextValue, info) => { - return await contextValue.shuttleRepository.getShuttleById(parent.shuttleId); + const system = contextValue.findSystemById(parent.systemId); + if (!system) return null; + + return await system.shuttleRepository.getShuttleById(parent.shuttleId); }, }, } diff --git a/src/resolvers/MutationResolvers.ts b/src/resolvers/MutationResolvers.ts index 9b48a7d..8d266c8 100644 --- a/src/resolvers/MutationResolvers.ts +++ b/src/resolvers/MutationResolvers.ts @@ -1,21 +1,47 @@ -import { NotificationResponse, Resolvers } from "../generated/graphql"; +import { MutationScheduleNotificationArgs, NotificationResponse, Resolvers } from "../generated/graphql"; import { ServerContext } from "../ServerContext"; import { ETANotificationScheduler, } from "../notifications/schedulers/ETANotificationScheduler"; import { ScheduledNotification } from "../repositories/NotificationRepository"; +import { InterchangeSystem } from "../entities/InterchangeSystem"; + +async function temp_findMatchingSystemBasedOnShuttleId(context: ServerContext, args: Omit & { + input: NonNullable +}) { + let matchingSystem: InterchangeSystem | undefined; + await Promise.all(context.systems.map(async (system) => { + const shuttle = await system.shuttleRepository.getShuttleById(args.input.shuttleId); + // Theoretically, there should only be one + if (shuttle !== null) { + matchingSystem = system; + } + return shuttle; + })); + return matchingSystem; +} export const MutationResolvers: Resolvers = { + // TODO: Require system ID on these endpoints Mutation: { scheduleNotification: async (_parent, args, context, _info) => { - const shuttle = await context.shuttleRepository.getShuttleById(args.input.shuttleId); + let matchingSystem = await temp_findMatchingSystemBasedOnShuttleId(context, args); + + if (!matchingSystem) { + return { + message: "Shuttle ID doesn't exist", + success: false, + } + } + + const shuttle = await matchingSystem.shuttleRepository.getShuttleById(args.input.shuttleId); if (!shuttle) { return { message: "Shuttle ID doesn't exist", success: false, } } - const stop = await context.shuttleRepository.getStopById(args.input.stopId); + const stop = await matchingSystem.shuttleRepository.getStopById(args.input.stopId); if (!stop) { return { message: "Stop ID doesn't exist", @@ -30,7 +56,7 @@ export const MutationResolvers: Resolvers = { : ETANotificationScheduler.defaultSecondsThresholdForNotificationToFire, } - await context.notificationRepository.addOrUpdateNotification(notificationData); + await matchingSystem.notificationRepository.addOrUpdateNotification(notificationData); const response: NotificationResponse = { message: "Notification scheduled", @@ -40,9 +66,18 @@ export const MutationResolvers: Resolvers = { return response; }, cancelNotification: async (_parent, args, context, _info) => { - const isScheduled = await context.notificationRepository.isNotificationScheduled(args.input) + const matchingSystem = await temp_findMatchingSystemBasedOnShuttleId(context, args); + if (!matchingSystem) { + return { + success: false, + message: "Unable to find correct system", + data: args.input, + } + } + + const isScheduled = await matchingSystem.notificationRepository.isNotificationScheduled(args.input) if (isScheduled) { - await context.notificationRepository.deleteNotificationIfExists(args.input); + await matchingSystem.notificationRepository.deleteNotificationIfExists(args.input); return { success: true, message: "Notification cancelled", diff --git a/src/resolvers/OrderedStopResolvers.ts b/src/resolvers/OrderedStopResolvers.ts index fe047aa..0410e46 100644 --- a/src/resolvers/OrderedStopResolvers.ts +++ b/src/resolvers/OrderedStopResolvers.ts @@ -3,49 +3,63 @@ import { ServerContext } from "../ServerContext"; export const OrderedStopResolvers: Resolvers = { OrderedStop: { - nextStop: async (parent, args, contextValue, info): Promise => { + nextStop: async (parent, args, contextValue, _info): Promise => { const routeId = parent.routeId; const stopId = parent.stopId; - const currentOrderedStop = await contextValue.shuttleRepository.getOrderedStopByRouteAndStopId(routeId, stopId); + const system = contextValue.findSystemById(parent.systemId); + if (!system) return null; + + const currentOrderedStop = await system.shuttleRepository.getOrderedStopByRouteAndStopId(routeId, stopId); if (!currentOrderedStop) return null; const nextOrderedStop = currentOrderedStop.nextStop; if (!nextOrderedStop) return null; - const nextOrderedStopObject = await contextValue.shuttleRepository.getStopById(nextOrderedStop.stopId); + const nextOrderedStopObject = await system.shuttleRepository.getStopById(nextOrderedStop.stopId); if (!nextOrderedStopObject) return null; return { route: parent.route, routeId: parent.routeId, stopId: nextOrderedStopObject.id, + systemId: system.id, } }, - previousStop: async (parent, args, contextValue, info): Promise => { + previousStop: async (parent, args, contextValue, _info): Promise => { const routeId = parent.routeId; const stopId = parent.stopId; - const currentOrderedStop = await contextValue.shuttleRepository.getOrderedStopByRouteAndStopId(routeId, stopId); + const system = contextValue.findSystemById(parent.systemId); + if (!system) return null; + + const currentOrderedStop = await system.shuttleRepository.getOrderedStopByRouteAndStopId(routeId, stopId); if (!currentOrderedStop) return null; const previousOrderedStop = currentOrderedStop.previousStop; if (!previousOrderedStop) return null; - const previousOrderedStopObject = await contextValue.shuttleRepository.getStopById(previousOrderedStop.stopId); + const previousOrderedStopObject = await system.shuttleRepository.getStopById(previousOrderedStop.stopId); if (!previousOrderedStopObject) return null; return { route: parent.route, routeId: parent.routeId, stopId: previousOrderedStopObject.id, + systemId: system.id, } }, - stop: async (parent, args, contextValue, info) => { - return await contextValue.shuttleRepository.getStopById(parent.stopId); + stop: async (parent, args, contextValue, _info) => { + const system = contextValue.findSystemById(parent.systemId); + if (!system) return null; + + return await system.shuttleRepository.getStopById(parent.stopId); }, - route: async (parent, args, contextValue, info) => { - return await contextValue.shuttleRepository.getRouteById(parent.routeId); + route: async (parent, args, contextValue, _info) => { + const system = contextValue.findSystemById(parent.systemId); + if (!system) return null; + + return await system.shuttleRepository.getRouteById(parent.routeId); }, }, diff --git a/src/resolvers/QueryResolvers.ts b/src/resolvers/QueryResolvers.ts index 06df1a9..08a415f 100644 --- a/src/resolvers/QueryResolvers.ts +++ b/src/resolvers/QueryResolvers.ts @@ -4,11 +4,16 @@ import { Resolvers } from "../generated/graphql"; export const QueryResolvers: Resolvers = { Query: { systems: async (_parent, args, contextValue, _info) => { - return await contextValue.shuttleRepository.getSystems(); + return contextValue.systems.map((system) => { + return { + name: system.name, + id: system.id, + }; + }) }, system: async (_parent, args, contextValue, _info) => { if (!args.id) return null; - const system = await contextValue.shuttleRepository.getSystemById(args.id); + const system = contextValue.findSystemById(args.id); if (system === null) return null; return { @@ -16,13 +21,30 @@ export const QueryResolvers: Resolvers = { id: system.id, }; }, + // TODO: Update the GraphQL schema to require a system ID isNotificationScheduled: async (_parent, args, contextValue, _info) => { const notificationData = args.input; - return await contextValue.notificationRepository.isNotificationScheduled(notificationData); + + for (let system of contextValue.systems) { + const isScheduled = await system.notificationRepository.isNotificationScheduled(notificationData); + if (isScheduled) { + return true; + } + } + + return false; }, secondsThresholdForNotification: async (_parent, args, contextValue, _info) => { const notificationData = args.input; - return await contextValue.notificationRepository.getSecondsThresholdForNotificationIfExists(notificationData); + + for (let system of contextValue.systems) { + const isScheduled = await system.notificationRepository.isNotificationScheduled(notificationData); + if (isScheduled) { + return await system.notificationRepository.getSecondsThresholdForNotificationIfExists(args.input); + } + } + + return null; }, }, } diff --git a/src/resolvers/RouteResolvers.ts b/src/resolvers/RouteResolvers.ts index a0adc01..aa29fdc 100644 --- a/src/resolvers/RouteResolvers.ts +++ b/src/resolvers/RouteResolvers.ts @@ -4,7 +4,10 @@ import { ServerContext } from "../ServerContext"; export const RouteResolvers: Resolvers = { Route: { shuttles: async (parent, args, contextValue, info) => { - const shuttles = await contextValue.shuttleRepository.getShuttlesByRouteId(parent.id); + const system = contextValue.findSystemById(parent.systemId); + if (!system) return null; + + const shuttles = await system.shuttleRepository.getShuttlesByRouteId(parent.id); return shuttles.map(({ coordinates, @@ -17,21 +20,27 @@ export const RouteResolvers: Resolvers = { route: parent, routeId: parent.id, id, - orientationInDegrees + orientationInDegrees, + systemId: parent.systemId, })); }, orderedStop: async (parent, args, contextValue, info) => { if (!args.forStopId) return null; - const orderedStop = await contextValue.shuttleRepository.getOrderedStopByRouteAndStopId(parent.id, args.forStopId); + + const system = contextValue.findSystemById(parent.systemId); + if (!system) return null; + + const orderedStop = await system.shuttleRepository.getOrderedStopByRouteAndStopId(parent.id, args.forStopId); if (!orderedStop) return null; - const stop = await contextValue.shuttleRepository.getStopById(orderedStop.stopId); + const stop = await system.shuttleRepository.getStopById(orderedStop.stopId); if (!stop) return null; return { stopId: args.forStopId, routeId: parent.id, route: parent, + systemId: system.id, } }, }, diff --git a/src/resolvers/ShuttleResolvers.ts b/src/resolvers/ShuttleResolvers.ts index b264306..103e40f 100644 --- a/src/resolvers/ShuttleResolvers.ts +++ b/src/resolvers/ShuttleResolvers.ts @@ -5,7 +5,11 @@ export const ShuttleResolvers: Resolvers = { Shuttle: { eta: async (parent, args, contextValue, info) => { if (!args.forStopId) return null; - const etaForStopId = await contextValue.shuttleRepository.getEtaForShuttleAndStopId(parent.id, args.forStopId); + + const system = contextValue.findSystemById(parent.systemId); + if (!system) return null; + + const etaForStopId = await system.shuttleRepository.getEtaForShuttleAndStopId(parent.id, args.forStopId); if (etaForStopId === null) return null; return { @@ -13,10 +17,14 @@ export const ShuttleResolvers: Resolvers = { secondsRemaining: etaForStopId.secondsRemaining, shuttleId: parent.id, shuttle: parent, + systemId: system.id, }; }, etas: async (parent, args, contextValue, info) => { - const etasForShuttle = await contextValue.shuttleRepository.getEtasForShuttleId(parent.id); + const system = contextValue.findSystemById(parent.systemId); + if (!system) return null; + + const etasForShuttle = await system.shuttleRepository.getEtasForShuttleId(parent.id); if (!etasForShuttle) return null; const computedEtas = await Promise.all(etasForShuttle.map(async ({ @@ -28,6 +36,7 @@ export const ShuttleResolvers: Resolvers = { stopId, shuttle: parent, shuttleId: parent.id, + systemId: system.id, } })); @@ -38,7 +47,10 @@ export const ShuttleResolvers: Resolvers = { return []; }, route: async (parent, args, contextValue, info) => { - const route = await contextValue.shuttleRepository.getRouteById(parent.routeId); + const system = contextValue.findSystemById(parent.systemId); + if (!system) return null; + + const route = await system.shuttleRepository.getRouteById(parent.routeId); if (route === null) return null; return { @@ -46,6 +58,7 @@ export const ShuttleResolvers: Resolvers = { id: route.id, name: route.name, polylineCoordinates: route.polylineCoordinates, + systemId: system.id, } } }, diff --git a/src/resolvers/StopResolvers.ts b/src/resolvers/StopResolvers.ts index 860e3be..0c3ea54 100644 --- a/src/resolvers/StopResolvers.ts +++ b/src/resolvers/StopResolvers.ts @@ -3,11 +3,19 @@ import { ServerContext } from "../ServerContext"; export const StopResolvers: Resolvers = { Stop: { - orderedStops: async (parent, args, contextValue, info) => { - return await contextValue.shuttleRepository.getOrderedStopsByStopId(parent.id); + orderedStops: async (parent, args, contextValue, _info) => { + const system = contextValue.findSystemById(parent.systemId); + if (!system) { + return []; + } + return await system.shuttleRepository.getOrderedStopsByStopId(parent.id); }, - etas: async (parent, args, contextValue, info) => { - return await contextValue.shuttleRepository.getEtasForStopId(parent.id); + etas: async (parent, args, contextValue, _info) => { + const system = contextValue.findSystemById(parent.systemId); + if (!system) { + return []; + } + return await system.shuttleRepository.getEtasForStopId(parent.id); }, }, } diff --git a/src/resolvers/SystemResolvers.ts b/src/resolvers/SystemResolvers.ts index ae9903a..de8e3b0 100644 --- a/src/resolvers/SystemResolvers.ts +++ b/src/resolvers/SystemResolvers.ts @@ -3,15 +3,30 @@ import { ServerContext } from "../ServerContext"; export const SystemResolvers: Resolvers = { System: { - routes: async (parent, args, contextValue, info) => { - return await contextValue.shuttleRepository.getRoutesBySystemId(parent.id); + routes: async (parent, _args, contextValue, _info) => { + const system = contextValue.findSystemById(parent.id); + if (!system) { + return []; + } + + return await system.shuttleRepository.getRoutes(); }, - stops: async (parent, args, contextValue, info) => { - return await contextValue.shuttleRepository.getStopsBySystemId(parent.id); + stops: async (parent, _args, contextValue, _info) => { + const system = contextValue.findSystemById(parent.id); + if (!system) { + return []; + } + + return await system.shuttleRepository.getStops(); }, - stop: async (parent, args, contextValue, info) => { + stop: async (parent, args, contextValue, _info) => { if (!args.id) return null; - const stop = await contextValue.shuttleRepository.getStopById(args.id); + const system = contextValue.findSystemById(parent.id); + if (!system) { + return null; + } + + const stop = await system.shuttleRepository.getStopById(args.id); if (stop === null) return null; if (stop.systemId !== parent.id) return null; @@ -20,11 +35,16 @@ export const SystemResolvers: Resolvers = { id: stop.id, name: stop.name, coordinates: stop.coordinates as Coordinates, + systemId: parent.id, }; }, - route: async (parent, args, contextValue, info) => { + route: async (parent, args, contextValue, _info) => { if (!args.id) return null; - const route = await contextValue.shuttleRepository.getRouteById(args.id); + const system = contextValue.findSystemById(parent.id); + if (!system) { + return null; + } + const route = await system.shuttleRepository.getRouteById(args.id); if (route === null) return null; if (route.systemId !== parent.id) return null; @@ -34,19 +54,29 @@ export const SystemResolvers: Resolvers = { id: route.id, name: route.name, polylineCoordinates: route.polylineCoordinates as Coordinates[], + systemId: parent.id, }; }, - shuttle: async (parent, args, contextValue, info) => { + shuttle: async (parent, args, contextValue, _info) => { if (!args.id) return null; - const shuttle = await contextValue.shuttleRepository.getShuttleById(args.id); + const system = contextValue.findSystemById(parent.id); + if (!system) { + return null; + } + const shuttle = await system.shuttleRepository.getShuttleById(args.id); if (shuttle === null) return null; if (shuttle.systemId !== parent.id) return null; return shuttle; }, - shuttles: async (parent, args, contextValue, info) => { - return await contextValue.shuttleRepository.getShuttlesBySystemId(parent.id); + shuttles: async (parent, args, contextValue, _info) => { + const system = contextValue.findSystemById(parent.id); + if (!system) { + return []; + } + + return await system.shuttleRepository.getShuttles(); } }, } diff --git a/test/loaders/ApiBasedShuttleRepositoryLoaderTests.test.ts b/test/loaders/ApiBasedShuttleRepositoryLoaderTests.test.ts index cead5ec..7ff472d 100644 --- a/test/loaders/ApiBasedShuttleRepositoryLoaderTests.test.ts +++ b/test/loaders/ApiBasedShuttleRepositoryLoaderTests.test.ts @@ -1,13 +1,11 @@ -import { beforeEach, describe, expect, it, jest, test } from "@jest/globals"; +import { beforeEach, describe, expect, it, jest } from "@jest/globals"; import { ApiBasedShuttleRepositoryLoader, ApiResponseError } from "../../src/loaders/ApiBasedShuttleRepositoryLoader"; import { UnoptimizedInMemoryShuttleRepository } from "../../src/repositories/UnoptimizedInMemoryShuttleRepository"; -import { fetchSystemDataSuccessfulResponse } from "../jsonSnapshots/fetchSystemData/fetchSystemDataSuccessfulResponse"; -import { fetchSystemDataFailedResponse } from "../jsonSnapshots/fetchSystemData/fetchSystemDataFailedResponse"; import { fetchRouteDataSuccessfulResponse } from "../jsonSnapshots/fetchRouteData/fetchRouteDataSuccessfulResponse"; import { fetchStopAndPolylineDataSuccessfulResponse } from "../jsonSnapshots/fetchStopAndPolylineData/fetchStopAndPolylineDataSuccessfulResponse"; -import { generateMockRoutes, generateMockShuttles, generateMockStops, generateMockSystems } from "../testHelpers/mockDataGenerators"; +import { generateMockRoutes, generateMockShuttles, generateMockStops } from "../testHelpers/mockDataGenerators"; import { fetchShuttleDataSuccessfulResponse } from "../jsonSnapshots/fetchShuttleData/fetchShuttleDataSuccessfulResponse"; @@ -26,69 +24,12 @@ describe("ApiBasedRepositoryLoader", () => { let loader: ApiBasedShuttleRepositoryLoader; beforeEach(() => { - loader = new ApiBasedShuttleRepositoryLoader(new UnoptimizedInMemoryShuttleRepository()); + loader = new ApiBasedShuttleRepositoryLoader("263", "1", new UnoptimizedInMemoryShuttleRepository()); resetGlobalFetchMockJson(); }); - describe("fetchAndUpdateSystemData", () => { - it("updates system data in repository if response received", async () => { - // Arrange - const systemsToPrune = generateMockSystems(); - await Promise.all(systemsToPrune.map(async (system) => { - await loader.repository.addOrUpdateSystem(system); - })); - - const numberOfSystemsInResponse = fetchSystemDataSuccessfulResponse.all.length; - updateGlobalFetchMockJson(fetchSystemDataSuccessfulResponse); - - // Act - await loader.fetchAndUpdateSystemData(); - - // Assert - const systems = await loader.repository.getSystems(); - if (loader.supportedSystemIds.length < numberOfSystemsInResponse) { - expect(systems).toHaveLength(loader.supportedSystemIds.length); - } else { - expect(systems).toHaveLength(numberOfSystemsInResponse); - } - }); - - it("throws the correct error if the API response contains no data", async () => { - updateGlobalFetchMockJson(fetchSystemDataFailedResponse); - - await assertAsyncCallbackThrowsApiResponseError(async () => { - await loader.fetchAndUpdateSystemData(); - }); - }); - - it("throws the correct error if HTTP status code is not 200", async () => { - updateGlobalFetchMockJson(fetchSystemDataFailedResponse, 400); - - await assertAsyncCallbackThrowsApiResponseError(async () => { - await loader.fetchAndUpdateSystemData(); - }); - }); - }); - - - describe("fetchAndUpdateRouteDataForExistingSystemsInRepository", () => { - test("calls fetchAndUpdateRouteDataForSystemId for all systems in repository", async () => { - const spy = jest.spyOn(loader, "fetchAndUpdateRouteDataForSystemId"); - - const systems = generateMockSystems(); - - await Promise.all(systems.map(async (system) => { - await loader.repository.addOrUpdateSystem(system); - })); - - await loader.fetchAndUpdateRouteDataForExistingSystemsInRepository(); - - expect(spy.mock.calls.length).toBe(systems.length); - }); - }); - - describe("fetchAndUpdateRouteDataForSystemId", () => { - const systemId = "263"; + const systemId = "1"; + describe("fetchAndUpdateRouteDataForSystem", () => { it("updates route data in repository if response received", async () => { // Arrange // Test pruning @@ -101,10 +42,10 @@ describe("ApiBasedRepositoryLoader", () => { updateGlobalFetchMockJson(fetchRouteDataSuccessfulResponse); // Act - await loader.fetchAndUpdateRouteDataForSystemId(systemId); + await loader.fetchAndUpdateRouteDataForSystem(); // Assert - const routes = await loader.repository.getRoutesBySystemId(systemId); + const routes = await loader.repository.getRoutes(); expect(routes.length).toEqual(fetchRouteDataSuccessfulResponse.all.length) }); @@ -116,29 +57,12 @@ describe("ApiBasedRepositoryLoader", () => { updateGlobalFetchMockJsonToThrowSyntaxError(); await assertAsyncCallbackThrowsApiResponseError(async () => { - await loader.fetchAndUpdateRouteDataForSystemId(systemId); + await loader.fetchAndUpdateRouteDataForSystem(); }); }); }); - describe("fetchAndUpdateStopAndPolylineDataForRoutesInExistingSystemsInRepository", () => { - it("calls fetchAndUpdateStopAndPolylineDataForRoutesWithSystemId for every system", async () => { - const spy = jest.spyOn(loader, "fetchAndUpdateStopAndPolylineDataForRoutesWithSystemId"); - - const systems = generateMockSystems(); - - await Promise.all(systems.map(async (system) => { - await loader.repository.addOrUpdateSystem(system); - })); - - await loader.fetchAndUpdateStopAndPolylineDataForRoutesInExistingSystemsInRepository(); - - expect(spy.mock.calls.length).toBe(systems.length); - }); - }) - describe("fetchAndUpdateStopAndPolylineDataForRoutesWithSystemId", () => { - const systemId = "263"; it("updates stop and polyline data if response received", async () => { // Arrange // Test pruning of stops only @@ -152,9 +76,9 @@ describe("ApiBasedRepositoryLoader", () => { const stopsArray = Object.values(fetchStopAndPolylineDataSuccessfulResponse.stops); - await loader.fetchAndUpdateStopAndPolylineDataForRoutesWithSystemId(systemId); + await loader.fetchAndUpdateStopAndPolylineDataForRoutesInSystem(); - const stops = await loader.repository.getStopsBySystemId(systemId); + const stops = await loader.repository.getStops(); expect(stops.length).toEqual(stopsArray.length); await Promise.all(stops.map(async (stop) => { @@ -162,7 +86,7 @@ describe("ApiBasedRepositoryLoader", () => { expect(orderedStops.length).toBeGreaterThan(0); })); - const routes = await loader.repository.getRoutesBySystemId(systemId); + const routes = await loader.repository.getRoutes(); routes.forEach((route) => { expect(route.polylineCoordinates.length).toBeGreaterThan(0); }); @@ -172,28 +96,12 @@ describe("ApiBasedRepositoryLoader", () => { updateGlobalFetchMockJsonToThrowSyntaxError(); await assertAsyncCallbackThrowsApiResponseError(async () => { - await loader.fetchAndUpdateStopAndPolylineDataForRoutesWithSystemId(systemId); + await loader.fetchAndUpdateStopAndPolylineDataForRoutesInSystem(); }); }) }); - describe("fetchAndUpdateShuttleDataForExistingSystemsInRepository", () => { - it("calls fetchAndUpdateShuttleDataForSystemId for every system", async () => { - const spy = jest.spyOn(loader, "fetchAndUpdateShuttleDataForSystemId"); - - const systems = generateMockSystems(); - await Promise.all(systems.map(async (system) => { - await loader.repository.addOrUpdateSystem(system); - })) - - await loader.fetchAndUpdateShuttleDataForExistingSystemsInRepository(); - - expect(spy.mock.calls.length).toBe(systems.length); - }); - }); - - describe("fetchAndUpdateShuttleDataForSystemId", () => { - const systemId = "263"; + describe("fetchAndUpdateShuttleDataForSystem", () => { it("updates shuttle data in repository if response received", async () => { const shuttlesToPrune = generateMockShuttles(); await Promise.all(shuttlesToPrune.map(async (shuttle) => { @@ -204,9 +112,9 @@ describe("ApiBasedRepositoryLoader", () => { updateGlobalFetchMockJson(fetchShuttleDataSuccessfulResponse); const busesInResponse = Object.values(fetchShuttleDataSuccessfulResponse.buses); - await loader.fetchAndUpdateShuttleDataForSystemId(systemId); + await loader.fetchAndUpdateShuttleDataForSystem(); - const shuttles = await loader.repository.getShuttlesBySystemId(systemId); + const shuttles = await loader.repository.getShuttles(); expect(shuttles.length).toEqual(busesInResponse.length); }); @@ -215,27 +123,12 @@ describe("ApiBasedRepositoryLoader", () => { updateGlobalFetchMockJsonToThrowSyntaxError(); await assertAsyncCallbackThrowsApiResponseError(async () => { - await loader.fetchAndUpdateShuttleDataForSystemId(systemId); + await loader.fetchAndUpdateShuttleDataForSystem(); }); }); }); - describe("fetchAndUpdateEtaDataForExistingStopsForSystemsInRepository", () => { - it("calls fetchAndUpdateEtaDataFoExistingStopsForSystemId for every system in repository", async () => { - const spy = jest.spyOn(loader, "fetchAndUpdateEtaDataForExistingStopsForSystemId"); - - const systems = generateMockSystems(); - await Promise.all(systems.map(async (system) => { - await loader.repository.addOrUpdateSystem(system); - })); - - await loader.fetchAndUpdateEtaDataForExistingStopsForSystemsInRepository(); - - expect(spy.mock.calls.length).toBe(systems.length); - }); - }); - - describe("fetchAndUpdateEtaDataForExistingStopsForSystemId", () => { + describe("fetchAndUpdateEtaDataForExistingStopsForSystem", () => { it("calls fetchAndUpdateEtaDataForStopId for every stop in repository", async () => { const spy = jest.spyOn(loader, "fetchAndUpdateEtaDataForStopId"); @@ -248,7 +141,7 @@ describe("ApiBasedRepositoryLoader", () => { await loader.repository.addOrUpdateStop(stop); })); - await loader.fetchAndUpdateEtaDataForExistingStopsForSystemId("1"); + await loader.fetchAndUpdateEtaDataForExistingStopsForSystem(); expect(spy.mock.calls.length).toEqual(stops.length); }); diff --git a/test/loaders/TimedApiBasedShuttleRepositoryLoaderTests.test.ts b/test/loaders/TimedApiBasedShuttleRepositoryLoaderTests.test.ts index 3c81887..80a8c4d 100644 --- a/test/loaders/TimedApiBasedShuttleRepositoryLoaderTests.test.ts +++ b/test/loaders/TimedApiBasedShuttleRepositoryLoaderTests.test.ts @@ -15,14 +15,17 @@ describe("TimedApiBasedRepositoryLoader", () => { beforeEach(() => { resetGlobalFetchMockJson(); - loader = new TimedApiBasedShuttleRepositoryLoader(new UnoptimizedInMemoryShuttleRepository()); + loader = new TimedApiBasedShuttleRepositoryLoader( + "1", + "1", + new UnoptimizedInMemoryShuttleRepository() + ); spies = { - fetchAndUpdateSystemData: jest.spyOn(loader, 'fetchAndUpdateSystemData'), - fetchAndUpdateRouteDataForExistingSystemsInRepository: jest.spyOn(loader, 'fetchAndUpdateRouteDataForExistingSystemsInRepository'), - fetchAndUpdateStopAndPolylineDataForRoutesInExistingSystemsInRepository: jest.spyOn(loader, 'fetchAndUpdateStopAndPolylineDataForRoutesInExistingSystemsInRepository'), - fetchAndUpdateShuttleDataForExistingSystemsInRepository: jest.spyOn(loader, 'fetchAndUpdateShuttleDataForExistingSystemsInRepository'), - fetchAndUpdateEtaDataForExistingStopsForSystemsInRepository: jest.spyOn(loader, 'fetchAndUpdateEtaDataForExistingStopsForSystemsInRepository') + fetchAndUpdateRouteDataForSystem: jest.spyOn(loader, 'fetchAndUpdateRouteDataForSystem'), + fetchAndUpdateStopAndPolylineDataForRoutesInSystem: jest.spyOn(loader, 'fetchAndUpdateStopAndPolylineDataForRoutesInSystem'), + fetchAndUpdateShuttleDataForSystem: jest.spyOn(loader, 'fetchAndUpdateShuttleDataForSystem'), + fetchAndUpdateEtaDataForExistingStopsForSystem: jest.spyOn(loader, 'fetchAndUpdateEtaDataForExistingStopsForSystem') }; Object.values(spies).forEach((spy: any) => { diff --git a/test/notifications/schedulers/ETANotificationSchedulerTests.test.ts b/test/notifications/schedulers/ETANotificationSchedulerTests.test.ts index 7d9392f..58ee0b3 100644 --- a/test/notifications/schedulers/ETANotificationSchedulerTests.test.ts +++ b/test/notifications/schedulers/ETANotificationSchedulerTests.test.ts @@ -50,6 +50,7 @@ describe("ETANotificationScheduler", () => { shuttleId: shuttle.id, stopId: stop.id, secondsRemaining: 120, + systemId: "1", }; const notificationData1 = { diff --git a/test/repositories/UnoptimizedInMemoryShuttleRepositoryTests.test.ts b/test/repositories/UnoptimizedInMemoryShuttleRepositoryTests.test.ts index 003f71f..e1d9ba7 100644 --- a/test/repositories/UnoptimizedInMemoryShuttleRepositoryTests.test.ts +++ b/test/repositories/UnoptimizedInMemoryShuttleRepositoryTests.test.ts @@ -6,7 +6,6 @@ import { generateMockRoutes, generateMockShuttles, generateMockStops, - generateMockSystems } from "../testHelpers/mockDataGenerators"; // For repositories created in the future, reuse core testing @@ -21,57 +20,19 @@ describe("UnoptimizedInMemoryRepository", () => { repository = new UnoptimizedInMemoryShuttleRepository(); }); - 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 () => { + describe("getStops", () => { + test("gets all stops in the repository", 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")); + const result = await repository.getStops(); + expect(result).toEqual(mockStops); }); test("returns an empty list if there are no stops for the given system ID", async () => { - const result = await repository.getStopsBySystemId("nonexistent-system"); + const result = await repository.getStops(); expect(result).toEqual([]); }); }); @@ -92,19 +53,19 @@ describe("UnoptimizedInMemoryRepository", () => { }); }); - describe("getRoutesBySystemId", () => { + describe("getRoutes", () => { 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")); + const result = await repository.getRoutes(); + expect(result).toEqual(mockRoutes); }); test("returns an empty list if there are no routes for the system ID", async () => { - const result = await repository.getRoutesBySystemId("nonexistent-system"); + const result = await repository.getRoutes(); expect(result).toEqual([]); }); }); @@ -124,19 +85,19 @@ describe("UnoptimizedInMemoryRepository", () => { expect(result).toBeNull(); }); }); - describe("getShuttlesBySystemId", () => { + describe("getShuttles", () => { 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")); + const result = await repository.getShuttles(); + expect(result).toEqual(mockShuttles); }); test("returns an empty list if there are no shuttles for the system ID", async () => { - const result = await repository.getShuttlesBySystemId("nonexistent-system"); + const result = await repository.getShuttles(); expect(result).toEqual([]); }); }); @@ -324,31 +285,6 @@ describe("UnoptimizedInMemoryRepository", () => { }); }); - 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(); @@ -356,7 +292,7 @@ describe("UnoptimizedInMemoryRepository", () => { await repository.addOrUpdateRoute(newRoute); - const result = await repository.getRoutesBySystemId("sys1"); + const result = await repository.getRoutes(); expect(result).toEqual([newRoute]); }); @@ -369,7 +305,7 @@ describe("UnoptimizedInMemoryRepository", () => { await repository.addOrUpdateRoute(existingRoute); await repository.addOrUpdateRoute(updatedRoute); - const result = await repository.getRoutesBySystemId("sys1"); + const result = await repository.getRoutes(); expect(result).toEqual([updatedRoute]); }); }); @@ -381,7 +317,7 @@ describe("UnoptimizedInMemoryRepository", () => { await repository.addOrUpdateShuttle(newShuttle); - const result = await repository.getShuttlesBySystemId("sys1"); + const result = await repository.getShuttles(); expect(result).toEqual([newShuttle]); }); @@ -394,7 +330,7 @@ describe("UnoptimizedInMemoryRepository", () => { await repository.addOrUpdateShuttle(existingShuttle); await repository.addOrUpdateShuttle(updatedShuttle); - const result = await repository.getShuttlesBySystemId("sys1"); + const result = await repository.getShuttles(); expect(result).toEqual([updatedShuttle]); }); }); @@ -406,7 +342,7 @@ describe("UnoptimizedInMemoryRepository", () => { await repository.addOrUpdateStop(newStop); - const result = await repository.getStopsBySystemId("sys1"); + const result = await repository.getStops(); expect(result).toEqual([newStop]); }); @@ -419,7 +355,7 @@ describe("UnoptimizedInMemoryRepository", () => { await repository.addOrUpdateStop(existingStop); await repository.addOrUpdateStop(updatedStop); - const result = await repository.getStopsBySystemId("sys1"); + const result = await repository.getStops(); expect(result).toEqual([updatedStop]); }); }); @@ -474,33 +410,6 @@ describe("UnoptimizedInMemoryRepository", () => { }); }); - describe("removeSystemIfExists", () => { - test("removes system given ID", async () => { - const mockSystems = generateMockSystems(); - await Promise.all(mockSystems.map(async (system) => { - await repository.addOrUpdateSystem(system); - })); - - const systemToRemove = mockSystems[0]; - await repository.removeSystemIfExists(systemToRemove.id); - - const remainingSystems = await repository.getSystems(); - expect(remainingSystems).toHaveLength(mockSystems.length - 1); - }); - - test("does nothing if system doesn't exist", async () => { - const mockSystems = generateMockSystems(); - await Promise.all(mockSystems.map(async (system) => { - await repository.addOrUpdateSystem(system); - })); - - await repository.removeSystemIfExists("nonexistent-id"); - - const remainingSystems = await repository.getSystems(); - expect(remainingSystems).toHaveLength(mockSystems.length); - }); - }); - describe("removeRouteIfExists", () => { test("removes route given ID", async () => { const systemId = "1"; @@ -513,7 +422,7 @@ describe("UnoptimizedInMemoryRepository", () => { const routeToRemove = mockRoutes[0]; await repository.removeRouteIfExists(routeToRemove.id); - const remainingRoutes = await repository.getRoutesBySystemId(systemId); + const remainingRoutes = await repository.getRoutes(); expect(remainingRoutes).toHaveLength(mockRoutes.length - 1); }); @@ -527,7 +436,7 @@ describe("UnoptimizedInMemoryRepository", () => { await repository.removeRouteIfExists("nonexistent-id"); - const remainingRoutes = await repository.getRoutesBySystemId(systemId); + const remainingRoutes = await repository.getRoutes(); expect(remainingRoutes).toHaveLength(mockRoutes.length); }); }); @@ -544,7 +453,7 @@ describe("UnoptimizedInMemoryRepository", () => { const shuttleToRemove = mockShuttles[0]; await repository.removeShuttleIfExists(shuttleToRemove.id); - const remainingShuttles = await repository.getShuttlesBySystemId(systemId); + const remainingShuttles = await repository.getShuttles(); expect(remainingShuttles).toHaveLength(mockShuttles.length - 1); }); @@ -558,7 +467,7 @@ describe("UnoptimizedInMemoryRepository", () => { await repository.removeShuttleIfExists("nonexistent-id"); - const remainingShuttles = await repository.getShuttlesBySystemId(systemId); + const remainingShuttles = await repository.getShuttles(); expect(remainingShuttles).toHaveLength(mockShuttles.length); }); }); @@ -575,7 +484,7 @@ describe("UnoptimizedInMemoryRepository", () => { const stopToRemove = mockStops[0]; await repository.removeStopIfExists(stopToRemove.id); - const remainingStops = await repository.getStopsBySystemId(systemId); + const remainingStops = await repository.getStops(); expect(remainingStops).toHaveLength(mockStops.length - 1); }); @@ -589,7 +498,7 @@ describe("UnoptimizedInMemoryRepository", () => { await repository.removeStopIfExists("nonexistent-id"); - const remainingStops = await repository.getStopsBySystemId(systemId); + const remainingStops = await repository.getStops(); expect(remainingStops).toHaveLength(mockStops.length); }); }); @@ -662,31 +571,6 @@ describe("UnoptimizedInMemoryRepository", () => { }); }); - 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(); @@ -696,7 +580,7 @@ describe("UnoptimizedInMemoryRepository", () => { await repository.clearShuttleData(); - const result = await repository.getShuttlesBySystemId("sys1"); + const result = await repository.getShuttles(); expect(result).toEqual([]); }); }); @@ -738,7 +622,7 @@ describe("UnoptimizedInMemoryRepository", () => { await repository.clearRouteData(); - const result = await repository.getRoutesBySystemId("sys1"); + const result = await repository.getRoutes(); expect(result).toEqual([]); }); }); @@ -752,7 +636,7 @@ describe("UnoptimizedInMemoryRepository", () => { await repository.clearStopData(); - const result = await repository.getStopsBySystemId("sys1"); + const result = await repository.getStops(); expect(result).toEqual([]); }); }); diff --git a/test/resolvers/EtaResolverTests.test.ts b/test/resolvers/EtaResolverTests.test.ts index 92a5600..1238f22 100644 --- a/test/resolvers/EtaResolverTests.test.ts +++ b/test/resolvers/EtaResolverTests.test.ts @@ -1,10 +1,9 @@ import { beforeEach, describe, expect, it } from "@jest/globals"; import { setupTestServerContext, setupTestServerHolder } from "../testHelpers/apolloTestServerHelpers"; -import { IEta, IShuttle, IStop, ISystem } from "../../src/entities/entities"; +import { IEta, IShuttle, IStop, IPassioSystem } from "../../src/entities/entities"; import { addMockEtaToRepository, addMockShuttleToRepository, addMockStopToRepository, - addMockSystemToRepository } from "../testHelpers/repositorySetupHelpers"; import assert = require("node:assert"); @@ -12,23 +11,21 @@ describe("EtaResolvers", () => { const holder = setupTestServerHolder(); const context = setupTestServerContext(); - let mockSystem: ISystem; let mockShuttle: IShuttle; let mockStop: IStop; let expectedEta: IEta; beforeEach(async () => { - mockSystem = await addMockSystemToRepository(context.shuttleRepository); - mockShuttle = await addMockShuttleToRepository(context.shuttleRepository, mockSystem.id); - mockStop = await addMockStopToRepository(context.shuttleRepository, mockSystem.id); - expectedEta = await addMockEtaToRepository(context.shuttleRepository, mockStop.id, mockShuttle.id); + mockShuttle = await addMockShuttleToRepository(context.systems[0].shuttleRepository, context.systems[0].id); + mockStop = await addMockStopToRepository(context.systems[0].shuttleRepository, context.systems[0].id); + expectedEta = await addMockEtaToRepository(context.systems[0].shuttleRepository, mockStop.id, mockShuttle.id); }); async function getResponseForEtaQuery(query: string) { const response = await holder.testServer.executeOperation({ query, variables: { - systemId: mockSystem.id, + systemId: context.systems[0].id, shuttleId: mockShuttle.id, }, }, { diff --git a/test/resolvers/MutationResolverTests.test.ts b/test/resolvers/MutationResolverTests.test.ts index 055195e..04dbf69 100644 --- a/test/resolvers/MutationResolverTests.test.ts +++ b/test/resolvers/MutationResolverTests.test.ts @@ -3,7 +3,6 @@ import { setupTestServerContext, setupTestServerHolder } from "../testHelpers/ap import { addMockShuttleToRepository, addMockStopToRepository, - addMockSystemToRepository } from "../testHelpers/repositorySetupHelpers"; import assert = require("node:assert"); import { NotificationInput } from "../../src/generated/graphql"; @@ -44,14 +43,14 @@ describe("MutationResolvers", () => { const notificationResponse = response.body.singleResult.data?.scheduleNotification as any; expect(notificationResponse.success).toBe(false); - expect(await context.notificationRepository.isNotificationScheduled(notificationInput)).toBe(false); + expect(await context.systems[0].notificationRepository.isNotificationScheduled(notificationInput)).toBe(false); } it("adds a notification to the notification service", async () => { - const system = await addMockSystemToRepository(context.shuttleRepository); - const shuttle = await addMockShuttleToRepository(context.shuttleRepository, system.id); - const stop = await addMockStopToRepository(context.shuttleRepository, system.id); + const system = context.systems[0]; + const shuttle = await addMockShuttleToRepository(context.systems[0].shuttleRepository, system.id); + const stop = await addMockStopToRepository(context.systems[0].shuttleRepository, system.id); const notificationInput = { deviceId: "1", @@ -72,13 +71,13 @@ describe("MutationResolvers", () => { expect(notificationResponse?.success).toBe(true); expect(notificationResponse?.data).toEqual(expectedNotificationData); - expect(await context.notificationRepository.getSecondsThresholdForNotificationIfExists(expectedNotificationData)).toBe(240); + expect(await context.systems[0].notificationRepository.getSecondsThresholdForNotificationIfExists(expectedNotificationData)).toBe(240); }); it("adds a notification with the default seconds threshold if none is provided", async () => { - const system = await addMockSystemToRepository(context.shuttleRepository); - const shuttle = await addMockShuttleToRepository(context.shuttleRepository, system.id); - const stop = await addMockStopToRepository(context.shuttleRepository, system.id); + const system = context.systems[0]; + const shuttle = await addMockShuttleToRepository(context.systems[0].shuttleRepository, system.id); + const stop = await addMockStopToRepository(context.systems[0].shuttleRepository, system.id); const notificationInput = { deviceId: "1", @@ -93,12 +92,12 @@ describe("MutationResolvers", () => { const notificationResponse = response.body.singleResult.data?.scheduleNotification as any; expect(notificationResponse?.success).toBe(true); - expect(await context.notificationRepository.getSecondsThresholdForNotificationIfExists(notificationInput)).toBe(180); + expect(await context.systems[0].notificationRepository.getSecondsThresholdForNotificationIfExists(notificationInput)).toBe(180); }); it("fails if the shuttle ID doesn't exist", async () => { - const system = await addMockSystemToRepository(context.shuttleRepository); - const stop = await addMockStopToRepository(context.shuttleRepository, system.id); + const system = context.systems[0]; + const stop = await addMockStopToRepository(context.systems[0].shuttleRepository, system.id); const notificationInput = { deviceId: "1", @@ -110,8 +109,8 @@ describe("MutationResolvers", () => { }); it("fails if the stop ID doesn't exist", async () => { - const system = await addMockSystemToRepository(context.shuttleRepository); - const shuttle = await addMockShuttleToRepository(context.shuttleRepository, system.id); + const system = context.systems[0]; + const shuttle = await addMockShuttleToRepository(context.systems[0].shuttleRepository, system.id); const notificationInput = { deviceId: "1", @@ -140,9 +139,9 @@ describe("MutationResolvers", () => { ` it("removes the notification from the notification service", async () => { - const system = await addMockSystemToRepository(context.shuttleRepository); - const shuttle = await addMockShuttleToRepository(context.shuttleRepository, system.id); - const stop = await addMockStopToRepository(context.shuttleRepository, system.id); + const system = context.systems[0]; + const shuttle = await addMockShuttleToRepository(context.systems[0].shuttleRepository, system.id); + const stop = await addMockStopToRepository(context.systems[0].shuttleRepository, system.id); const notificationInput: any = { deviceId: "1", @@ -150,7 +149,7 @@ describe("MutationResolvers", () => { stopId: stop.id, secondsThreshold: 180, } - await context.notificationRepository.addOrUpdateNotification(notificationInput); + await context.systems[0].notificationRepository.addOrUpdateNotification(notificationInput); const notificationLookup = { ...notificationInput @@ -166,7 +165,7 @@ describe("MutationResolvers", () => { expect(notificationResponse.success).toBe(true); expect(notificationResponse.data).toEqual(notificationLookup); - expect(await context.notificationRepository.isNotificationScheduled(notificationLookup)).toBe(false); + expect(await context.systems[0].notificationRepository.isNotificationScheduled(notificationLookup)).toBe(false); }); it("fails if the notification doesn't exist", async () => { diff --git a/test/resolvers/OrderedStopResolverTests.test.ts b/test/resolvers/OrderedStopResolverTests.test.ts index 4813e2d..855fbd4 100644 --- a/test/resolvers/OrderedStopResolverTests.test.ts +++ b/test/resolvers/OrderedStopResolverTests.test.ts @@ -1,26 +1,24 @@ import { beforeEach, describe, expect, it } from "@jest/globals"; import { setupTestServerContext, setupTestServerHolder } from "../testHelpers/apolloTestServerHelpers"; -import { IRoute, IStop, ISystem } from "../../src/entities/entities"; +import { IRoute, IStop, IPassioSystem } from "../../src/entities/entities"; import { generateMockOrderedStops, generateMockStops } from "../testHelpers/mockDataGenerators"; -import { addMockRouteToRepository, addMockSystemToRepository } from "../testHelpers/repositorySetupHelpers"; +import { addMockRouteToRepository } from "../testHelpers/repositorySetupHelpers"; import assert = require("node:assert"); describe("OrderedStopResolvers", () => { const holder = setupTestServerHolder(); const context = setupTestServerContext(); - let mockSystem: ISystem; let mockRoute: IRoute; let mockStops: IStop[]; beforeEach(async () => { - mockSystem = await addMockSystemToRepository(context.shuttleRepository); - mockRoute = await addMockRouteToRepository(context.shuttleRepository, mockSystem.id); + mockRoute = await addMockRouteToRepository(context.systems[0].shuttleRepository, context.systems[0].id); mockStops = generateMockStops(); await Promise.all(mockStops.map(async (mockStop) => { - mockStop.systemId = mockSystem.id; - await context.shuttleRepository.addOrUpdateStop(mockStop); + mockStop.systemId = context.systems[0].id; + await context.systems[0].shuttleRepository.addOrUpdateStop(mockStop); })); }); @@ -38,8 +36,8 @@ describe("OrderedStopResolvers", () => { // Link the stops together orderedStops[0].nextStop = orderedStops[1]; orderedStops[1].previousStop = orderedStops[0]; - await context.shuttleRepository.addOrUpdateOrderedStop(orderedStops[0]); - await context.shuttleRepository.addOrUpdateOrderedStop(orderedStops[1]); + await context.systems[0].shuttleRepository.addOrUpdateOrderedStop(orderedStops[0]); + await context.systems[0].shuttleRepository.addOrUpdateOrderedStop(orderedStops[1]); return orderedStops; } @@ -63,7 +61,7 @@ describe("OrderedStopResolvers", () => { return await holder.testServer.executeOperation({ query, variables: { - systemId: mockSystem.id, + systemId: context.systems[0].id, routeId: mockRoute.id, stopId, }, @@ -93,7 +91,7 @@ describe("OrderedStopResolvers", () => { it("returns null if there is no next stop in the repository", async () => { const orderedStops = await setUpOrderedStopsInRepository(); orderedStops[0].nextStop = undefined; - await context.shuttleRepository.addOrUpdateOrderedStop(orderedStops[0]); + await context.systems[0].shuttleRepository.addOrUpdateOrderedStop(orderedStops[0]); const response = await getResponseForNextStopQuery(orderedStops[0].stopId); @@ -104,7 +102,7 @@ describe("OrderedStopResolvers", () => { it("returns null if the next stop object no longer exists", async () => { const orderedStops = await setUpOrderedStopsInRepository(); - await context.shuttleRepository.removeStopIfExists(orderedStops[1].stopId); + await context.systems[0].shuttleRepository.removeStopIfExists(orderedStops[1].stopId); const response = await getResponseForNextStopQuery(orderedStops[0].stopId); @@ -134,7 +132,7 @@ describe("OrderedStopResolvers", () => { return await holder.testServer.executeOperation({ query, variables: { - systemId: mockSystem.id, + systemId: context.systems[0].id, routeId: mockRoute.id, stopId, }, @@ -163,7 +161,7 @@ describe("OrderedStopResolvers", () => { it("returns null if there is no previous stop in the repository", async () => { const orderedStops = await setUpOrderedStopsInRepository(); orderedStops[1].previousStop = undefined; - await context.shuttleRepository.addOrUpdateOrderedStop(orderedStops[1]); + await context.systems[0].shuttleRepository.addOrUpdateOrderedStop(orderedStops[1]); const response = await getResponseForPreviousStopQuery(orderedStops[1].stopId); @@ -174,7 +172,7 @@ describe("OrderedStopResolvers", () => { it("returns null if the current stop no longer exists", async () => { const orderedStops = await setUpOrderedStopsInRepository(); - await context.shuttleRepository.removeStopIfExists(orderedStops[0].stopId); + await context.systems[0].shuttleRepository.removeStopIfExists(orderedStops[0].stopId); const response = await getResponseForPreviousStopQuery(orderedStops[1].stopId); @@ -208,7 +206,7 @@ describe("OrderedStopResolvers", () => { return await holder.testServer.executeOperation({ query, variables: { - systemId: mockSystem.id, + systemId: context.systems[0].id, stopId, } }, { @@ -223,7 +221,7 @@ describe("OrderedStopResolvers", () => { orderedStops[0].stopId = mockStops[0].id; // Add one stop only - await context.shuttleRepository.addOrUpdateOrderedStop(orderedStops[0]); + await context.systems[0].shuttleRepository.addOrUpdateOrderedStop(orderedStops[0]); const response = await getResponseForRouteQuery(orderedStops[1].stopId); @@ -257,7 +255,7 @@ describe("OrderedStopResolvers", () => { return await holder.testServer.executeOperation({ query, variables: { - systemId: mockSystem.id, + systemId: context.systems[0].id, routeId: mockRoute.id, stopId, } @@ -270,7 +268,7 @@ describe("OrderedStopResolvers", () => { it("returns the associated stop if it exists", async () => { const orderedStops = await setUpOrderedStopsInRepository(); orderedStops[0].stopId = mockStops[0].id; - await context.shuttleRepository.addOrUpdateOrderedStop(orderedStops[0]); + await context.systems[0].shuttleRepository.addOrUpdateOrderedStop(orderedStops[0]); const response = await getResponseForStopQuery(orderedStops[0].stopId); diff --git a/test/resolvers/QueryResolverTests.test.ts b/test/resolvers/QueryResolverTests.test.ts index 52d6605..c471549 100644 --- a/test/resolvers/QueryResolverTests.test.ts +++ b/test/resolvers/QueryResolverTests.test.ts @@ -1,6 +1,10 @@ import { describe, expect, it } from "@jest/globals"; -import { generateMockSystems } from "../testHelpers/mockDataGenerators"; -import { setupTestServerContext, setupTestServerHolder } from "../testHelpers/apolloTestServerHelpers"; +import { generateMockPassioSystems } from "../testHelpers/mockDataGenerators"; +import { + buildSystemForTesting, + setupTestServerContext, + setupTestServerHolder +} from "../testHelpers/apolloTestServerHelpers"; import assert = require("node:assert"); import { addMockShuttleToRepository, addMockStopToRepository } from "../testHelpers/repositorySetupHelpers"; import { ScheduledNotification } from "../../src/repositories/NotificationRepository"; @@ -12,17 +16,9 @@ describe("QueryResolvers", () => { const holder = setupTestServerHolder(); const context = setupTestServerContext(); - async function addMockSystems() { - const systems = generateMockSystems(); - await Promise.all(systems.map(async (system) => { - await context.shuttleRepository.addOrUpdateSystem(system); - })); - return systems; - } - describe("systems", () => { it("returns systems from the repository", async () => { - const systems = await addMockSystems(); + const systems = context.systems; const query = ` query GetSystems @@ -58,7 +54,14 @@ describe("QueryResolvers", () => { `; it("returns a system for an ID from the repository", async () => { - const systems = await addMockSystems(); + context.systems = [ + buildSystemForTesting(), + buildSystemForTesting(), + ]; + context.findSystemById = (_: string) => context.systems[1]; + context.systems[1].id = "test-id"; + + const systems = context.systems; const systemToGet = systems[1]; const response = await holder.testServer.executeOperation({ @@ -77,6 +80,8 @@ describe("QueryResolvers", () => { }); it("returns null if there is no system", async () => { + context.findSystemById = (_: string) => null; + const response = await holder.testServer.executeOperation({ query, variables: { @@ -103,8 +108,8 @@ describe("QueryResolvers", () => { it("returns correct data if the notification is scheduled", async () => { // Arrange - const shuttle = await addMockShuttleToRepository(context.shuttleRepository, "1"); - const stop = await addMockStopToRepository(context.shuttleRepository, "1") + const shuttle = await addMockShuttleToRepository(context.systems[0].shuttleRepository, "1"); + const stop = await addMockStopToRepository(context.systems[0].shuttleRepository, "1") const notification: ScheduledNotification = { shuttleId: shuttle.id, @@ -112,7 +117,7 @@ describe("QueryResolvers", () => { deviceId: "1", secondsThreshold: 240, }; - await context.notificationRepository.addOrUpdateNotification(notification); + await context.systems[0].notificationRepository.addOrUpdateNotification(notification); const notificationLookup: any = { ...notification, diff --git a/test/resolvers/RouteResolverTests.test.ts b/test/resolvers/RouteResolverTests.test.ts index 909c1d8..71c7815 100644 --- a/test/resolvers/RouteResolverTests.test.ts +++ b/test/resolvers/RouteResolverTests.test.ts @@ -2,27 +2,26 @@ import { beforeEach, describe, expect, it } from "@jest/globals"; import { setupTestServerContext, setupTestServerHolder } from "../testHelpers/apolloTestServerHelpers"; import { addMockRouteToRepository, - addMockStopToRepository, - addMockSystemToRepository + addMockStopToRepository } from "../testHelpers/repositorySetupHelpers"; import { generateMockOrderedStops, generateMockShuttles } from "../testHelpers/mockDataGenerators"; -import { IRoute, IStop, ISystem } from "../../src/entities/entities"; +import { IRoute, IStop, IPassioSystem } from "../../src/entities/entities"; import assert = require("node:assert"); describe("RouteResolvers", () => { const holder = setupTestServerHolder(); const context = setupTestServerContext(); - let mockSystem: ISystem; + let mockSystem: IPassioSystem; let mockRoute: IRoute; let mockStop: IStop; beforeEach(async () => { - mockSystem = await addMockSystemToRepository(context.shuttleRepository); + mockSystem = context.systems[0]; const systemId = mockSystem.id; - mockRoute = await addMockRouteToRepository(context.shuttleRepository, systemId); - mockStop = await addMockStopToRepository(context.shuttleRepository, systemId); + mockRoute = await addMockRouteToRepository(context.systems[0].shuttleRepository, systemId); + mockStop = await addMockStopToRepository(context.systems[0].shuttleRepository, systemId); }); @@ -58,7 +57,7 @@ describe("RouteResolvers", () => { const expectedShuttle = expectedShuttles[0]; expectedShuttle.systemId = mockSystem.id; expectedShuttle.routeId = mockRoute.id; - await context.shuttleRepository.addOrUpdateShuttle(expectedShuttle); + await context.systems[0].shuttleRepository.addOrUpdateShuttle(expectedShuttle); const response = await getResponseForShuttlesQuery(); @@ -113,7 +112,7 @@ describe("RouteResolvers", () => { const expectedOrderedStop = orderedStops[0]; expectedOrderedStop.stopId = mockStop.id; expectedOrderedStop.routeId = mockRoute.id; - await context.shuttleRepository.addOrUpdateOrderedStop(expectedOrderedStop); + await context.systems[0].shuttleRepository.addOrUpdateOrderedStop(expectedOrderedStop); const response = await getResponseForOrderedStopQuery(); @@ -130,9 +129,9 @@ describe("RouteResolvers", () => { const expectedOrderedStop = orderedStops[0]; expectedOrderedStop.stopId = mockStop.id; expectedOrderedStop.routeId = mockRoute.id; - await context.shuttleRepository.addOrUpdateOrderedStop(expectedOrderedStop); + await context.systems[0].shuttleRepository.addOrUpdateOrderedStop(expectedOrderedStop); - await context.shuttleRepository.removeStopIfExists(mockStop.id); + await context.systems[0].shuttleRepository.removeStopIfExists(mockStop.id); const response = await getResponseForOrderedStopQuery(); diff --git a/test/resolvers/ShuttleResolverTests.test.ts b/test/resolvers/ShuttleResolverTests.test.ts index 800f34c..31ba7a5 100644 --- a/test/resolvers/ShuttleResolverTests.test.ts +++ b/test/resolvers/ShuttleResolverTests.test.ts @@ -1,8 +1,8 @@ import { beforeEach, describe, expect, it } from "@jest/globals"; import { generateMockEtas, generateMockRoutes } from "../testHelpers/mockDataGenerators"; -import { IShuttle, ISystem } from "../../src/entities/entities"; +import { IShuttle, IPassioSystem } from "../../src/entities/entities"; import { setupTestServerContext, setupTestServerHolder } from "../testHelpers/apolloTestServerHelpers"; -import { addMockShuttleToRepository, addMockSystemToRepository } from "../testHelpers/repositorySetupHelpers"; +import { addMockShuttleToRepository } from "../testHelpers/repositorySetupHelpers"; import assert = require("node:assert"); @@ -10,12 +10,12 @@ describe("ShuttleResolvers", () => { const holder = setupTestServerHolder(); const context = setupTestServerContext(); - let mockSystem: ISystem; + let mockSystem: IPassioSystem; let mockShuttle: IShuttle; beforeEach(async () => { - mockSystem = await addMockSystemToRepository(context.shuttleRepository); - mockShuttle = await addMockShuttleToRepository(context.shuttleRepository, + mockSystem = context.systems[0]; + mockShuttle = await addMockShuttleToRepository(context.systems[0].shuttleRepository, mockSystem.id); }); @@ -24,7 +24,7 @@ describe("ShuttleResolvers", () => { const etas = generateMockEtas(); await Promise.all(etas.map(async (eta) => { eta.shuttleId = shuttleId; - await context.shuttleRepository.addOrUpdateEta(eta); + await context.systems[0].shuttleRepository.addOrUpdateEta(eta); })); return etas; } @@ -175,7 +175,7 @@ describe("ShuttleResolvers", () => { it("returns the route if it exists", async () => { const mockRoute = generateMockRoutes()[0]; - await context.shuttleRepository.addOrUpdateRoute(mockRoute); + await context.systems[0].shuttleRepository.addOrUpdateRoute(mockRoute); const response = await getResponseForQuery(); diff --git a/test/resolvers/StopResolverTests.test.ts b/test/resolvers/StopResolverTests.test.ts index d82ca78..ac732e8 100644 --- a/test/resolvers/StopResolverTests.test.ts +++ b/test/resolvers/StopResolverTests.test.ts @@ -1,8 +1,11 @@ import { beforeEach, describe, expect, it } from "@jest/globals"; -import { setupTestServerContext, setupTestServerHolder } from "../testHelpers/apolloTestServerHelpers"; +import { + setupTestServerContext, + setupTestServerHolder +} from "../testHelpers/apolloTestServerHelpers"; import { generateMockEtas, generateMockOrderedStops } from "../testHelpers/mockDataGenerators"; -import { IStop, ISystem } from "../../src/entities/entities"; -import { addMockStopToRepository, addMockSystemToRepository } from "../testHelpers/repositorySetupHelpers"; +import { IStop } from "../../src/entities/entities"; +import { addMockStopToRepository } from "../testHelpers/repositorySetupHelpers"; import assert = require("node:assert"); describe("StopResolvers", () => { @@ -10,18 +13,16 @@ describe("StopResolvers", () => { const context = setupTestServerContext(); let mockStop: IStop; - let mockSystem: ISystem; beforeEach(async () => { - mockSystem = await addMockSystemToRepository(context.shuttleRepository); - mockStop = await addMockStopToRepository(context.shuttleRepository, mockSystem.id); + mockStop = await addMockStopToRepository(context.systems[0].shuttleRepository, context.systems[0].id); }) async function getResponseForQuery(query: string) { return await holder.testServer.executeOperation({ query, variables: { - systemId: mockSystem.id, + systemId: context.systems[0].id, stopId: mockStop.id, }, }, { @@ -49,7 +50,7 @@ describe("StopResolvers", () => { mockOrderedStops = mockOrderedStops.filter((orderedStop) => orderedStop.stopId === mockOrderedStops[0].stopId); await Promise.all(mockOrderedStops.map(async orderedStop => { orderedStop.stopId = mockStop.id; - await context.shuttleRepository.addOrUpdateOrderedStop(orderedStop); + await context.systems[0].shuttleRepository.addOrUpdateOrderedStop(orderedStop); })); const response = await getResponseForQuery(query); @@ -86,7 +87,7 @@ describe("StopResolvers", () => { mockEtas = mockEtas.filter((eta) => eta.stopId === mockEtas[0].stopId); await Promise.all(mockEtas.map(async eta => { eta.stopId = mockStop.id; - await context.shuttleRepository.addOrUpdateEta(eta); + await context.systems[0].shuttleRepository.addOrUpdateEta(eta); })); const response = await getResponseForQuery(query); diff --git a/test/resolvers/SystemResolverTests.test.ts b/test/resolvers/SystemResolverTests.test.ts index 1eb817c..bf86d8e 100644 --- a/test/resolvers/SystemResolverTests.test.ts +++ b/test/resolvers/SystemResolverTests.test.ts @@ -5,19 +5,18 @@ import { addMockRouteToRepository, addMockShuttleToRepository, addMockStopToRepository, - addMockSystemToRepository } from "../testHelpers/repositorySetupHelpers"; -import { ISystem } from "../../src/entities/entities"; +import { IPassioSystem } from "../../src/entities/entities"; import assert = require("node:assert"); describe("SystemResolvers", () => { const holder = setupTestServerHolder(); const context = setupTestServerContext(); - let mockSystem: ISystem; + let mockSystem: IPassioSystem; beforeEach(async () => { - mockSystem = await addMockSystemToRepository(context.shuttleRepository); + mockSystem = context.systems[0]; }); // TODO: Consolidate these into one single method taking an object @@ -28,9 +27,7 @@ describe("SystemResolvers", () => { systemId: mockSystem.id, }, }, { - contextValue: { - shuttleRepository: context.shuttleRepository - }, + contextValue: context, }); } @@ -50,7 +47,7 @@ describe("SystemResolvers", () => { const expectedRoutes = generateMockRoutes(); await Promise.all(expectedRoutes.map(async (route) => { route.systemId = mockSystem.id; - await context.shuttleRepository.addOrUpdateRoute(route); + await context.systems[0].shuttleRepository.addOrUpdateRoute(route); })); const response = await getResponseFromQueryNeedingSystemId(query); @@ -78,7 +75,7 @@ describe("SystemResolvers", () => { const expectedStops = generateMockStops(); await Promise.all(expectedStops.map(async (stop) => { stop.systemId = mockSystem.id; - await context.shuttleRepository.addOrUpdateStop(stop); + await context.systems[0].shuttleRepository.addOrUpdateStop(stop); })); const response = await getResponseFromQueryNeedingSystemId(query); @@ -110,14 +107,12 @@ describe("SystemResolvers", () => { stopId: stopId, }, }, { - contextValue: { - shuttleRepository: context.shuttleRepository, - } + contextValue: context, }); } it("gets the stop with the correct id", async () => { - const mockStop = await addMockStopToRepository(context.shuttleRepository, mockSystem.id); + const mockStop = await addMockStopToRepository(context.systems[0].shuttleRepository, mockSystem.id); const response = await getResponseForStopQuery(mockStop.id); @@ -133,9 +128,8 @@ describe("SystemResolvers", () => { ...mockSystem, id: "2", } - await context.shuttleRepository.addOrUpdateSystem(updatedSystem); - const mockStop = await addMockStopToRepository(context.shuttleRepository, updatedSystem.id); + const mockStop = await addMockStopToRepository(context.systems[0].shuttleRepository, updatedSystem.id); const response = await getResponseForStopQuery(mockStop.id); @@ -182,7 +176,7 @@ describe("SystemResolvers", () => { } it("gets the route with the correct id", async () => { - const mockRoute = await addMockRouteToRepository(context.shuttleRepository, mockSystem.id); + const mockRoute = await addMockRouteToRepository(context.systems[0].shuttleRepository, mockSystem.id); const response = await getResponseForRouteQuery(mockRoute.id); @@ -199,9 +193,8 @@ describe("SystemResolvers", () => { ...mockSystem, id: "2", } - await context.shuttleRepository.addOrUpdateSystem(updatedSystem); - const mockRoute = await addMockRouteToRepository(context.shuttleRepository, updatedSystem.id); + const mockRoute = await addMockRouteToRepository(context.systems[0].shuttleRepository, updatedSystem.id); const response = await getResponseForRouteQuery(mockRoute.id); @@ -249,7 +242,7 @@ describe("SystemResolvers", () => { } it("gets the shuttle with the correct id", async () => { - const mockShuttle = await addMockShuttleToRepository(context.shuttleRepository, mockSystem.id); + const mockShuttle = await addMockShuttleToRepository(context.systems[0].shuttleRepository, mockSystem.id); const response = await getResponseForShuttleQuery(mockShuttle.id); @@ -265,9 +258,8 @@ describe("SystemResolvers", () => { ...mockSystem, id: "2", } - await context.shuttleRepository.addOrUpdateSystem(updatedSystem); - const mockShuttle = await addMockShuttleToRepository(context.shuttleRepository, updatedSystem.id); + const mockShuttle = await addMockShuttleToRepository(context.systems[0].shuttleRepository, updatedSystem.id); const response = await getResponseForShuttleQuery(mockShuttle.id); @@ -305,7 +297,7 @@ describe("SystemResolvers", () => { const expectedShuttles = generateMockShuttles(); await Promise.all(expectedShuttles.map(async (shuttle) => { shuttle.systemId = mockSystem.id; - await context.shuttleRepository.addOrUpdateShuttle(shuttle); + await context.systems[0].shuttleRepository.addOrUpdateShuttle(shuttle); })); const response = await getResponseFromQueryNeedingSystemId(query); diff --git a/test/testHelpers/apolloTestServerHelpers.ts b/test/testHelpers/apolloTestServerHelpers.ts index a7512ec..731ae98 100644 --- a/test/testHelpers/apolloTestServerHelpers.ts +++ b/test/testHelpers/apolloTestServerHelpers.ts @@ -6,6 +6,7 @@ import { beforeEach } from "@jest/globals"; import { ServerContext } from "../../src/ServerContext"; import { ETANotificationScheduler } from "../../src/notifications/schedulers/ETANotificationScheduler"; import { InMemoryNotificationRepository } from "../../src/repositories/InMemoryNotificationRepository"; +import { InterchangeSystem } from "../../src/entities/InterchangeSystem"; function setUpTestServer() { @@ -18,6 +19,16 @@ function setUpTestServer() { }); } +const systemInfoForTesting = { + id: "1", name: "Chapman University", passioSystemId: "263" +}; + +export function buildSystemForTesting() { + return InterchangeSystem.buildForTesting( + systemInfoForTesting, + ); +} + /** * Returns a `ServerContext` object which can be passed to requests * for testing. @@ -26,8 +37,10 @@ export function setupTestServerContext() { const context: { [key: string] : any } = {}; beforeEach(() => { - context.shuttleRepository = new UnoptimizedInMemoryShuttleRepository(); - context.notificationRepository = new InMemoryNotificationRepository(); + context.systems = [ + buildSystemForTesting(), + ]; + context.findSystemById = (_: string) => context.systems[0]; }); return context as ServerContext; diff --git a/test/testHelpers/mockDataGenerators.ts b/test/testHelpers/mockDataGenerators.ts index 07d14bf..8f2afa4 100644 --- a/test/testHelpers/mockDataGenerators.ts +++ b/test/testHelpers/mockDataGenerators.ts @@ -1,9 +1,9 @@ -import { IEta, IOrderedStop, IRoute, IShuttle, IStop, ISystem } from "../../src/entities/entities"; +import { IEta, IOrderedStop, IRoute, IShuttle, IStop, IPassioSystem } from "../../src/entities/entities"; // Use a single set of generators in case any of the // interfaces change in the future -export function generateMockSystems(): ISystem[] { +export function generateMockPassioSystems(): IPassioSystem[] { return [ { id: "1", name: "System A" }, { id: "2", name: "System B" }, @@ -67,17 +67,17 @@ export function generateMockStops(): IStop[] { 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 }, + { stopId: "st1", routeId: "r1", position: 1, systemId: "sys1" }, + { stopId: "st1", routeId: "r2", position: 2, systemId: "sys1" }, + { stopId: "st2", routeId: "r1", position: 3, systemId: "sys1" }, + { stopId: "st2", routeId: "r2", position: 4, systemId: "sys1" }, ]; } export function generateMockEtas(): IEta[] { return [ - { shuttleId: "sh1", stopId: "st1", secondsRemaining: 120 }, - { shuttleId: "sh1", stopId: "st2", secondsRemaining: 180 }, - { shuttleId: "sh2", stopId: "st3", secondsRemaining: 240 }, + { shuttleId: "sh1", stopId: "st1", secondsRemaining: 120, systemId: "sys1" }, + { shuttleId: "sh1", stopId: "st2", secondsRemaining: 180, systemId: "sys1" }, + { shuttleId: "sh2", stopId: "st3", secondsRemaining: 240, systemId: "sys1" }, ]; } diff --git a/test/testHelpers/repositorySetupHelpers.ts b/test/testHelpers/repositorySetupHelpers.ts index 58caea3..85e5e1e 100644 --- a/test/testHelpers/repositorySetupHelpers.ts +++ b/test/testHelpers/repositorySetupHelpers.ts @@ -3,19 +3,9 @@ import { generateMockRoutes, generateMockShuttles, generateMockStops, - generateMockSystems } from "./mockDataGenerators"; import { ShuttleGetterSetterRepository } from "../../src/repositories/ShuttleGetterSetterRepository"; -export async function addMockSystemToRepository(repository: ShuttleGetterSetterRepository) { - const mockSystems = generateMockSystems(); - const mockSystem = mockSystems[0]; - mockSystem.id = "1"; - await repository.addOrUpdateSystem(mockSystem); - - return mockSystem; -} - export async function addMockRouteToRepository(repository: ShuttleGetterSetterRepository, systemId: string) { const mockRoutes = generateMockRoutes(); const mockRoute = mockRoutes[0];