Merge pull request #38 from brendan-ch/chore/reorganize-classes-for-multiple-systems

[INT-58] chore/reorganize-classes-for-multiple-systems
This commit is contained in:
2025-04-07 20:00:00 -07:00
committed by GitHub
35 changed files with 583 additions and 654 deletions

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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,
);
}
}

View File

@@ -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;
}

View File

@@ -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<ServerContext>({
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;
},
}
},
});

View File

@@ -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<string> = 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,
};
}

View File

@@ -1,12 +1,7 @@
export interface ShuttleRepositoryLoader {
fetchAndUpdateSystemData(): Promise<void>;
fetchAndUpdateRouteDataForExistingSystemsInRepository(): Promise<void>;
fetchAndUpdateRouteDataForSystemId(systemId: string): Promise<void>;
fetchAndUpdateStopAndPolylineDataForRoutesInExistingSystemsInRepository(): Promise<void>;
fetchAndUpdateStopAndPolylineDataForRoutesWithSystemId(systemId: string): Promise<void>;
fetchAndUpdateShuttleDataForExistingSystemsInRepository(): Promise<void>;
fetchAndUpdateShuttleDataForSystemId(systemId: string): Promise<void>;
fetchAndUpdateEtaDataForExistingStopsForSystemsInRepository(): Promise<void>;
fetchAndUpdateEtaDataForExistingStopsForSystemId(systemId: string): Promise<void>;
fetchAndUpdateRouteDataForSystem(): Promise<void>;
fetchAndUpdateStopAndPolylineDataForRoutesInSystem(): Promise<void>;
fetchAndUpdateShuttleDataForSystem(): Promise<void>;
fetchAndUpdateEtaDataForExistingStopsForSystem(): Promise<void>;
fetchAndUpdateEtaDataForStopId(stopId: string): Promise<void>;
}

View File

@@ -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);
}

View File

@@ -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);
}));

View File

@@ -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<ISystem[]>;
getSystemById(systemId: string): Promise<ISystem | null>;
getStopsBySystemId(systemId: string): Promise<IStop[]>;
getStops(): Promise<IStop[]>;
getStopById(stopId: string): Promise<IStop | null>;
getRoutesBySystemId(systemId: string): Promise<IRoute[]>;
getRoutes(): Promise<IRoute[]>;
getRouteById(routeId: string): Promise<IRoute | null>;
getShuttlesBySystemId(systemId: string): Promise<IShuttle[]>;
getShuttles(): Promise<IShuttle[]>;
getShuttleById(shuttleId: string): Promise<IShuttle | null>;
getShuttlesByRouteId(routeId: string): Promise<IShuttle[]>;

View File

@@ -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<void>;
addOrUpdateRoute(route: IRoute): Promise<void>;
addOrUpdateShuttle(shuttle: IShuttle): Promise<void>;
addOrUpdateStop(stop: IStop): Promise<void>;
addOrUpdateOrderedStop(orderedStop: IOrderedStop): Promise<void>;
addOrUpdateEta(eta: IEta): Promise<void>;
removeSystemIfExists(systemId: string): Promise<ISystem | null>;
removeRouteIfExists(routeId: string): Promise<IRoute | null>;
removeShuttleIfExists(shuttleId: string): Promise<IShuttle | null>;
removeStopIfExists(stopId: string): Promise<IStop | null>;
removeOrderedStopIfExists(stopId: string, routeId: string): Promise<IOrderedStop | null>;
removeEtaIfExists(shuttleId: string, stopId: string): Promise<IEta | null>;
clearSystemData(): Promise<void>;
clearRouteData(): Promise<void>;
clearShuttleData(): Promise<void>;
clearStopData(): Promise<void>;

View File

@@ -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<IStop[]> {
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<IRoute[]> {
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<IShuttle[]> {
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<IEta[]> {
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<void> {
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<void> {
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<ISystem | null> {
return await this.removeEntityByIdIfExists(systemId, this.systems);
}
public async removeRouteIfExists(routeId: string): Promise<IRoute | null> {
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<void> {
this.shuttles = [];
}

View File

@@ -4,10 +4,16 @@ import { ServerContext } from "../ServerContext";
export const EtaResolvers: Resolvers<ServerContext> = {
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);
},
},
}

View File

@@ -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<MutationScheduleNotificationArgs, "input"> & {
input: NonNullable<MutationScheduleNotificationArgs["input"]>
}) {
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<ServerContext> = {
// 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<ServerContext> = {
: 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<ServerContext> = {
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",

View File

@@ -3,49 +3,63 @@ import { ServerContext } from "../ServerContext";
export const OrderedStopResolvers: Resolvers<ServerContext> = {
OrderedStop: {
nextStop: async (parent, args, contextValue, info): Promise<OrderedStop | null> => {
nextStop: async (parent, args, contextValue, _info): Promise<OrderedStop | null> => {
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<OrderedStop | null> => {
previousStop: async (parent, args, contextValue, _info): Promise<OrderedStop | null> => {
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);
},
},

View File

@@ -4,11 +4,16 @@ import { Resolvers } from "../generated/graphql";
export const QueryResolvers: Resolvers<ServerContext> = {
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<ServerContext> = {
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;
},
},
}

View File

@@ -4,7 +4,10 @@ import { ServerContext } from "../ServerContext";
export const RouteResolvers: Resolvers<ServerContext> = {
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<ServerContext> = {
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,
}
},
},

View File

@@ -5,7 +5,11 @@ export const ShuttleResolvers: Resolvers<ServerContext> = {
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<ServerContext> = {
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<ServerContext> = {
stopId,
shuttle: parent,
shuttleId: parent.id,
systemId: system.id,
}
}));
@@ -38,7 +47,10 @@ export const ShuttleResolvers: Resolvers<ServerContext> = {
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<ServerContext> = {
id: route.id,
name: route.name,
polylineCoordinates: route.polylineCoordinates,
systemId: system.id,
}
}
},

View File

@@ -3,11 +3,19 @@ import { ServerContext } from "../ServerContext";
export const StopResolvers: Resolvers<ServerContext> = {
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);
},
},
}

View File

@@ -3,15 +3,30 @@ import { ServerContext } from "../ServerContext";
export const SystemResolvers: Resolvers<ServerContext> = {
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<ServerContext> = {
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<ServerContext> = {
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();
}
},
}

View File

@@ -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);
});

View File

@@ -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) => {

View File

@@ -50,6 +50,7 @@ describe("ETANotificationScheduler", () => {
shuttleId: shuttle.id,
stopId: stop.id,
secondsRemaining: 120,
systemId: "1",
};
const notificationData1 = {

View File

@@ -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([]);
});
});

View File

@@ -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,
},
}, {

View File

@@ -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 () => {

View File

@@ -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);

View File

@@ -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,

View File

@@ -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();

View File

@@ -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();

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;

View File

@@ -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" },
];
}

View File

@@ -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];