mirror of
https://github.com/brendan-ch/project-inter-server.git
synced 2026-04-17 07:50:31 +00:00
Merge pull request #11 from brendan-ch/bugfix/fix-data-flickering
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { GetterSetterRepository } from "../repositories/GetterSetterRepository";
|
||||
import { IEta, IRoute, IShuttle, IStop, ISystem } from "../entities/entities";
|
||||
import { IEntityWithId, IEta, IRoute, IShuttle, IStop, ISystem } from "../entities/entities";
|
||||
|
||||
export class ApiResponseError extends Error {
|
||||
constructor(message: string) {
|
||||
@@ -8,6 +8,11 @@ export class ApiResponseError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class which can load data into a repository from the
|
||||
* Passio Go API. Supports automatic pruning of all data types
|
||||
* which inherit from `IEntityWithId`.
|
||||
*/
|
||||
export class ApiBasedRepositoryLoader {
|
||||
supportedSystemIds = ["263"];
|
||||
baseUrl = "https://passiogo.com/mapGetData.php";
|
||||
@@ -17,12 +22,25 @@ export class ApiBasedRepositoryLoader {
|
||||
) {
|
||||
}
|
||||
|
||||
private async constructExistingEntityIdSet<T extends IEntityWithId>(entitySearchCallback: () => Promise<T[]>) {
|
||||
const existingEntities = await entitySearchCallback();
|
||||
const ids = new Set<string>();
|
||||
existingEntities.forEach((entity) => {
|
||||
ids.add(entity.id);
|
||||
});
|
||||
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();
|
||||
@@ -41,10 +59,16 @@ export class ApiBasedRepositoryLoader {
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -58,6 +82,10 @@ export class ApiBasedRepositoryLoader {
|
||||
}
|
||||
|
||||
public async fetchAndUpdateRouteDataForSystemId(systemId: string) {
|
||||
const routeIdsToPrune = await this.constructExistingEntityIdSet(async () => {
|
||||
return await this.repository.getRoutesBySystemId(systemId);
|
||||
});
|
||||
|
||||
const params = {
|
||||
getRoutes: "2",
|
||||
};
|
||||
@@ -89,8 +117,14 @@ export class ApiBasedRepositoryLoader {
|
||||
};
|
||||
|
||||
await this.repository.addOrUpdateRoute(constructedRoute);
|
||||
|
||||
routeIdsToPrune.delete(constructedRoute.id);
|
||||
}))
|
||||
}
|
||||
|
||||
await Promise.all(Array.from(routeIdsToPrune).map(async (routeId) => {
|
||||
await this.repository.removeRouteIfExists(routeId);
|
||||
}));
|
||||
} catch(e: any) {
|
||||
throw new ApiResponseError(e.message);
|
||||
}
|
||||
@@ -106,6 +140,10 @@ export class ApiBasedRepositoryLoader {
|
||||
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);
|
||||
});
|
||||
|
||||
const params = {
|
||||
getStops: "2",
|
||||
};
|
||||
@@ -126,9 +164,13 @@ export class ApiBasedRepositoryLoader {
|
||||
});
|
||||
const json = await response.json();
|
||||
|
||||
await this.updateStopDataForSystemAndApiResponse(systemId, json);
|
||||
await this.updateStopDataForSystemAndApiResponse(systemId, json, stopIdsToPrune);
|
||||
await this.updateOrderedStopDataForExistingStops(json);
|
||||
await this.updatePolylineDataForExistingRoutesAndApiResponse(json);
|
||||
|
||||
await Promise.all(Array.from(stopIdsToPrune).map(async (stopId) => {
|
||||
await this.repository.removeStopIfExists(stopId);
|
||||
}));
|
||||
} catch(e: any) {
|
||||
throw new ApiResponseError(e.message);
|
||||
}
|
||||
@@ -143,6 +185,10 @@ export class ApiBasedRepositoryLoader {
|
||||
}
|
||||
|
||||
public async fetchAndUpdateShuttleDataForSystemId(systemId: string) {
|
||||
const shuttleIdsToPrune = await this.constructExistingEntityIdSet(async () => {
|
||||
return await this.repository.getShuttlesBySystemId(systemId);
|
||||
});
|
||||
|
||||
const params = {
|
||||
getBuses: "2"
|
||||
};
|
||||
@@ -182,8 +228,14 @@ export class ApiBasedRepositoryLoader {
|
||||
}
|
||||
|
||||
await this.repository.addOrUpdateShuttle(constructedShuttle);
|
||||
|
||||
shuttleIdsToPrune.delete(constructedShuttle.id);
|
||||
}));
|
||||
}
|
||||
|
||||
await Promise.all(Array.from(shuttleIdsToPrune).map(async (shuttleId) => {
|
||||
await this.repository.removeShuttleIfExists(shuttleId);
|
||||
}));
|
||||
} catch(e: any) {
|
||||
throw new ApiResponseError(e.message);
|
||||
}
|
||||
@@ -240,7 +292,11 @@ export class ApiBasedRepositoryLoader {
|
||||
}
|
||||
}
|
||||
|
||||
protected async updateStopDataForSystemAndApiResponse(systemId: string, json: any) {
|
||||
protected async updateStopDataForSystemAndApiResponse(
|
||||
systemId: string,
|
||||
json: any,
|
||||
setOfIdsToPrune: Set<string> = new Set(),
|
||||
) {
|
||||
if (json.stops) {
|
||||
const jsonStops = Object.values(json.stops);
|
||||
|
||||
@@ -256,6 +312,8 @@ export class ApiBasedRepositoryLoader {
|
||||
};
|
||||
|
||||
await this.repository.addOrUpdateStop(constructedStop);
|
||||
|
||||
setOfIdsToPrune.delete(constructedStop.id);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,15 +47,10 @@ export class TimedApiBasedRepositoryLoader extends ApiBasedRepositoryLoader {
|
||||
if (!this.shouldBeRunning) return;
|
||||
|
||||
try {
|
||||
await this.repository.clearSystemData();
|
||||
await this.fetchAndUpdateSystemData();
|
||||
await this.repository.clearRouteData();
|
||||
await this.fetchAndUpdateRouteDataForExistingSystemsInRepository();
|
||||
await this.repository.clearStopData();
|
||||
await this.fetchAndUpdateStopAndPolylineDataForRoutesInExistingSystemsInRepository();
|
||||
await this.repository.clearShuttleData();
|
||||
await this.fetchAndUpdateShuttleDataForExistingSystemsInRepository();
|
||||
await this.repository.clearEtaData();
|
||||
await this.fetchAndUpdateEtaDataForExistingStopsForSystemsInRepository();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
|
||||
@@ -19,6 +19,13 @@ export interface GetterSetterRepository extends GetterRepository {
|
||||
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>;
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
import { GetterSetterRepository } from "./GetterSetterRepository";
|
||||
import {
|
||||
IEntityWithId,
|
||||
IEta,
|
||||
IOrderedStop,
|
||||
IRoute,
|
||||
IShuttle,
|
||||
IStop,
|
||||
ISystem
|
||||
} from "../entities/entities";
|
||||
import { IEntityWithId, IEta, IOrderedStop, IRoute, IShuttle, IStop, ISystem } from "../entities/entities";
|
||||
|
||||
/**
|
||||
* An unoptimized in memory repository.
|
||||
@@ -148,6 +140,51 @@ export class UnoptimizedInMemoryRepository implements GetterSetterRepository {
|
||||
}
|
||||
}
|
||||
|
||||
private async removeEntityByMatcherIfExists<T>(callback: (value: T) => boolean, arrayToSearchIn: T[]) {
|
||||
const index = arrayToSearchIn.findIndex(callback);
|
||||
if (index > -1) {
|
||||
const entityToReturn = arrayToSearchIn[index];
|
||||
arrayToSearchIn.splice(index, 1);
|
||||
return entityToReturn;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private async removeEntityByIdIfExists<T extends IEntityWithId>(entityId: string, arrayToSearchIn: T[]) {
|
||||
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);
|
||||
}
|
||||
|
||||
public async removeShuttleIfExists(shuttleId: string): Promise<IShuttle | null> {
|
||||
return await this.removeEntityByIdIfExists(shuttleId, this.shuttles);
|
||||
}
|
||||
|
||||
public async removeStopIfExists(stopId: string): Promise<IStop | null> {
|
||||
return await this.removeEntityByIdIfExists(stopId, this.stops);
|
||||
}
|
||||
|
||||
public async removeOrderedStopIfExists(stopId: string, routeId: string): Promise<IOrderedStop | null> {
|
||||
return await this.removeEntityByMatcherIfExists((orderedStop) => {
|
||||
return orderedStop.stopId === stopId
|
||||
&& orderedStop.routeId === routeId
|
||||
}, this.orderedStops);
|
||||
}
|
||||
|
||||
public async removeEtaIfExists(shuttleId: string, stopId: string): Promise<IEta | null> {
|
||||
return await this.removeEntityByMatcherIfExists((eta) => {
|
||||
return eta.stopId === stopId
|
||||
&& eta.shuttleId === shuttleId
|
||||
}, this.etas);
|
||||
}
|
||||
|
||||
public async clearSystemData() {
|
||||
this.systems = [];
|
||||
}
|
||||
@@ -171,4 +208,5 @@ export class UnoptimizedInMemoryRepository implements GetterSetterRepository {
|
||||
public async clearStopData(): Promise<void> {
|
||||
this.stops = [];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,7 +7,14 @@ import { fetchRouteDataSuccessfulResponse } from "../jsonSnapshots/fetchRouteDat
|
||||
import {
|
||||
fetchStopAndPolylineDataSuccessfulResponse
|
||||
} from "../jsonSnapshots/fetchStopAndPolylineData/fetchStopAndPolylineDataSuccessfulResponse";
|
||||
import { generateMockStops, generateMockSystems } from "../generators";
|
||||
import {
|
||||
generateMockEtas,
|
||||
generateMockOrderedStops,
|
||||
generateMockRoutes,
|
||||
generateMockShuttles,
|
||||
generateMockStops,
|
||||
generateMockSystems
|
||||
} from "../generators";
|
||||
import { IStop } from "../../src/entities/entities";
|
||||
import {
|
||||
fetchShuttleDataSuccessfulResponse
|
||||
@@ -33,11 +40,19 @@ describe("ApiBasedRepositoryLoader", () => {
|
||||
|
||||
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);
|
||||
@@ -81,12 +96,23 @@ describe("ApiBasedRepositoryLoader", () => {
|
||||
});
|
||||
|
||||
describe("fetchAndUpdateRouteDataForSystemId", () => {
|
||||
const systemId = "263";
|
||||
it("updates route data in repository if response received", async () => {
|
||||
// Arrange
|
||||
// Test pruning
|
||||
const routesToPrune = generateMockRoutes();
|
||||
await Promise.all(routesToPrune.map(async (route) => {
|
||||
route.systemId = systemId;
|
||||
await loader.repository.addOrUpdateRoute(route);
|
||||
}));
|
||||
|
||||
updateGlobalFetchMockJson(fetchRouteDataSuccessfulResponse);
|
||||
|
||||
await loader.fetchAndUpdateRouteDataForSystemId("263");
|
||||
// Act
|
||||
await loader.fetchAndUpdateRouteDataForSystemId(systemId);
|
||||
|
||||
const routes = await loader.repository.getRoutesBySystemId("263");
|
||||
// Assert
|
||||
const routes = await loader.repository.getRoutesBySystemId(systemId);
|
||||
|
||||
expect(routes.length).toEqual(fetchRouteDataSuccessfulResponse.all.length)
|
||||
});
|
||||
@@ -98,7 +124,7 @@ describe("ApiBasedRepositoryLoader", () => {
|
||||
updateGlobalFetchMockJsonToThrowSyntaxError();
|
||||
|
||||
await assertAsyncCallbackThrowsApiResponseError(async () => {
|
||||
await loader.fetchAndUpdateRouteDataForSystemId("263");
|
||||
await loader.fetchAndUpdateRouteDataForSystemId(systemId);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -120,14 +146,23 @@ describe("ApiBasedRepositoryLoader", () => {
|
||||
})
|
||||
|
||||
describe("fetchAndUpdateStopAndPolylineDataForRoutesWithSystemId", () => {
|
||||
const systemId = "263";
|
||||
it("updates stop and polyline data if response received", async () => {
|
||||
// Arrange
|
||||
// Test pruning of stops only
|
||||
const stopsToPrune = generateMockStops();
|
||||
await Promise.all(stopsToPrune.map(async (stop) => {
|
||||
stop.systemId = systemId;
|
||||
await loader.repository.addOrUpdateStop(stop);
|
||||
}));
|
||||
|
||||
updateGlobalFetchMockJson(fetchStopAndPolylineDataSuccessfulResponse);
|
||||
|
||||
const stopsArray = Object.values(fetchStopAndPolylineDataSuccessfulResponse.stops);
|
||||
|
||||
await loader.fetchAndUpdateStopAndPolylineDataForRoutesWithSystemId("263");
|
||||
await loader.fetchAndUpdateStopAndPolylineDataForRoutesWithSystemId(systemId);
|
||||
|
||||
const stops = await loader.repository.getStopsBySystemId("263");
|
||||
const stops = await loader.repository.getStopsBySystemId(systemId);
|
||||
expect(stops.length).toEqual(stopsArray.length);
|
||||
|
||||
await Promise.all(stops.map(async (stop) => {
|
||||
@@ -135,7 +170,7 @@ describe("ApiBasedRepositoryLoader", () => {
|
||||
expect(orderedStops.length).toBeGreaterThan(0);
|
||||
}));
|
||||
|
||||
const routes = await loader.repository.getRoutesBySystemId("263");
|
||||
const routes = await loader.repository.getRoutesBySystemId(systemId);
|
||||
routes.forEach((route) => {
|
||||
expect(route.polylineCoordinates.length).toBeGreaterThan(0);
|
||||
});
|
||||
@@ -145,7 +180,7 @@ describe("ApiBasedRepositoryLoader", () => {
|
||||
updateGlobalFetchMockJsonToThrowSyntaxError();
|
||||
|
||||
await assertAsyncCallbackThrowsApiResponseError(async () => {
|
||||
await loader.fetchAndUpdateStopAndPolylineDataForRoutesWithSystemId("263");
|
||||
await loader.fetchAndUpdateStopAndPolylineDataForRoutesWithSystemId(systemId);
|
||||
});
|
||||
})
|
||||
});
|
||||
@@ -166,13 +201,20 @@ describe("ApiBasedRepositoryLoader", () => {
|
||||
});
|
||||
|
||||
describe("fetchAndUpdateShuttleDataForSystemId", () => {
|
||||
const systemId = "263";
|
||||
it("updates shuttle data in repository if response received", async () => {
|
||||
const shuttlesToPrune = generateMockShuttles();
|
||||
await Promise.all(shuttlesToPrune.map(async (shuttle) => {
|
||||
shuttle.systemId = systemId;
|
||||
await loader.repository.addOrUpdateShuttle(shuttle);
|
||||
}))
|
||||
|
||||
updateGlobalFetchMockJson(fetchShuttleDataSuccessfulResponse);
|
||||
const busesInResponse = Object.values(fetchShuttleDataSuccessfulResponse.buses);
|
||||
|
||||
await loader.fetchAndUpdateShuttleDataForSystemId("263");
|
||||
await loader.fetchAndUpdateShuttleDataForSystemId(systemId);
|
||||
|
||||
const shuttles = await loader.repository.getShuttlesBySystemId("263");
|
||||
const shuttles = await loader.repository.getShuttlesBySystemId(systemId);
|
||||
|
||||
expect(shuttles.length).toEqual(busesInResponse.length);
|
||||
});
|
||||
@@ -181,7 +223,7 @@ describe("ApiBasedRepositoryLoader", () => {
|
||||
updateGlobalFetchMockJsonToThrowSyntaxError();
|
||||
|
||||
await assertAsyncCallbackThrowsApiResponseError(async () => {
|
||||
await loader.fetchAndUpdateShuttleDataForSystemId("263");
|
||||
await loader.fetchAndUpdateShuttleDataForSystemId(systemId);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -221,9 +263,9 @@ describe("ApiBasedRepositoryLoader", () => {
|
||||
});
|
||||
|
||||
describe("fetchAndUpdateEtaDataForStopId", () => {
|
||||
const stopId = "177666";
|
||||
it("updates ETA data for stop id if response received", async () => {
|
||||
updateGlobalFetchMockJson(fetchEtaDataSuccessfulResponse);
|
||||
const stopId = "177666";
|
||||
// @ts-ignore
|
||||
const etasFromResponse = fetchEtaDataSuccessfulResponse.ETAs[stopId]
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, jest } from "@jest/globals";
|
||||
import { TimedApiBasedRepositoryLoader } from "../../src/loaders/TimedApiBasedRepositoryLoader";
|
||||
import { resetGlobalFetchMockJson } from "../mockHelpers/fetchMockHelpers";
|
||||
import { GetterSetterRepository } from "../../src/repositories/GetterSetterRepository";
|
||||
import { UnoptimizedInMemoryRepository } from "../../src/repositories/UnoptimizedInMemoryRepository";
|
||||
|
||||
describe("TimedApiBasedRepositoryLoader", () => {
|
||||
let repositoryMock: GetterSetterRepository;
|
||||
let loader: TimedApiBasedRepositoryLoader;
|
||||
let spies: any;
|
||||
|
||||
@@ -16,15 +15,7 @@ describe("TimedApiBasedRepositoryLoader", () => {
|
||||
beforeEach(() => {
|
||||
resetGlobalFetchMockJson();
|
||||
|
||||
repositoryMock = {
|
||||
clearSystemData: jest.fn(),
|
||||
clearRouteData: jest.fn(),
|
||||
clearStopData: jest.fn(),
|
||||
clearShuttleData: jest.fn(),
|
||||
clearEtaData: jest.fn(),
|
||||
} as unknown as GetterSetterRepository;
|
||||
|
||||
loader = new TimedApiBasedRepositoryLoader(repositoryMock);
|
||||
loader = new TimedApiBasedRepositoryLoader(new UnoptimizedInMemoryRepository());
|
||||
|
||||
spies = {
|
||||
fetchAndUpdateSystemData: jest.spyOn(loader, 'fetchAndUpdateSystemData'),
|
||||
@@ -49,9 +40,6 @@ describe("TimedApiBasedRepositoryLoader", () => {
|
||||
await loader.start();
|
||||
expect(loader["shouldBeRunning"]).toBe(true);
|
||||
|
||||
Object.values(repositoryMock).forEach((mockFn) => {
|
||||
expect(mockFn).toHaveBeenCalled();
|
||||
});
|
||||
Object.values(spies).forEach((spy: any) => {
|
||||
expect(spy).toHaveBeenCalled();
|
||||
});
|
||||
@@ -64,9 +52,6 @@ describe("TimedApiBasedRepositoryLoader", () => {
|
||||
await loader.start();
|
||||
await loader.start();
|
||||
|
||||
Object.values(repositoryMock).forEach((mockFn) => {
|
||||
expect(mockFn).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
Object.values(spies).forEach((spy: any) => {
|
||||
expect(spy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
@@ -429,6 +429,194 @@ 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";
|
||||
const mockRoutes = generateMockRoutes();
|
||||
await Promise.all(mockRoutes.map(async (route) => {
|
||||
route.systemId = systemId;
|
||||
await repository.addOrUpdateRoute(route);
|
||||
}));
|
||||
|
||||
const routeToRemove = mockRoutes[0];
|
||||
await repository.removeRouteIfExists(routeToRemove.id);
|
||||
|
||||
const remainingRoutes = await repository.getRoutesBySystemId(systemId);
|
||||
expect(remainingRoutes).toHaveLength(mockRoutes.length - 1);
|
||||
});
|
||||
|
||||
test("does nothing if route doesn't exist", async () => {
|
||||
const systemId = "1";
|
||||
const mockRoutes = generateMockRoutes();
|
||||
await Promise.all(mockRoutes.map(async (route) => {
|
||||
route.systemId = systemId;
|
||||
await repository.addOrUpdateRoute(route);
|
||||
}));
|
||||
|
||||
await repository.removeRouteIfExists("nonexistent-id");
|
||||
|
||||
const remainingRoutes = await repository.getRoutesBySystemId(systemId);
|
||||
expect(remainingRoutes).toHaveLength(mockRoutes.length);
|
||||
});
|
||||
});
|
||||
|
||||
describe("removeShuttleIfExists", () => {
|
||||
test("removes shuttle given ID", async () => {
|
||||
const systemId = "1";
|
||||
const mockShuttles = generateMockShuttles();
|
||||
await Promise.all(mockShuttles.map(async (shuttle) => {
|
||||
shuttle.systemId = systemId;
|
||||
await repository.addOrUpdateShuttle(shuttle);
|
||||
}));
|
||||
|
||||
const shuttleToRemove = mockShuttles[0];
|
||||
await repository.removeShuttleIfExists(shuttleToRemove.id);
|
||||
|
||||
const remainingShuttles = await repository.getShuttlesBySystemId(systemId);
|
||||
expect(remainingShuttles).toHaveLength(mockShuttles.length - 1);
|
||||
});
|
||||
|
||||
test("does nothing if shuttle doesn't exist", async () => {
|
||||
const systemId = "1";
|
||||
const mockShuttles = generateMockShuttles();
|
||||
await Promise.all(mockShuttles.map(async (shuttle) => {
|
||||
shuttle.systemId = systemId;
|
||||
await repository.addOrUpdateShuttle(shuttle);
|
||||
}));
|
||||
|
||||
await repository.removeShuttleIfExists("nonexistent-id");
|
||||
|
||||
const remainingShuttles = await repository.getShuttlesBySystemId(systemId);
|
||||
expect(remainingShuttles).toHaveLength(mockShuttles.length);
|
||||
});
|
||||
});
|
||||
|
||||
describe("removeStopIfExists", () => {
|
||||
test("removes stop given ID", async () => {
|
||||
const systemId = "1";
|
||||
const mockStops = generateMockStops();
|
||||
await Promise.all(mockStops.map(async (stop) => {
|
||||
stop.systemId = systemId;
|
||||
await repository.addOrUpdateStop(stop);
|
||||
}));
|
||||
|
||||
const stopToRemove = mockStops[0];
|
||||
await repository.removeStopIfExists(stopToRemove.id);
|
||||
|
||||
const remainingStops = await repository.getStopsBySystemId(systemId);
|
||||
expect(remainingStops).toHaveLength(mockStops.length - 1);
|
||||
});
|
||||
|
||||
test("does nothing if stop doesn't exist", async () => {
|
||||
const systemId = "1";
|
||||
const mockStops = generateMockStops();
|
||||
await Promise.all(mockStops.map(async (stop) => {
|
||||
stop.systemId = systemId;
|
||||
await repository.addOrUpdateStop(stop);
|
||||
}));
|
||||
|
||||
await repository.removeStopIfExists("nonexistent-id");
|
||||
|
||||
const remainingStops = await repository.getStopsBySystemId(systemId);
|
||||
expect(remainingStops).toHaveLength(mockStops.length);
|
||||
});
|
||||
});
|
||||
|
||||
describe("removeOrderedStopIfExists", () => {
|
||||
test("removes ordered stop given stop ID and route ID", async () => {
|
||||
let mockOrderedStops = generateMockOrderedStops();
|
||||
const routeId = mockOrderedStops[0].routeId;
|
||||
mockOrderedStops = mockOrderedStops.filter((orderedStop) => orderedStop.routeId === routeId);
|
||||
await Promise.all(mockOrderedStops.map(async (stop) => {
|
||||
stop.routeId = routeId;
|
||||
await repository.addOrUpdateOrderedStop(stop);
|
||||
}));
|
||||
|
||||
const orderedStopToRemove = mockOrderedStops[0];
|
||||
await repository.removeOrderedStopIfExists(orderedStopToRemove.stopId, orderedStopToRemove.routeId);
|
||||
|
||||
const remainingOrderedStops = await repository.getOrderedStopsByRouteId(routeId);
|
||||
expect(remainingOrderedStops).toHaveLength(mockOrderedStops.length - 1);
|
||||
});
|
||||
|
||||
test("does nothing if ordered stop doesn't exist", async () => {
|
||||
let mockOrderedStops = generateMockOrderedStops();
|
||||
const routeId = mockOrderedStops[0].routeId;
|
||||
mockOrderedStops = mockOrderedStops.filter((orderedStop) => orderedStop.routeId === routeId);
|
||||
await Promise.all(mockOrderedStops.map(async (stop) => {
|
||||
stop.routeId = routeId;
|
||||
await repository.addOrUpdateOrderedStop(stop);
|
||||
}));
|
||||
|
||||
await repository.removeOrderedStopIfExists("nonexistent-stop-id", "nonexistent-route-id");
|
||||
|
||||
const remainingOrderedStops = await repository.getOrderedStopsByRouteId(routeId);
|
||||
expect(remainingOrderedStops).toHaveLength(mockOrderedStops.length);
|
||||
});
|
||||
});
|
||||
|
||||
describe("removeEtaIfExists", () => {
|
||||
test("removes eta given shuttle ID and stop ID", async () => {
|
||||
let mockEtas = generateMockEtas();
|
||||
const stopId = mockEtas[0].stopId;
|
||||
mockEtas = mockEtas.filter((eta) => eta.stopId === stopId);
|
||||
|
||||
await Promise.all(mockEtas.map(async (eta) => {
|
||||
eta.stopId = stopId;
|
||||
await repository.addOrUpdateEta(eta);
|
||||
}));
|
||||
|
||||
const etaToRemove = mockEtas[0];
|
||||
await repository.removeEtaIfExists(etaToRemove.shuttleId, etaToRemove.stopId);
|
||||
|
||||
const remainingEtas = await repository.getEtasForStopId(stopId);
|
||||
expect(remainingEtas).toHaveLength(mockEtas.length - 1);
|
||||
});
|
||||
|
||||
test("does nothing if eta doesn't exist", async () => {
|
||||
let mockEtas = generateMockEtas();
|
||||
const stopId = mockEtas[0].stopId;
|
||||
mockEtas = mockEtas.filter((eta) => eta.stopId === stopId);
|
||||
|
||||
await Promise.all(mockEtas.map(async (eta) => {
|
||||
eta.stopId = stopId;
|
||||
await repository.addOrUpdateEta(eta);
|
||||
}));
|
||||
|
||||
await repository.removeEtaIfExists("nonexistent-shuttle-id", "nonexistent-stop-id");
|
||||
|
||||
const remainingEtas = await repository.getEtasForStopId(stopId);
|
||||
expect(remainingEtas).toHaveLength(mockEtas.length);
|
||||
});
|
||||
});
|
||||
|
||||
describe("clearSystemData", () => {
|
||||
test("clears all systems from the repository", async () => {
|
||||
const mockSystems = generateMockSystems();
|
||||
|
||||
Reference in New Issue
Block a user