mirror of
https://github.com/brendan-ch/project-inter-server.git
synced 2026-04-17 07:50:31 +00:00
271 lines
9.7 KiB
TypeScript
271 lines
9.7 KiB
TypeScript
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";
|
|
|
|
/**
|
|
* 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 () => {
|
|
|
|
});
|
|
|
|
it("throws the correct error if the API response contains no data", async () => {
|
|
updateGlobalFetchMockJsonToThrowSyntaxError();
|
|
|
|
await assertAsyncCallbackThrowsApiResponseError(async () => {
|
|
await loader.fetchAndUpdateEtaDataForStopId("263");
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|