Files
project-inter-server/test/repositories/ApiBasedRepositoryTests.test.ts

632 lines
17 KiB
TypeScript

import { beforeEach, describe, expect, jest, test } from "@jest/globals";
import {
ApiBasedRepository,
ApiBasedRepositoryCache,
ApiBasedRepositoryMillisecondTTLs
} from "../../src/repositories/ApiBasedRepository";
import { IEta, IShuttle, IStop } from "../../src/entities/entities";
/**
* Update the global fetch function to return a specific object.
* @param obj
*/
function updateGlobalFetchMockJson(obj: any) {
// @ts-ignore
global.fetch = jest.fn(() => {
return Promise.resolve({
json: () => Promise.resolve(obj)
})
}) as jest.Mock;
}
/**
* Reset the global fetch function mock's JSON to return an empty object.
* @param obj
*/
function resetGlobalFetchMockJson() {
updateGlobalFetchMockJson({})
}
beforeEach(() => {
resetGlobalFetchMockJson();
})
// Snapshots taken from the Passio GO! API
const genericEtaDataByStopId = {
"ETAs": {
"177666": [
{
"OOS": 0,
"busName": "08",
"distance": 1,
"speed": 10.028535400123669,
"routeBlockId": "142270",
"actualRouteBlockId": "142270",
"arrived": null,
"eta": "10 min ",
"color": "#000000",
"bg": "#ffea3f",
"order": 0,
"dwell": null,
"stopsAmount": 2,
"secondsSpent": 587,
"etaR": "10",
"error": null,
"outdated": 0,
"routeId": "53966",
"serviceTime": "",
"scheduleTimes": [],
"goShowSchedule": 0,
"looping": "1",
"routeGroupId": "6703",
"busId": 5577,
"tripId": 751430,
"deviceId": 402840,
"created": "2025-01-07 15:00:09",
"routePointPosition": 6,
"routeStopPosition": 1,
"stopRoutePointPosition": 217,
"timezoneOffset": -10800,
"busLatLng": [
33.7933406,
-117.8539321
],
"busProjectionLatlng": {
"lat": 33.79331052666975,
"lng": -117.85392945849208
},
"busProjectionError": 3,
"stopId": "177666",
"theStop": {
"name": "Chapman Court",
"position": 3,
"userId": "263",
"routeStopId": "1348785",
"busId": 5577,
"routeName": "Red Route",
"shortName": null,
"routeId": "53966",
"stopId": "177666"
}
},
{
"OOS": 0,
"busName": "07",
"distance": 1,
"speed": 12.160256921380398,
"routeBlockId": "142270",
"actualRouteBlockId": "142270",
"arrived": null,
"eta": "11 min ",
"color": "#000000",
"bg": "#ffea3f",
"order": 0,
"dwell": null,
"stopsAmount": 2,
"secondsSpent": 635,
"etaR": "11",
"error": null,
"outdated": 0,
"routeId": "53966",
"serviceTime": "",
"scheduleTimes": [],
"goShowSchedule": 0,
"looping": "1",
"routeGroupId": "6703",
"busId": 5576,
"tripId": 751430,
"deviceId": 441065,
"created": "2025-01-07 15:00:10",
"routePointPosition": 448,
"routeStopPosition": 4,
"stopRoutePointPosition": 217,
"timezoneOffset": -10800,
"busLatLng": [
33.7933284,
-117.855132
],
"busProjectionLatlng": {
"lat": 33.79332033922653,
"lng": -117.85513217762522
},
"busProjectionError": 1,
"stopId": "177666",
"theStop": {
"name": "Chapman Court",
"position": 3,
"userId": "263",
"routeStopId": "1348785",
"busId": 5576,
"routeName": "Red Route",
"shortName": null,
"routeId": "53966",
"stopId": "177666"
}
},
]
},
}
const genericShuttleDataBySystemId = {
"alertCRC": "23c1b91c",
"buses": {
"402840": [
{
"deviceId": 402840,
"created": "08:24 PM",
"createdTime": "08:24 PM",
"paxLoad": 0,
"bus": "08",
"busId": 5577,
"userId": "263",
"routeBlockId": "142270",
"latitude": "33.791781800",
"longitude": "-117.858964600",
"calculatedCourse": "351.796001302109",
"outOfService": 0,
"more": "102",
"totalCap": 20,
"color": "#d62728",
"busName": "08",
"busType": "",
"routeId": "53966",
"route": "Red Route",
"outdated": 0
}
],
"404873": [
{
"deviceId": 404873,
"created": "08:24 PM",
"createdTime": "08:24 PM",
"paxLoad": 0,
"bus": "10",
"busId": 7105,
"userId": "263",
"routeBlockId": "142270",
"latitude": "33.789331300",
"longitude": "-117.888790600",
"calculatedCourse": "76.005762226701",
"outOfService": 0,
"more": "101",
"totalCap": 20,
"color": "#d62728",
"busName": "10",
"busType": "",
"routeId": "53966",
"route": "Red Route",
"outdated": 0
}
],
"421421": [
{
"deviceId": 421421,
"created": "08:24 PM",
"createdTime": "08:24 PM",
"paxLoad": 0,
"bus": "17",
"busId": 12502,
"userId": "263",
"routeBlockId": "142660",
"latitude": "33.790699500",
"longitude": "-117.890385500",
"calculatedCourse": "10.65684824528148",
"outOfService": 0,
"more": "101",
"totalCap": 32,
"color": "#bd9e39",
"busName": "17",
"busType": "Shuttle Bus",
"routeId": "54256",
"route": "Gold Route",
"outdated": 0
}
],
"441065": [
{
"deviceId": 441065,
"created": "08:19 PM",
"createdTime": "08:19 PM",
"paxLoad": 0,
"bus": "07",
"busId": 5576,
"userId": "263",
"routeBlockId": "142270",
"latitude": "33.793278900",
"longitude": "-117.852629400",
"calculatedCourse": "299.74488110904485",
"outOfService": 0,
"more": "22",
"totalCap": 20,
"color": "#d62728",
"busName": "07",
"busType": "",
"routeId": "53966",
"route": "Red Route",
"outdated": 0
}
]
},
"microtime": 0.023222923278808594,
"time": {
"263": "08:24 PM"
}
}
describe("getEtaForShuttleAndStopId", () => {
test("getEtaForShuttleAndStopId returns correct ETA data", async () => {
updateGlobalFetchMockJson(genericEtaDataByStopId);
const initialCache: ApiBasedRepositoryCache = {
etasForStopId: {
"177666": [
{
secondsRemaining: 587,
shuttleId: "5577",
stopId: "177666",
millisecondsSinceEpoch: Date.now(),
}
],
},
}
const repository = new ApiBasedRepository(initialCache);
repository.getShuttleById = jest.fn(async () => {
const shuttle: IShuttle = {
id: "5577",
name: "08",
coordinates: {
latitude: 33.7933406,
longitude: -117.8539321,
},
routeId: "53966",
systemId: "1",
};
return shuttle;
});
repository.updateEtasForSystemIfTTL = jest.fn(async () => {
});
const result = await repository.getEtaForShuttleAndStopId("5577", "177666");
expect(result?.secondsRemaining).toEqual(587);
expect(result?.millisecondsSinceEpoch).toBeDefined();
});
test("getEtaForShuttleAndStopId returns null if API call is invalid and cache is empty", async () => {
const repository = new ApiBasedRepository();
const result = await repository.getEtaForShuttleAndStopId("5577", "177666");
expect(result).toEqual(null);
});
});
describe("getEtasForShuttleId", () => {
test("getEtasForShuttleId returns correct ETA data", async () => {
updateGlobalFetchMockJson(genericEtaDataByStopId);
const initialCache: ApiBasedRepositoryCache = {
etasForStopId: {},
etasForShuttleId: {
"5577": [
{
secondsRemaining: 587,
shuttleId: "5577",
stopId: "177666",
millisecondsSinceEpoch: Date.now(),
}
]
},
};
const ttls: ApiBasedRepositoryMillisecondTTLs = {
etasForShuttleId: 100000,
etasForStopId: 100000,
};
const repository = new ApiBasedRepository(initialCache, ttls);
repository.updateEtasForSystemIfTTL = jest.fn(async () => {
});
repository.getShuttleById = jest.fn(async () => {
const shuttle: IShuttle = {
id: "5577",
name: "08",
coordinates: {
latitude: 33.7933406,
longitude: -117.8539321,
},
routeId: "53966",
systemId: "1",
};
return shuttle;
});
const result = await repository.getEtasForShuttleId("5577");
// @ts-ignore
expect(result).toEqual(initialCache.etasForShuttleId["5577"]);
});
test("getEtasForShuttleId returns empty array if no data available", async () => {
const repository = new ApiBasedRepository();
repository.updateEtasForSystemIfTTL = jest.fn(async () => {
});
const result = await repository.getEtasForShuttleId("5577");
expect(result).toEqual([]);
});
});
describe("getEtasForStopId", () => {
test("getEtasForStopId returns correct ETA data", async () => {
// Because I'm testing updateEtasForSystemIfTTL separately,
// stub it out here
updateGlobalFetchMockJson(genericEtaDataByStopId);
const initialCache: ApiBasedRepositoryCache = {
etasForStopId: {
"177666": [
{
secondsRemaining: 587,
shuttleId: "5577",
stopId: "177666",
millisecondsSinceEpoch: Date.now(),
}
]
},
etasForShuttleId: {}
};
const ttls: ApiBasedRepositoryMillisecondTTLs = {
etasForShuttleId: 100000,
etasForStopId: 100000,
};
const repository = new ApiBasedRepository(initialCache, ttls);
repository.getStopById = jest.fn(async () => {
const stop: IStop = {
name: "Chapman Court",
systemId: "1",
id: "177666",
coordinates: {
latitude: 33.796796,
longitude: -117.889293
},
};
return stop;
});
repository.updateEtasForSystemIfTTL = jest.fn(async () => {
});
const result = await repository.getEtasForStopId("177666");
expect(result).toEqual(initialCache.etasForStopId!["177666"]);
});
test("getEtasForStopId returns empty array if no data available", async () => {
const repository = new ApiBasedRepository();
repository.updateEtasForSystemIfTTL = jest.fn(async () => {
});
const result = await repository.getEtasForShuttleId("5577");
expect(result).toEqual([]);
});
});
describe("updateEtasForSystemIfTTL", () => {
// test("updateEtasForSystemIfTTL does nothing if data is not TTL", async () => {
// updateGlobalFetchMockJson(genericEtaDataByStopId);
//
// // If ETA data is not TTL, then don't do anything
// const expectedEta: IEta = {
// secondsRemaining: 587,
// shuttleId: "5577",
// stopId: "177666",
// millisecondsSinceEpoch: Date.now() - 1000,
// };
//
// const initialCache: ApiBasedRepositoryCache = {
// etasForShuttleId: {
// "5577": [
// expectedEta,
// ],
// },
// etasForStopId: {
// "177666": [
// expectedEta,
// ],
// },
// stopsBySystemId: {
// "1": [
// {
// systemId: "1",
// millisecondsSinceEpoch: Date.now() - 1000,
// name: "Chapman Court",
// id: "177666",
// coordinates: {
// latitude: 33.796796,
// longitude: -117.889293
// },
// }
// ],
// },
// };
//
// const ttls: ApiBasedRepositoryMillisecondTTLs = {
// etasForShuttleId: 100000,
// etasForStopId: 100000,
// };
//
// const repository = new ApiBasedRepository(initialCache, ttls);
// await repository.updateEtasForSystemIfTTL("1");
//
// const updatedResult = await repository.getEtaForShuttleAndStopId(
// "5577",
// "177666",
// );
// expect(updatedResult?.millisecondsSinceEpoch).toEqual(expectedEta.millisecondsSinceEpoch);
// });
test("updateEtasForSystemIfTTL updates all ETA data if data is TTL", async () => {
updateGlobalFetchMockJson(genericEtaDataByStopId);
const sampleStop: IStop = {
name: "Chapman Court",
systemId: "1",
id: "177666",
coordinates: {
latitude: 33.796796,
longitude: -117.889293
},
}
const repository = new ApiBasedRepository();
repository.getStopsBySystemId = jest.fn(async () => {
return [
sampleStop
];
});
repository.getStopById = jest.fn(async () => {
return sampleStop;
});
await repository.updateEtasForSystemIfTTL("1");
const updatedResult = await repository.getEtasForStopId("177666");
expect(updatedResult.length).toEqual(2);
});
});
describe("getShuttleById", () => {
test("getShuttleById returns null if unseeded cache", async () => {
updateGlobalFetchMockJson(genericShuttleDataBySystemId);
const initialCache: ApiBasedRepositoryCache = {};
const repository = new ApiBasedRepository(initialCache);
const shuttle = await repository.getShuttleById("5577");
expect(shuttle).toBeNull();
});
test("getShuttleById returns data if present", async () => {
updateGlobalFetchMockJson(genericShuttleDataBySystemId);
const initialCacheShuttle = {
coordinates: {
latitude: 33.7917818,
longitude: -117.8589646,
},
name: "08",
routeId: "53966",
systemId: "1",
id: "5577",
}
const initialCache: ApiBasedRepositoryCache = {
shuttleByShuttleId: {
"5577": initialCacheShuttle
}
};
const ttls: ApiBasedRepositoryMillisecondTTLs = {
shuttleByShuttleId: 1000,
};
const repository = new ApiBasedRepository(initialCache, ttls);
repository.updateStopsForSystemIdIfTTL = jest.fn(async () => {
})
const shuttle = await repository.getShuttleById("5577");
expect(shuttle).toEqual(initialCacheShuttle);
});
});
describe("getShuttlesBySystemId", () => {
test("getShuttlesBySystemId returns old data if not expired", async () => {
updateGlobalFetchMockJson(genericShuttleDataBySystemId);
const initialCacheShuttle = {
coordinates: {
latitude: 33.791781,
longitude: -117.8589646,
},
name: "08",
routeId: "53966",
systemId: "1",
id: "5577",
millisecondsSinceEpoch: Date.now() - 1000,
};
const initialCache: ApiBasedRepositoryCache = {
shuttlesBySystemId: {
"1": [
initialCacheShuttle,
]
},
shuttleByShuttleId: {
"5577": initialCacheShuttle,
}
};
const ttls: ApiBasedRepositoryMillisecondTTLs = {
shuttleByShuttleId: 100000,
shuttlesBySystemId: 100000,
};
const repository = new ApiBasedRepository(initialCache, ttls);
const shuttles = await repository.getShuttlesBySystemId("1");
expect(shuttles.length).toEqual(1);
expect(shuttles[0].id).toEqual(initialCacheShuttle.id);
});
test("getShuttlesBySystemId returns fresh data if expired", async () => {
updateGlobalFetchMockJson(genericShuttleDataBySystemId);
// TODO: move construction of shuttle into method
const initialCacheShuttle = {
coordinates: {
latitude: 33.791781,
longitude: -117.8589646,
},
name: "08",
routeId: "53966",
systemId: "1",
id: "5577",
millisecondsSinceEpoch: Date.now() - 100000,
};
const initialCache: ApiBasedRepositoryCache = {
shuttlesBySystemId: {
"1": [
initialCacheShuttle,
]
},
shuttleByShuttleId: {
"5577": initialCacheShuttle,
}
};
const ttls: ApiBasedRepositoryMillisecondTTLs = {
shuttleByShuttleId: 1000,
shuttlesBySystemId: 1000,
};
const repository = new ApiBasedRepository(initialCache, ttls);
const shuttles = await repository.getShuttlesBySystemId("1");
expect(shuttles.length).toEqual(1);
expect(shuttles[0].id).toEqual("5577");
expect(shuttles[0].millisecondsSinceEpoch).not.toEqual(initialCacheShuttle.millisecondsSinceEpoch);
});
test("getShuttlesBySystemId returns fresh data if no seeded data", async () => {
updateGlobalFetchMockJson(genericShuttleDataBySystemId);
const repository = new ApiBasedRepository();
const shuttles = await repository.getShuttlesBySystemId("1");
expect(shuttles.length).toEqual(1);
});
});