diff --git a/src/repositories/shuttle/eta/InMemorySelfUpdatingETARepository.ts b/src/repositories/shuttle/eta/InMemorySelfUpdatingETARepository.ts index 164b734..ef04186 100644 --- a/src/repositories/shuttle/eta/InMemorySelfUpdatingETARepository.ts +++ b/src/repositories/shuttle/eta/InMemorySelfUpdatingETARepository.ts @@ -90,9 +90,25 @@ export class InMemorySelfUpdatingETARepository extends BaseInMemoryETARepository } private async handleShuttleUpdate(shuttle: IShuttle): Promise { + const isAtStop = await this.shuttleRepository.checkIfShuttleIsAtStop(shuttle.id); const lastStop = await this.shuttleRepository.getShuttleLastStopArrival(shuttle.id); if (!lastStop) return; + if (isAtStop) { + // Update the ETA *to* the stop the shuttle is currently at, + // before starting from the current stop as normal. + // Account for cases where the shuttle arrived way earlier than + // expected based on the calculated ETA. + + await this.addOrUpdateEta({ + secondsRemaining: 1, + shuttleId: shuttle.id, + stopId: lastStop.stopId, + systemId: shuttle.systemId, + updatedTime: new Date(), + }); + } + const lastOrderedStop = await this.shuttleRepository.getOrderedStopByRouteAndStopId(shuttle.routeId, lastStop.stopId); await this.updateCascadingEta({ diff --git a/src/repositories/shuttle/eta/RedisSelfUpdatingETARepository.ts b/src/repositories/shuttle/eta/RedisSelfUpdatingETARepository.ts index 1755ec1..cabf5a3 100644 --- a/src/repositories/shuttle/eta/RedisSelfUpdatingETARepository.ts +++ b/src/repositories/shuttle/eta/RedisSelfUpdatingETARepository.ts @@ -102,9 +102,25 @@ export class RedisSelfUpdatingETARepository extends BaseRedisETARepository imple } private async handleShuttleUpdate(shuttle: IShuttle) { + const isAtStop = await this.shuttleRepository.checkIfShuttleIsAtStop(shuttle.id); const lastStop = await this.shuttleRepository.getShuttleLastStopArrival(shuttle.id); if (!lastStop) return; + if (isAtStop) { + // Update the ETA *to* the stop the shuttle is currently at, + // before starting from the current stop as normal. + // Account for cases where the shuttle arrived way earlier than + // expected based on the calculated ETA. + + await this.addOrUpdateEta({ + secondsRemaining: 1, + shuttleId: shuttle.id, + stopId: lastStop.stopId, + systemId: shuttle.systemId, + updatedTime: new Date(), + }); + } + const lastOrderedStop = await this.shuttleRepository.getOrderedStopByRouteAndStopId(shuttle.routeId, lastStop.stopId); await this.updateCascadingEta({ diff --git a/src/repositories/shuttle/eta/__tests__/SelfUpdatingETARepositorySharedTests.test.ts b/src/repositories/shuttle/eta/__tests__/SelfUpdatingETARepositorySharedTests.test.ts index 86ae52b..6d5d9b1 100644 --- a/src/repositories/shuttle/eta/__tests__/SelfUpdatingETARepositorySharedTests.test.ts +++ b/src/repositories/shuttle/eta/__tests__/SelfUpdatingETARepositorySharedTests.test.ts @@ -194,6 +194,41 @@ describe.each(repositoryImplementations)('$name', (holder) => { currentTime, shuttleSecondArrivalTimeAtFirstStop ); }); + + test("adds a 'stopgap' entry of 1 second when the shuttle arrives at a stop", async () => { + const { stop1, stop2, stop3, sampleShuttleNotInRepository: shuttle } = await setupRouteAndOrderedStops(); + + const shuttleSecondArrivalTimeAtFirstStop = new Date(2025, 0, 1, 12, 5, 0); + const currentTime = new Date(shuttleSecondArrivalTimeAtFirstStop.getTime() + 7 * 60 * 1000); + + // Populating travel time data + await populateTravelTimeDataForStops({ currentTime, shuttle, stop1, stop2, stop3 }); + + // Populate ETA data + // Simulate shuttle running early for second stop + shuttle.coordinates = stop1.coordinates; + await shuttleRepository.addOrUpdateShuttle( + shuttle, + shuttleSecondArrivalTimeAtFirstStop.getTime() + ); + + shuttle.coordinates = stop2.coordinates; + // Call twice to get the ETA repository to read the correct flag + await shuttleRepository.addOrUpdateShuttle( + shuttle, + currentTime.getTime(), + ); + await new Promise((resolve) => setTimeout(resolve, 500)); + + await shuttleRepository.addOrUpdateShuttle( + shuttle, + currentTime.getTime(), // ~8 minutes early + ); + await new Promise((resolve) => setTimeout(resolve, 500)); + + const eta = await repository.getEtaForShuttleAndStopId(shuttle.id, stop2.id); + expect(eta?.secondsRemaining).toEqual(1); + }); }); describe("handleShuttleWillLeaveStop", () => {