mirror of
https://github.com/brendan-ch/project-inter-server.git
synced 2026-04-17 07:50:31 +00:00
Merge pull request #9 from brendan-ch/chore/test-api-based-repository-loader
chore/test-api-based-repository-loader
This commit is contained in:
6
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
6
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="ExceptionCaughtLocallyJS" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
</profile>
|
||||
</component>
|
||||
@@ -1,26 +1,39 @@
|
||||
import { GetterSetterRepository } from "../repositories/GetterSetterRepository";
|
||||
import { IEta, IRoute, IShuttle, IStop, ISystem } from "../entities/entities";
|
||||
|
||||
const systemIdsToSupport = ["263"];
|
||||
const baseUrl = "https://passiogo.com/mapGetData.php";
|
||||
export class ApiResponseError extends Error {
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
this.name = "ApiResponseError";
|
||||
}
|
||||
}
|
||||
|
||||
export class ApiBasedRepositoryLoader {
|
||||
supportedSystemIds = ["263"];
|
||||
baseUrl = "https://passiogo.com/mapGetData.php";
|
||||
|
||||
constructor(
|
||||
protected repository: GetterSetterRepository,
|
||||
public repository: GetterSetterRepository,
|
||||
) {
|
||||
}
|
||||
|
||||
protected async fetchAndUpdateSystemData() {
|
||||
public async fetchAndUpdateSystemData() {
|
||||
const params = {
|
||||
getSystems: "2",
|
||||
};
|
||||
const query = new URLSearchParams(params).toString();
|
||||
const response = await fetch(`${baseUrl}?${query}`);
|
||||
const json = await response.json()
|
||||
|
||||
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) => systemIdsToSupport.includes(jsonSystem.id));
|
||||
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,
|
||||
@@ -29,25 +42,37 @@ export class ApiBasedRepositoryLoader {
|
||||
|
||||
await this.repository.addOrUpdateSystem(constructedSystem);
|
||||
}));
|
||||
} else {
|
||||
throw new Error("Received JSON object does not contain `all` field")
|
||||
}
|
||||
} catch(e: any) {
|
||||
throw new ApiResponseError(e.message);
|
||||
}
|
||||
}
|
||||
|
||||
protected async fetchAndUpdateRouteDataForExistingSystems() {
|
||||
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) {
|
||||
const params = {
|
||||
getRoutes: "2",
|
||||
};
|
||||
|
||||
const formDataJsonObject = {
|
||||
"systemSelected0": system.id,
|
||||
"systemSelected0": systemId,
|
||||
"amount": "1",
|
||||
}
|
||||
const formData = new FormData();
|
||||
formData.set("json", JSON.stringify(formDataJsonObject));
|
||||
|
||||
const query = new URLSearchParams(params).toString();
|
||||
const response = await fetch(`${baseUrl}?${query}`, {
|
||||
|
||||
try {
|
||||
const response = await fetch(`${this.baseUrl}?${query}`, {
|
||||
method: "POST",
|
||||
body: formData,
|
||||
});
|
||||
@@ -60,53 +85,70 @@ export class ApiBasedRepositoryLoader {
|
||||
color: jsonRoute.color,
|
||||
id: jsonRoute.myid,
|
||||
polylineCoordinates: [],
|
||||
systemId: system.id,
|
||||
systemId: systemId,
|
||||
};
|
||||
|
||||
await this.repository.addOrUpdateRoute(constructedRoute);
|
||||
}))
|
||||
}
|
||||
} catch(e: any) {
|
||||
throw new ApiResponseError(e.message);
|
||||
}
|
||||
}
|
||||
|
||||
public async fetchAndUpdateStopAndPolylineDataForRoutesInExistingSystemsInRepository() {
|
||||
const systems = await this.repository.getSystems();
|
||||
await Promise.all(systems.map(async (system: ISystem) => {
|
||||
await this.fetchAndUpdateStopAndPolylineDataForRoutesWithSystemId(system.id);
|
||||
}));
|
||||
}
|
||||
|
||||
protected async fetchAndUpdateStopAndPolylineDataForRoutesInExistingSystems() {
|
||||
public async fetchAndUpdateStopAndPolylineDataForRoutesWithSystemId(systemId: string) {
|
||||
// Fetch from the API
|
||||
// Pass JSON output into two different methods to update repository
|
||||
const systems = await this.repository.getSystems();
|
||||
await Promise.all(systems.map(async (system: ISystem) => {
|
||||
const params = {
|
||||
getStops: "2",
|
||||
};
|
||||
|
||||
const formDataJsonObject = {
|
||||
"s0": system.id,
|
||||
"s0": systemId,
|
||||
"sA": 1
|
||||
};
|
||||
const formData = new FormData();
|
||||
formData.set("json", JSON.stringify(formDataJsonObject));
|
||||
|
||||
const query = new URLSearchParams(params).toString();
|
||||
const response = await fetch(`${baseUrl}?${query}`, {
|
||||
|
||||
try {
|
||||
const response = await fetch(`${this.baseUrl}?${query}`, {
|
||||
method: "POST",
|
||||
body: formData,
|
||||
});
|
||||
const json = await response.json();
|
||||
|
||||
await this.updateStopDataForSystemAndApiResponse(system, json);
|
||||
await this.updateStopDataForSystemAndApiResponse(systemId, json);
|
||||
await this.updateOrderedStopDataForExistingStops(json);
|
||||
await this.updatePolylineDataForExistingRoutesAndApiResponse(json);
|
||||
} catch(e: any) {
|
||||
throw new ApiResponseError(e.message);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}));
|
||||
}
|
||||
|
||||
protected async fetchAndUpdateShuttleDataForExistingSystems() {
|
||||
const systems = await this.repository.getSystems();
|
||||
await Promise.all(systems.map(async (system: ISystem) => {
|
||||
public async fetchAndUpdateShuttleDataForSystemId(systemId: string) {
|
||||
const params = {
|
||||
getBuses: "2"
|
||||
};
|
||||
|
||||
const formDataJsonObject = {
|
||||
"s0": system.id,
|
||||
"s0": systemId,
|
||||
"sA": "1"
|
||||
};
|
||||
|
||||
@@ -114,7 +156,9 @@ export class ApiBasedRepositoryLoader {
|
||||
formData.set("json", JSON.stringify(formDataJsonObject));
|
||||
|
||||
const query = new URLSearchParams(params).toString();
|
||||
const response = await fetch(`${baseUrl}?${query}`, {
|
||||
|
||||
try {
|
||||
const response = await fetch(`${this.baseUrl}?${query}`, {
|
||||
method: "POST",
|
||||
body: formData,
|
||||
});
|
||||
@@ -133,55 +177,70 @@ export class ApiBasedRepositoryLoader {
|
||||
longitude: parseFloat(jsonBus.longitude),
|
||||
},
|
||||
routeId: jsonBus.routeId,
|
||||
systemId: system.id,
|
||||
systemId: systemId,
|
||||
id: `${jsonBus.busId}`
|
||||
}
|
||||
|
||||
await this.repository.addOrUpdateShuttle(constructedShuttle);
|
||||
}));
|
||||
}
|
||||
} catch(e: any) {
|
||||
throw new ApiResponseError(e.message);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
await Promise.all(stops.map(async (stop) => {
|
||||
let stopId = stop.id;
|
||||
await this.fetchAndUpdateEtaDataForStopId(stopId);
|
||||
}));
|
||||
}
|
||||
|
||||
protected async fetchAndUpdateEtaDataForExistingOrderedStops() {
|
||||
// TODO implement once I figure out how to associate ETA data with shuttles
|
||||
|
||||
const systems = await this.repository.getSystems()
|
||||
await Promise.all(systems.map(async (system: ISystem) => {
|
||||
const stops = await this.repository.getStopsBySystemId(system.id);
|
||||
await Promise.all(stops.map(async (stop) => {
|
||||
public async fetchAndUpdateEtaDataForStopId(stopId: string) {
|
||||
const params = {
|
||||
eta: "3",
|
||||
stopIds: stop.id,
|
||||
stopIds: stopId,
|
||||
};
|
||||
|
||||
const query = new URLSearchParams(params).toString();
|
||||
const response = await fetch(`${baseUrl}?${query}`, {
|
||||
|
||||
try {
|
||||
const response = await fetch(`${this.baseUrl}?${query}`, {
|
||||
method: "GET",
|
||||
});
|
||||
const json = await response.json();
|
||||
|
||||
if (json.ETAs && json.ETAs[stop.id]) {
|
||||
if (json.ETAs && json.ETAs[stopId]) {
|
||||
// Continue with the parsing
|
||||
json.ETAs[stop.id].forEach((jsonEta: any) => {
|
||||
json.ETAs[stopId].forEach((jsonEta: any) => {
|
||||
// Update cache
|
||||
const shuttleId: string = jsonEta.busId;
|
||||
|
||||
const eta: IEta = {
|
||||
secondsRemaining: jsonEta.secondsSpent,
|
||||
shuttleId: `${shuttleId}`,
|
||||
stopId: stop.id,
|
||||
stopId: stopId,
|
||||
millisecondsSinceEpoch: Date.now(),
|
||||
};
|
||||
|
||||
this.repository.addOrUpdateEta(eta);
|
||||
});
|
||||
}
|
||||
}));
|
||||
}))
|
||||
} catch(e: any) {
|
||||
throw new ApiResponseError(e.message);
|
||||
}
|
||||
}
|
||||
|
||||
protected async updateStopDataForSystemAndApiResponse(system: ISystem, json: any) {
|
||||
protected async updateStopDataForSystemAndApiResponse(systemId: string, json: any) {
|
||||
if (json.stops) {
|
||||
const jsonStops = Object.values(json.stops);
|
||||
|
||||
@@ -189,7 +248,7 @@ export class ApiBasedRepositoryLoader {
|
||||
const constructedStop: IStop = {
|
||||
name: stop.name,
|
||||
id: stop.id,
|
||||
systemId: system.id,
|
||||
systemId,
|
||||
coordinates: {
|
||||
latitude: parseFloat(stop.latitude),
|
||||
longitude: parseFloat(stop.longitude),
|
||||
|
||||
@@ -50,13 +50,13 @@ export class TimedApiBasedRepositoryLoader extends ApiBasedRepositoryLoader {
|
||||
await this.repository.clearSystemData();
|
||||
await this.fetchAndUpdateSystemData();
|
||||
await this.repository.clearRouteData();
|
||||
await this.fetchAndUpdateRouteDataForExistingSystems();
|
||||
await this.fetchAndUpdateRouteDataForExistingSystemsInRepository();
|
||||
await this.repository.clearStopData();
|
||||
await this.fetchAndUpdateStopAndPolylineDataForRoutesInExistingSystems();
|
||||
await this.fetchAndUpdateStopAndPolylineDataForRoutesInExistingSystemsInRepository();
|
||||
await this.repository.clearShuttleData();
|
||||
await this.fetchAndUpdateShuttleDataForExistingSystems();
|
||||
await this.fetchAndUpdateShuttleDataForExistingSystemsInRepository();
|
||||
await this.repository.clearEtaData();
|
||||
await this.fetchAndUpdateEtaDataForExistingOrderedStops();
|
||||
await this.fetchAndUpdateEtaDataForExistingStopsForSystemsInRepository();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Snapshot taken from the Passio GO! API
|
||||
export const genericEtaDataByStopId = {
|
||||
export const fetchEtaDataSuccessfulResponse = {
|
||||
"ETAs": {
|
||||
"177666": [
|
||||
{
|
||||
@@ -0,0 +1,153 @@
|
||||
export const fetchRouteDataSuccessfulResponse = {
|
||||
"all": [
|
||||
{
|
||||
"name": "Rinker Route",
|
||||
"shortName": null,
|
||||
"color": "#696969",
|
||||
"userId": "263",
|
||||
"myid": "53970",
|
||||
"mapApp": "1",
|
||||
"archive": "0",
|
||||
"goPrefixRouteName": "1",
|
||||
"goShowSchedule": 0,
|
||||
"outdated": "0",
|
||||
"id": "5902",
|
||||
"distance": 1900,
|
||||
"latitude": "33.656851000",
|
||||
"longitude": "-117.733221000",
|
||||
"timezone": "America/Los_Angeles",
|
||||
"fullname": "Chapman University",
|
||||
"nameOrig": "Rinker Route",
|
||||
"groupId": "6706",
|
||||
"groupColor": "#696969"
|
||||
},
|
||||
{
|
||||
"name": "Red Route",
|
||||
"shortName": null,
|
||||
"color": "#d62728",
|
||||
"userId": "263",
|
||||
"myid": "53966",
|
||||
"mapApp": "1",
|
||||
"archive": "0",
|
||||
"goPrefixRouteName": "1",
|
||||
"goShowSchedule": 0,
|
||||
"outdated": "0",
|
||||
"id": "2032",
|
||||
"distance": 1905,
|
||||
"latitude": "33.793325000",
|
||||
"longitude": "-117.852810000",
|
||||
"timezone": "America/Los_Angeles",
|
||||
"fullname": "Chapman University",
|
||||
"nameOrig": "Red Route",
|
||||
"groupId": "6703",
|
||||
"groupColor": "#d62728"
|
||||
},
|
||||
{
|
||||
"name": "Teal Route",
|
||||
"shortName": null,
|
||||
"color": "#096e90",
|
||||
"userId": "263",
|
||||
"myid": "54191",
|
||||
"mapApp": "1",
|
||||
"archive": "0",
|
||||
"goPrefixRouteName": "1",
|
||||
"goShowSchedule": 0,
|
||||
"outdated": "0",
|
||||
"id": "2032",
|
||||
"distance": 1905,
|
||||
"latitude": "33.793325000",
|
||||
"longitude": "-117.852810000",
|
||||
"timezone": "America/Los_Angeles",
|
||||
"fullname": "Chapman University",
|
||||
"nameOrig": "Teal Route",
|
||||
"groupId": "6705",
|
||||
"groupColor": "#096e90"
|
||||
},
|
||||
{
|
||||
"name": "Gold Route",
|
||||
"shortName": null,
|
||||
"color": "#bd9e39",
|
||||
"userId": "263",
|
||||
"myid": "54256",
|
||||
"mapApp": "1",
|
||||
"archive": "0",
|
||||
"goPrefixRouteName": "1",
|
||||
"goShowSchedule": 0,
|
||||
"outdated": "0",
|
||||
"id": "2032",
|
||||
"distance": 1905,
|
||||
"latitude": "33.793325000",
|
||||
"longitude": "-117.852810000",
|
||||
"timezone": "America/Los_Angeles",
|
||||
"fullname": "Chapman University",
|
||||
"nameOrig": "Gold Route",
|
||||
"groupId": "6707",
|
||||
"groupColor": "#bd9e39"
|
||||
},
|
||||
{
|
||||
"name": "Black Route",
|
||||
"shortName": null,
|
||||
"color": "#000000",
|
||||
"userId": "263",
|
||||
"myid": "54257",
|
||||
"mapApp": "1",
|
||||
"archive": "0",
|
||||
"goPrefixRouteName": "1",
|
||||
"goShowSchedule": 0,
|
||||
"outdated": "0",
|
||||
"id": "2032",
|
||||
"distance": 1905,
|
||||
"latitude": "33.793325000",
|
||||
"longitude": "-117.852810000",
|
||||
"timezone": "America/Los_Angeles",
|
||||
"fullname": "Chapman University",
|
||||
"nameOrig": "Black Route",
|
||||
"groupId": "6704",
|
||||
"groupColor": "#000000"
|
||||
},
|
||||
{
|
||||
"name": "Weekend Route",
|
||||
"shortName": null,
|
||||
"color": "#510094",
|
||||
"userId": "263",
|
||||
"myid": "54282",
|
||||
"mapApp": "1",
|
||||
"archive": "0",
|
||||
"goPrefixRouteName": "1",
|
||||
"goShowSchedule": 0,
|
||||
"outdated": "1",
|
||||
"id": "2032",
|
||||
"distance": 1905,
|
||||
"latitude": "33.793325000",
|
||||
"longitude": "-117.852810000",
|
||||
"timezone": "America/Los_Angeles",
|
||||
"fullname": "Chapman University",
|
||||
"nameOrig": "Weekend Route",
|
||||
"groupId": "6710",
|
||||
"groupColor": "#510094",
|
||||
"serviceTime": "is not provided: no bus on the route",
|
||||
"serviceTimeShort": "No bus in service"
|
||||
},
|
||||
{
|
||||
"name": "Parking Lot",
|
||||
"shortName": null,
|
||||
"color": "#000000",
|
||||
"userId": "263",
|
||||
"myid": "54529",
|
||||
"mapApp": "1",
|
||||
"archive": "0",
|
||||
"goPrefixRouteName": "1",
|
||||
"goShowSchedule": 0,
|
||||
"outdated": "0",
|
||||
"id": "2032",
|
||||
"distance": 1905,
|
||||
"latitude": "33.793325000",
|
||||
"longitude": "-117.852810000",
|
||||
"timezone": "America/Los_Angeles",
|
||||
"fullname": "Chapman University",
|
||||
"nameOrig": "Parking Lot",
|
||||
"groupId": "6731",
|
||||
"groupColor": "#393838"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// Snapshot taken from the Passio GO! API
|
||||
export const genericShuttleDataBySystemId = {
|
||||
export const fetchShuttleDataSuccessfulResponse = {
|
||||
"alertCRC": "23c1b91c",
|
||||
"buses": {
|
||||
"402840": [
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,5 @@
|
||||
// Not an actual response snapshot
|
||||
// Simulate a case where `all` property of response is unavailable
|
||||
export const fetchSystemDataFailedResponse = {
|
||||
"error": "no systems",
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
279
test/loaders/ApiBasedRepositoryLoaderTests.test.ts
Normal file
279
test/loaders/ApiBasedRepositoryLoaderTests.test.ts
Normal file
@@ -0,0 +1,279 @@
|
||||
import { beforeEach, describe, expect, it, jest, test } from "@jest/globals";
|
||||
import { ApiBasedRepositoryLoader, ApiResponseError } from "../../src/loaders/ApiBasedRepositoryLoader";
|
||||
import { UnoptimizedInMemoryRepository } from "../../src/repositories/UnoptimizedInMemoryRepository";
|
||||
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 { generateMockStops, generateMockSystems } from "../generators";
|
||||
import { IStop } from "../../src/entities/entities";
|
||||
import {
|
||||
fetchShuttleDataSuccessfulResponse
|
||||
} from "../jsonSnapshots/fetchShuttleData/fetchShuttleDataSuccessfulResponse";
|
||||
import { fetchEtaDataSuccessfulResponse } from "../jsonSnapshots/fetchEtaData/fetchEtaDataSuccessfulResponse";
|
||||
|
||||
/**
|
||||
* Function to update behavior of the global `fetch` function.
|
||||
* Note that the Passio GO API returns status code 200 for failed responses.
|
||||
* @param obj
|
||||
* @param status
|
||||
*/
|
||||
function updateGlobalFetchMockJson(
|
||||
obj: any,
|
||||
status: number = 200
|
||||
) {
|
||||
// @ts-ignore
|
||||
global.fetch = jest.fn(() => {
|
||||
return Promise.resolve({
|
||||
json: () => Promise.resolve(obj),
|
||||
status,
|
||||
ok: status.toString().startsWith("2"), // 200-level codes are OK
|
||||
})
|
||||
}) as jest.Mock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the global fetch function mock's JSON to return an empty object.
|
||||
* @param obj
|
||||
*/
|
||||
function resetGlobalFetchMockJson() {
|
||||
updateGlobalFetchMockJson({});
|
||||
}
|
||||
|
||||
async function assertAsyncCallbackThrowsApiResponseError(callback: () => Promise<any>) {
|
||||
await expect(callback).rejects.toThrow(ApiResponseError);
|
||||
}
|
||||
|
||||
function updateGlobalFetchMockJsonToThrowSyntaxError() {
|
||||
// @ts-ignore
|
||||
global.fetch = jest.fn(() => {
|
||||
return Promise.resolve({
|
||||
json: () => Promise.reject(new SyntaxError("Unable to parse JSON")),
|
||||
status: 200,
|
||||
ok: true,
|
||||
})
|
||||
}) as jest.Mock;
|
||||
}
|
||||
|
||||
describe("ApiBasedRepositoryLoader", () => {
|
||||
let loader: ApiBasedRepositoryLoader;
|
||||
|
||||
beforeEach(() => {
|
||||
loader = new ApiBasedRepositoryLoader(new UnoptimizedInMemoryRepository());
|
||||
resetGlobalFetchMockJson();
|
||||
});
|
||||
|
||||
describe("fetchAndUpdateSystemData", () => {
|
||||
it("updates system data in repository if response received", async () => {
|
||||
const numberOfSystemsInResponse = fetchSystemDataSuccessfulResponse.all.length;
|
||||
updateGlobalFetchMockJson(fetchSystemDataSuccessfulResponse);
|
||||
|
||||
await loader.fetchAndUpdateSystemData();
|
||||
|
||||
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", () => {
|
||||
it("updates route data in repository if response received", async () => {
|
||||
updateGlobalFetchMockJson(fetchRouteDataSuccessfulResponse);
|
||||
|
||||
await loader.fetchAndUpdateRouteDataForSystemId("263");
|
||||
|
||||
const routes = await loader.repository.getRoutesBySystemId("263");
|
||||
|
||||
expect(routes.length).toEqual(fetchRouteDataSuccessfulResponse.all.length)
|
||||
});
|
||||
|
||||
it("throws the correct error if the API response contains no data", async () => {
|
||||
// The Passio API returns some invalid JSON if there is no data,
|
||||
// so simulate a JSON parsing error
|
||||
|
||||
updateGlobalFetchMockJsonToThrowSyntaxError();
|
||||
|
||||
await assertAsyncCallbackThrowsApiResponseError(async () => {
|
||||
await loader.fetchAndUpdateRouteDataForSystemId("263");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
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", () => {
|
||||
it("updates stop and polyline data if response received", async () => {
|
||||
updateGlobalFetchMockJson(fetchStopAndPolylineDataSuccessfulResponse);
|
||||
|
||||
const stopsArray = Object.values(fetchStopAndPolylineDataSuccessfulResponse.stops);
|
||||
|
||||
await loader.fetchAndUpdateStopAndPolylineDataForRoutesWithSystemId("263");
|
||||
|
||||
const stops = await loader.repository.getStopsBySystemId("263");
|
||||
expect(stops.length).toEqual(stopsArray.length);
|
||||
|
||||
await Promise.all(stops.map(async (stop) => {
|
||||
const orderedStops = await loader.repository.getOrderedStopsByStopId(stop.id)
|
||||
expect(orderedStops.length).toBeGreaterThan(0);
|
||||
}));
|
||||
|
||||
const routes = await loader.repository.getRoutesBySystemId("263");
|
||||
routes.forEach((route) => {
|
||||
expect(route.polylineCoordinates.length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
it("throws the correct error if the API response contains no data", async () => {
|
||||
updateGlobalFetchMockJsonToThrowSyntaxError();
|
||||
|
||||
await assertAsyncCallbackThrowsApiResponseError(async () => {
|
||||
await loader.fetchAndUpdateStopAndPolylineDataForRoutesWithSystemId("263");
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
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", () => {
|
||||
it("updates shuttle data in repository if response received", async () => {
|
||||
updateGlobalFetchMockJson(fetchShuttleDataSuccessfulResponse);
|
||||
const busesInResponse = Object.values(fetchShuttleDataSuccessfulResponse.buses);
|
||||
|
||||
await loader.fetchAndUpdateShuttleDataForSystemId("263");
|
||||
|
||||
const shuttles = await loader.repository.getShuttlesBySystemId("263");
|
||||
|
||||
expect(shuttles.length).toEqual(busesInResponse.length);
|
||||
});
|
||||
|
||||
it("throws the correct error if the API response contains no data", async () => {
|
||||
updateGlobalFetchMockJsonToThrowSyntaxError();
|
||||
|
||||
await assertAsyncCallbackThrowsApiResponseError(async () => {
|
||||
await loader.fetchAndUpdateShuttleDataForSystemId("263");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
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", () => {
|
||||
it("calls fetchAndUpdateEtaDataForStopId for every stop in repository", async () => {
|
||||
const spy = jest.spyOn(loader, "fetchAndUpdateEtaDataForStopId");
|
||||
|
||||
const stops = generateMockStops();
|
||||
stops.forEach((stop) => {
|
||||
stop.systemId = "1";
|
||||
});
|
||||
|
||||
await Promise.all(stops.map(async (stop) => {
|
||||
await loader.repository.addOrUpdateStop(stop);
|
||||
}));
|
||||
|
||||
await loader.fetchAndUpdateEtaDataForExistingStopsForSystemId("1");
|
||||
|
||||
expect(spy.mock.calls.length).toEqual(stops.length);
|
||||
});
|
||||
});
|
||||
|
||||
describe("fetchAndUpdateEtaDataForStopId", () => {
|
||||
it("updates ETA data for stop id if response received", async () => {
|
||||
updateGlobalFetchMockJson(fetchEtaDataSuccessfulResponse);
|
||||
const stopId = "177666";
|
||||
// @ts-ignore
|
||||
const etasFromResponse = fetchEtaDataSuccessfulResponse.ETAs[stopId]
|
||||
|
||||
await loader.fetchAndUpdateEtaDataForStopId(stopId);
|
||||
|
||||
const etas = await loader.repository.getEtasForStopId(stopId);
|
||||
expect(etas.length).toEqual(etasFromResponse.length);
|
||||
});
|
||||
|
||||
it("throws the correct error if the API response contains no data", async () => {
|
||||
updateGlobalFetchMockJsonToThrowSyntaxError();
|
||||
|
||||
await assertAsyncCallbackThrowsApiResponseError(async () => {
|
||||
await loader.fetchAndUpdateEtaDataForStopId("263");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user