mirror of
https://github.com/brendan-ch/project-inter-server.git
synced 2026-04-19 08:50:29 +00:00
800 lines
28 KiB
TypeScript
800 lines
28 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, jest, test } from "@jest/globals";
|
|
import { createClient, RedisClientType } from "redis";
|
|
import { UnoptimizedInMemoryShuttleRepository } from "../UnoptimizedInMemoryShuttleRepository";
|
|
import { ShuttleGetterSetterRepository } from "../ShuttleGetterSetterRepository";
|
|
import { RedisShuttleRepository } from "../RedisShuttleRepository";
|
|
import {
|
|
generateMockOrderedStops,
|
|
generateMockRoutes,
|
|
generateMockShuttles,
|
|
generateMockStops,
|
|
} from "../../../../testHelpers/mockDataGenerators";
|
|
import { RepositoryHolder } from "../../../../testHelpers/RepositoryHolder";
|
|
import { setupRouteAndOrderedStopsForShuttleRepository } from "../../../../testHelpers/setupRouteAndOrderedStopsForShuttleRepository";
|
|
import { ShuttleRepositoryEvent } from "../ShuttleGetterRepository";
|
|
|
|
class UnoptimizedInMemoryShuttleRepositoryHolder implements RepositoryHolder<ShuttleGetterSetterRepository> {
|
|
name = 'UnoptimizedInMemoryShuttleRepository';
|
|
factory = async () => {
|
|
return new UnoptimizedInMemoryShuttleRepository();
|
|
};
|
|
teardown = async () => {};
|
|
}
|
|
|
|
class RedisShuttleRepositoryHolder implements RepositoryHolder<ShuttleGetterSetterRepository> {
|
|
repo: RedisShuttleRepository | undefined;
|
|
redisClient: RedisClientType | undefined;
|
|
|
|
name = 'RedisShuttleRepository';
|
|
factory = async () => {
|
|
this.redisClient = createClient({
|
|
url: process.env.REDIS_URL,
|
|
});
|
|
await this.redisClient.connect();
|
|
this.repo = new RedisShuttleRepository(this.redisClient);
|
|
return this.repo;
|
|
};
|
|
teardown = async () => {
|
|
if (this.redisClient) {
|
|
await this.redisClient.flushAll();
|
|
await this.redisClient.disconnect();
|
|
}
|
|
};
|
|
}
|
|
|
|
const repositoryImplementations = [
|
|
new UnoptimizedInMemoryShuttleRepositoryHolder(),
|
|
new RedisShuttleRepositoryHolder(),
|
|
];
|
|
|
|
describe.each(repositoryImplementations)('$name', (holder) => {
|
|
let repository: ShuttleGetterSetterRepository;
|
|
|
|
beforeEach(async () => {
|
|
repository = await holder.factory();
|
|
jest.useRealTimers();
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await holder.teardown();
|
|
jest.useRealTimers();
|
|
});
|
|
|
|
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.getStops();
|
|
expect(result).toHaveLength(mockStops.length);
|
|
expect(result).toEqual(expect.arrayContaining(mockStops));
|
|
});
|
|
|
|
test("returns an empty list if there are no stops for the given system ID", async () => {
|
|
const result = await repository.getStops();
|
|
expect(result).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe("getStopById", () => {
|
|
test("gets a stop by ID if it exists", async () => {
|
|
const mockStops = generateMockStops();
|
|
const mockStop = mockStops[0];
|
|
await repository.addOrUpdateStop(mockStop);
|
|
|
|
const result = await repository.getStopById("st1");
|
|
expect(result).toEqual(mockStop);
|
|
});
|
|
|
|
test("returns null if the stop does not exist", async () => {
|
|
const result = await repository.getStopById("nonexistent-stop");
|
|
expect(result).toBeNull();
|
|
});
|
|
});
|
|
|
|
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.getRoutes();
|
|
expect(result).toHaveLength(mockRoutes.length);
|
|
expect(result).toEqual(expect.arrayContaining(mockRoutes));
|
|
});
|
|
|
|
test("returns an empty list if there are no routes for the system ID", async () => {
|
|
const result = await repository.getRoutes();
|
|
expect(result).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe("getRouteById", () => {
|
|
test("gets a route by ID if it exists", async () => {
|
|
const mockRoutes = generateMockRoutes();
|
|
const mockRoute = mockRoutes[0];
|
|
await repository.addOrUpdateRoute(mockRoute);
|
|
|
|
const result = await repository.getRouteById("r1");
|
|
expect(result).toEqual(mockRoute);
|
|
});
|
|
|
|
test("returns null if the route does not exist", async () => {
|
|
const result = await repository.getRouteById("nonexistent-route");
|
|
expect(result).toBeNull();
|
|
});
|
|
});
|
|
|
|
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.getShuttles();
|
|
expect(result).toHaveLength(mockShuttles.length);
|
|
expect(result).toEqual(expect.arrayContaining(mockShuttles));
|
|
});
|
|
|
|
test("returns an empty list if there are no shuttles for the system ID", async () => {
|
|
const result = await repository.getShuttles();
|
|
expect(result).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe("getShuttlesByRouteId", () => {
|
|
test("gets all shuttles for a specific route ID", async () => {
|
|
const mockShuttles = generateMockShuttles();
|
|
for (const shuttle of mockShuttles) {
|
|
await repository.addOrUpdateShuttle(shuttle);
|
|
}
|
|
|
|
const result = await repository.getShuttlesByRouteId("r1");
|
|
expect(result).toEqual(mockShuttles.filter((sh) => sh.routeId === "r1"));
|
|
});
|
|
|
|
test("returns an empty list if there are no shuttles for the route ID", async () => {
|
|
const result = await repository.getShuttlesByRouteId("nonexistent-route");
|
|
expect(result).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe("getShuttleById", () => {
|
|
test("gets a shuttle by ID if it exists", async () => {
|
|
const mockShuttles = generateMockShuttles();
|
|
for (const shuttle of mockShuttles) {
|
|
await repository.addOrUpdateShuttle(shuttle);
|
|
}
|
|
|
|
const result = await repository.getShuttleById("sh2");
|
|
expect(result).toEqual(mockShuttles[1]);
|
|
});
|
|
|
|
test("returns null if the shuttle doesn't exist", async () => {
|
|
const result = await repository.getShuttleById("nonexistent-id");
|
|
expect(result).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe("getOrderedStopByRouteAndStopId", () => {
|
|
test("gets an ordered stop by route ID and stop ID", async () => {
|
|
const mockOrderedStops = generateMockOrderedStops();
|
|
for (const orderedStop of mockOrderedStops) {
|
|
await repository.addOrUpdateOrderedStop(orderedStop);
|
|
}
|
|
|
|
const mockOrderedStop = mockOrderedStops[0];
|
|
const { routeId, stopId } = mockOrderedStop;
|
|
|
|
const result = await repository.getOrderedStopByRouteAndStopId(routeId, stopId);
|
|
expect(result).toEqual(mockOrderedStop);
|
|
});
|
|
|
|
test("returns null if no ordered stop matches the given route ID and stop ID", async () => {
|
|
const result = await repository.getOrderedStopByRouteAndStopId("nonexistent-route", "nonexistent-stop");
|
|
expect(result).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe("getOrderedStopsByStopId", () => {
|
|
test("gets all ordered stops for a specific stop ID", async () => {
|
|
const mockOrderedStops = generateMockOrderedStops();
|
|
for (const orderedStop of mockOrderedStops) {
|
|
await repository.addOrUpdateOrderedStop(orderedStop);
|
|
}
|
|
|
|
const result = await repository.getOrderedStopsByStopId("st1");
|
|
const expected = mockOrderedStops.filter((os) => os.stopId === "st1");
|
|
expect(result).toHaveLength(expected.length);
|
|
expect(result).toEqual(expect.arrayContaining(expected));
|
|
});
|
|
|
|
test("returns an empty list if there are no ordered stops for the stop ID", async () => {
|
|
const result = await repository.getOrderedStopsByStopId("nonexistent-stop");
|
|
expect(result).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe("getOrderedStopsByRouteId", () => {
|
|
test("gets all ordered stops for a specific route ID", async () => {
|
|
const mockOrderedStops = generateMockOrderedStops();
|
|
for (const orderedStop of mockOrderedStops) {
|
|
await repository.addOrUpdateOrderedStop(orderedStop);
|
|
}
|
|
|
|
const result = await repository.getOrderedStopsByRouteId("r1");
|
|
const expected = mockOrderedStops.filter((os) => os.routeId === "r1");
|
|
expect(result).toHaveLength(expected.length);
|
|
expect(result).toEqual(expect.arrayContaining(expected));
|
|
});
|
|
|
|
test("returns an empty list if there are no ordered stops for the route ID", async () => {
|
|
const result = await repository.getOrderedStopsByRouteId("nonexistent-route");
|
|
expect(result).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe("addOrUpdateRoute", () => {
|
|
test("adds a new route if nonexistent", async () => {
|
|
const mockRoutes = generateMockRoutes();
|
|
const newRoute = mockRoutes[0];
|
|
|
|
await repository.addOrUpdateRoute(newRoute);
|
|
|
|
const result = await repository.getRoutes();
|
|
expect(result).toEqual([newRoute]);
|
|
});
|
|
|
|
test("updates an existing route if it exists", async () => {
|
|
const mockRoutes = generateMockRoutes();
|
|
const existingRoute = mockRoutes[0];
|
|
const updatedRoute = structuredClone(existingRoute);
|
|
updatedRoute.name = "Updated Route";
|
|
|
|
await repository.addOrUpdateRoute(existingRoute);
|
|
await repository.addOrUpdateRoute(updatedRoute);
|
|
|
|
const result = await repository.getRoutes();
|
|
expect(result).toEqual([updatedRoute]);
|
|
});
|
|
});
|
|
|
|
describe("addOrUpdateShuttle", () => {
|
|
test("adds a new shuttle if nonexistent", async () => {
|
|
const mockShuttles = generateMockShuttles();
|
|
const newShuttle = mockShuttles[0];
|
|
|
|
await repository.addOrUpdateShuttle(newShuttle);
|
|
|
|
const result = await repository.getShuttles();
|
|
expect(result).toEqual([newShuttle]);
|
|
});
|
|
|
|
test("updates an existing shuttle if it exists", async () => {
|
|
const mockShuttles = generateMockShuttles();
|
|
const existingShuttle = mockShuttles[0];
|
|
const updatedShuttle = structuredClone(existingShuttle);
|
|
updatedShuttle.name = "Updated Shuttle";
|
|
|
|
await repository.addOrUpdateShuttle(existingShuttle);
|
|
await repository.addOrUpdateShuttle(updatedShuttle);
|
|
|
|
const result = await repository.getShuttles();
|
|
expect(result).toEqual([updatedShuttle]);
|
|
});
|
|
});
|
|
|
|
describe("addOrUpdateStop", () => {
|
|
test("adds a new stop if nonexistent", async () => {
|
|
const mockStops = generateMockStops();
|
|
const newStop = mockStops[0];
|
|
|
|
await repository.addOrUpdateStop(newStop);
|
|
|
|
const result = await repository.getStops();
|
|
expect(result).toEqual([newStop]);
|
|
});
|
|
|
|
test("updates an existing stop if it exists", async () => {
|
|
const mockStops = generateMockStops();
|
|
const existingStop = mockStops[0];
|
|
const updatedStop = structuredClone(existingStop);
|
|
updatedStop.name = "Updated Stop";
|
|
|
|
await repository.addOrUpdateStop(existingStop);
|
|
await repository.addOrUpdateStop(updatedStop);
|
|
|
|
const result = await repository.getStops();
|
|
expect(result).toEqual([updatedStop]);
|
|
});
|
|
});
|
|
|
|
describe("addOrUpdateOrderedStop", () => {
|
|
test("adds a new ordered stop if nonexistent", async () => {
|
|
const mockOrderedStops = generateMockOrderedStops();
|
|
const newOrderedStop = mockOrderedStops[0];
|
|
|
|
await repository.addOrUpdateOrderedStop(newOrderedStop);
|
|
|
|
const result = await repository.getOrderedStopsByRouteId(newOrderedStop.routeId);
|
|
expect(result).toEqual([newOrderedStop]);
|
|
});
|
|
|
|
test("updates an existing ordered stop if it exists", async () => {
|
|
const mockOrderedStops = generateMockOrderedStops();
|
|
const existingOrderedStop = mockOrderedStops[0];
|
|
const updatedOrderedStop = structuredClone(existingOrderedStop);
|
|
updatedOrderedStop.position = 5;
|
|
|
|
await repository.addOrUpdateOrderedStop(existingOrderedStop);
|
|
await repository.addOrUpdateOrderedStop(updatedOrderedStop);
|
|
|
|
const result = await repository.getOrderedStopsByRouteId(existingOrderedStop.routeId);
|
|
expect(result).toEqual([updatedOrderedStop]);
|
|
});
|
|
});
|
|
|
|
|
|
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.getRoutes();
|
|
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.getRoutes();
|
|
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.getShuttles();
|
|
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.getShuttles();
|
|
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.getStops();
|
|
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.getStops();
|
|
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("clearShuttleData", () => {
|
|
test("clears all shuttles from the repository", async () => {
|
|
const mockShuttles = generateMockShuttles();
|
|
for (const shuttle of mockShuttles) {
|
|
await repository.addOrUpdateShuttle(shuttle);
|
|
}
|
|
|
|
await repository.clearShuttleData();
|
|
|
|
const result = await repository.getShuttles();
|
|
expect(result).toEqual([]);
|
|
});
|
|
});
|
|
|
|
|
|
describe("clearOrderedStopData", () => {
|
|
test("clears all ordered stops from the repository", async () => {
|
|
const mockOrderedStops = generateMockOrderedStops();
|
|
for (const system of mockOrderedStops) {
|
|
await repository.addOrUpdateOrderedStop(system);
|
|
}
|
|
|
|
await repository.clearOrderedStopData();
|
|
|
|
const result = await repository.getOrderedStopsByRouteId("route1");
|
|
expect(result).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe("clearRouteData", () => {
|
|
test("clears all routes from the repository", async () => {
|
|
const mockRoutes = generateMockRoutes();
|
|
for (const route of mockRoutes) {
|
|
await repository.addOrUpdateRoute(route);
|
|
}
|
|
|
|
await repository.clearRouteData();
|
|
|
|
const result = await repository.getRoutes();
|
|
expect(result).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe("clearStopData", () => {
|
|
test("clears all stops from the repository", async () => {
|
|
const mockStops = generateMockStops();
|
|
for (const stop of mockStops) {
|
|
await repository.addOrUpdateStop(stop);
|
|
}
|
|
|
|
await repository.clearStopData();
|
|
|
|
const result = await repository.getStops();
|
|
expect(result).toEqual([]);
|
|
});
|
|
});
|
|
|
|
// Helper function for setting up routes and ordered stops for shuttle tracking tests
|
|
async function setupRouteAndOrderedStops() {
|
|
return await setupRouteAndOrderedStopsForShuttleRepository(repository);
|
|
}
|
|
|
|
describe("addOrUpdateShuttle with shuttle tracking", () => {
|
|
test("updates the shuttle's last stop arrival if shuttle is at a stop", async () => {
|
|
const { route, systemId, stop2 } = await setupRouteAndOrderedStops();
|
|
|
|
const shuttle = {
|
|
id: "sh1",
|
|
name: "Shuttle 1",
|
|
routeId: route.id,
|
|
systemId: systemId,
|
|
coordinates: stop2.coordinates,
|
|
orientationInDegrees: 0,
|
|
updatedTime: new Date(),
|
|
};
|
|
|
|
await repository.addOrUpdateShuttle(shuttle);
|
|
const lastStop = await repository.getShuttleLastStopArrival(shuttle.id);
|
|
expect(lastStop?.stopId).toEqual(stop2.id);
|
|
});
|
|
});
|
|
|
|
describe("getArrivedStopIfExists", () => {
|
|
test("gets the stop that the shuttle is currently at, if exists", async () => {
|
|
const { route, systemId, stop2 } = await setupRouteAndOrderedStops();
|
|
|
|
const shuttle = {
|
|
id: "sh1",
|
|
name: "Shuttle 1",
|
|
routeId: route.id,
|
|
systemId: systemId,
|
|
coordinates: stop2.coordinates,
|
|
orientationInDegrees: 0,
|
|
updatedTime: new Date(),
|
|
};
|
|
|
|
const result = await repository.getArrivedStopIfExists(shuttle);
|
|
|
|
expect(result).toBeDefined();
|
|
expect(result?.id).toBe("st2");
|
|
expect(result?.name).toBe("Stop 2");
|
|
});
|
|
|
|
test("returns undefined if shuttle is not currently at a stop", async () => {
|
|
const { route, systemId } = await setupRouteAndOrderedStops();
|
|
|
|
const shuttle = {
|
|
id: "sh1",
|
|
name: "Shuttle 1",
|
|
routeId: route.id,
|
|
systemId: systemId,
|
|
coordinates: { latitude: 12.5, longitude: 22.5 },
|
|
orientationInDegrees: 0,
|
|
updatedTime: new Date(),
|
|
};
|
|
|
|
const result = await repository.getArrivedStopIfExists(shuttle);
|
|
|
|
expect(result).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
describe("getShuttleLastStopArrival", () => {
|
|
test("gets the shuttle's last stop if existing in the data", async () => {
|
|
const { route, systemId, stop1 } = await setupRouteAndOrderedStops();
|
|
|
|
const shuttle = {
|
|
id: "sh1",
|
|
name: "Shuttle 1",
|
|
routeId: route.id,
|
|
systemId: systemId,
|
|
coordinates: stop1.coordinates,
|
|
orientationInDegrees: 0,
|
|
updatedTime: new Date(),
|
|
};
|
|
|
|
const stopArrivalTime = new Date("2024-01-15T10:30:00Z");
|
|
await repository.addOrUpdateShuttle(shuttle, stopArrivalTime.getTime());
|
|
|
|
const result = await repository.getShuttleLastStopArrival(shuttle.id);
|
|
|
|
expect(result).toBeDefined();
|
|
expect(result?.stopId).toBe(stop1.id);
|
|
expect(result?.timestamp.getTime()).toBe(stopArrivalTime.getTime());
|
|
});
|
|
|
|
test("returns undefined if the data has never been initialized", async () => {
|
|
const mockShuttles = generateMockShuttles();
|
|
const shuttle = mockShuttles[0];
|
|
|
|
const result = await repository.getShuttleLastStopArrival(shuttle.id);
|
|
|
|
expect(result).toBeUndefined();
|
|
});
|
|
|
|
test("returns the most recent stop arrival when updated multiple times", async () => {
|
|
const { route, systemId, stop1, stop2 } = await setupRouteAndOrderedStops();
|
|
|
|
const shuttle = {
|
|
id: "sh1",
|
|
name: "Shuttle 1",
|
|
routeId: route.id,
|
|
systemId: systemId,
|
|
coordinates: stop1.coordinates,
|
|
orientationInDegrees: 0,
|
|
updatedTime: new Date(),
|
|
};
|
|
|
|
const firstArrivalTime = new Date("2024-01-15T10:30:00Z");
|
|
await repository.addOrUpdateShuttle(shuttle, firstArrivalTime.getTime());
|
|
|
|
shuttle.coordinates = stop2.coordinates;
|
|
const secondArrivalTime = new Date("2024-01-15T10:35:00Z");
|
|
await repository.addOrUpdateShuttle(shuttle, secondArrivalTime.getTime());
|
|
|
|
const result = await repository.getShuttleLastStopArrival(shuttle.id);
|
|
|
|
expect(result).toBeDefined();
|
|
expect(result?.stopId).toBe(stop2.id);
|
|
expect(result?.timestamp.getTime()).toBe(secondArrivalTime.getTime());
|
|
});
|
|
});
|
|
|
|
describe("SHUTTLE_UPDATED event", () => {
|
|
test("emits SHUTTLE_UPDATED event when shuttles are added or updated", async () => {
|
|
const mockListener = jest.fn();
|
|
repository.on(ShuttleRepositoryEvent.SHUTTLE_UPDATED, mockListener);
|
|
|
|
const mockShuttles = generateMockShuttles();
|
|
for (const shuttle of mockShuttles) {
|
|
await repository.addOrUpdateShuttle(shuttle);
|
|
}
|
|
|
|
expect(mockListener).toHaveBeenCalledTimes(mockShuttles.length);
|
|
expect(mockListener).toHaveBeenCalledWith(mockShuttles[0]);
|
|
expect(mockListener).toHaveBeenCalledWith(mockShuttles[mockShuttles.length - 1]);
|
|
});
|
|
|
|
test("does not notify listener after it has been removed", async () => {
|
|
const mockListener = jest.fn();
|
|
repository.on(ShuttleRepositoryEvent.SHUTTLE_UPDATED, mockListener);
|
|
|
|
const mockShuttles = generateMockShuttles();
|
|
|
|
repository.off(ShuttleRepositoryEvent.SHUTTLE_UPDATED, mockListener);
|
|
await repository.addOrUpdateShuttle(mockShuttles[0]);
|
|
expect(mockListener).toHaveBeenCalledTimes(0);
|
|
});
|
|
|
|
test("stops notifying specific listener after removal but continues for others", async () => {
|
|
const mockListener1 = jest.fn();
|
|
const mockListener2 = jest.fn();
|
|
repository.on(ShuttleRepositoryEvent.SHUTTLE_UPDATED, mockListener1);
|
|
repository.on(ShuttleRepositoryEvent.SHUTTLE_UPDATED, mockListener2);
|
|
|
|
const mockShuttles = generateMockShuttles();
|
|
await repository.addOrUpdateShuttle(mockShuttles[0]);
|
|
|
|
repository.off(ShuttleRepositoryEvent.SHUTTLE_UPDATED, mockListener1);
|
|
|
|
await repository.addOrUpdateShuttle(mockShuttles[mockShuttles.length - 1]);
|
|
|
|
expect(mockListener1).toHaveBeenCalledTimes(1);
|
|
expect(mockListener1).toHaveBeenCalledWith(mockShuttles[0]);
|
|
expect(mockListener1).not.toHaveBeenCalledWith(mockShuttles[mockShuttles.length - 1]);
|
|
|
|
expect(mockListener2).toHaveBeenCalledTimes(2);
|
|
expect(mockListener2).toHaveBeenCalledWith(mockShuttles[0]);
|
|
expect(mockListener2).toHaveBeenCalledWith(mockShuttles[mockShuttles.length - 1]);
|
|
});
|
|
});
|
|
|
|
describe("SHUTTLE_REMOVED event", () => {
|
|
test("emits SHUTTLE_REMOVED event when a shuttle is removed", async () => {
|
|
const mockShuttles = generateMockShuttles();
|
|
const shuttleToRemove = mockShuttles[0];
|
|
const listener = jest.fn();
|
|
repository.on(ShuttleRepositoryEvent.SHUTTLE_REMOVED, listener);
|
|
|
|
await repository.addOrUpdateShuttle(shuttleToRemove);
|
|
await repository.removeShuttleIfExists(shuttleToRemove.id);
|
|
|
|
expect(listener).toHaveBeenCalledTimes(1);
|
|
expect(listener).toHaveBeenCalledWith(shuttleToRemove);
|
|
});
|
|
});
|
|
|
|
describe("SHUTTLE_WILL_ARRIVE_AT_STOP event", () => {
|
|
test("emits SHUTTLE_WILL_ARRIVE_AT_STOP event before shuttle arrives at a stop", async () => {
|
|
const { route, systemId, stop1 } = await setupRouteAndOrderedStops();
|
|
|
|
const listener = jest.fn();
|
|
repository.on(ShuttleRepositoryEvent.SHUTTLE_WILL_ARRIVE_AT_STOP, listener);
|
|
|
|
const shuttle = {
|
|
id: "sh1",
|
|
name: "Shuttle 1",
|
|
routeId: route.id,
|
|
systemId: systemId,
|
|
coordinates: stop1.coordinates,
|
|
orientationInDegrees: 0,
|
|
updatedTime: new Date(),
|
|
};
|
|
|
|
const arrivalTime = new Date("2024-01-15T10:30:00Z");
|
|
await repository.addOrUpdateShuttle(shuttle, arrivalTime.getTime());
|
|
|
|
expect(listener).toHaveBeenCalledTimes(1);
|
|
const emittedPayload = listener.mock.calls[0][0] as any;
|
|
expect(emittedPayload.shuttleId).toBe(shuttle.id);
|
|
expect(emittedPayload.stopId).toBe(stop1.id);
|
|
expect(emittedPayload.timestamp.getTime()).toBe(arrivalTime.getTime());
|
|
});
|
|
|
|
test("does not emit event when shuttle is not at a stop", async () => {
|
|
const { route, systemId } = await setupRouteAndOrderedStops();
|
|
|
|
const listener = jest.fn();
|
|
repository.on(ShuttleRepositoryEvent.SHUTTLE_WILL_ARRIVE_AT_STOP, listener);
|
|
|
|
const shuttle = {
|
|
id: "sh1",
|
|
name: "Shuttle 1",
|
|
routeId: route.id,
|
|
systemId: systemId,
|
|
coordinates: { latitude: 12.5, longitude: 22.5 }, // Not at any stop
|
|
orientationInDegrees: 0,
|
|
updatedTime: new Date(),
|
|
};
|
|
|
|
await repository.addOrUpdateShuttle(shuttle);
|
|
|
|
expect(listener).toHaveBeenCalledTimes(0);
|
|
});
|
|
|
|
test("emits multiple events as shuttle visits multiple stops", async () => {
|
|
const { route, systemId, stop1, stop2 } = await setupRouteAndOrderedStops();
|
|
|
|
const listener = jest.fn();
|
|
repository.on(ShuttleRepositoryEvent.SHUTTLE_WILL_ARRIVE_AT_STOP, listener);
|
|
|
|
const shuttle = {
|
|
id: "sh1",
|
|
name: "Shuttle 1",
|
|
routeId: route.id,
|
|
systemId: systemId,
|
|
coordinates: stop1.coordinates,
|
|
orientationInDegrees: 0,
|
|
updatedTime: new Date(),
|
|
};
|
|
|
|
const firstArrivalTime = new Date("2024-01-15T10:30:00Z");
|
|
await repository.addOrUpdateShuttle(shuttle, firstArrivalTime.getTime());
|
|
|
|
shuttle.coordinates = stop2.coordinates;
|
|
const secondArrivalTime = new Date("2024-01-15T10:35:00Z");
|
|
await repository.addOrUpdateShuttle(shuttle, secondArrivalTime.getTime());
|
|
|
|
expect(listener).toHaveBeenCalledTimes(2);
|
|
|
|
const firstPayload = listener.mock.calls[0][0] as any;
|
|
expect(firstPayload.shuttleId).toBe(shuttle.id);
|
|
expect(firstPayload.stopId).toBe(stop1.id);
|
|
expect(firstPayload.timestamp.getTime()).toBe(firstArrivalTime.getTime());
|
|
|
|
const secondPayload = listener.mock.calls[1][0] as any;
|
|
expect(secondPayload.shuttleId).toBe(shuttle.id);
|
|
expect(secondPayload.stopId).toBe(stop2.id);
|
|
expect(secondPayload.timestamp.getTime()).toBe(secondArrivalTime.getTime());
|
|
});
|
|
});
|
|
});
|