minotor
Version:
A lightweight client-side transit routing library.
282 lines (242 loc) • 8.86 kB
text/typescript
import assert from 'node:assert';
import { describe, it } from 'node:test';
import { encodePickUpDropOffTypes } from '../../gtfs/trips.js';
import { PickUpDropOffTypes, Route } from '../route.js';
import { TIME_ORIGIN, timeFromHMS } from '../time.js';
describe('Route', () => {
const stopTimes = new Uint16Array([
// Trip 0: Stop 1 -> Stop 2
timeFromHMS(8, 0, 0),
timeFromHMS(8, 1, 0),
timeFromHMS(8, 30, 0),
timeFromHMS(8, 31, 0),
// Trip 1: Stop 1 -> Stop 2
timeFromHMS(9, 0, 0),
timeFromHMS(9, 1, 0),
timeFromHMS(9, 30, 0),
timeFromHMS(9, 31, 0),
// Trip 2: Stop 1 -> Stop 2
timeFromHMS(10, 0, 0),
timeFromHMS(10, 1, 0),
timeFromHMS(10, 30, 0),
timeFromHMS(10, 31, 0),
]);
const pickupDropOffTypes = encodePickUpDropOffTypes(
[
// Trip 0
PickUpDropOffTypes.REGULAR,
PickUpDropOffTypes.NOT_AVAILABLE,
// Trip 1
PickUpDropOffTypes.REGULAR,
PickUpDropOffTypes.REGULAR,
// Trip 2
PickUpDropOffTypes.MUST_PHONE_AGENCY,
PickUpDropOffTypes.MUST_COORDINATE_WITH_DRIVER,
],
[
// Trip 0
PickUpDropOffTypes.REGULAR,
PickUpDropOffTypes.REGULAR,
// Trip 1
PickUpDropOffTypes.REGULAR,
PickUpDropOffTypes.REGULAR,
// Trip 2
PickUpDropOffTypes.REGULAR,
PickUpDropOffTypes.REGULAR,
],
);
const stops = new Uint32Array([1001, 1002]);
const serviceRouteId = 0;
const route = new Route(
0,
stopTimes,
pickupDropOffTypes,
stops,
serviceRouteId,
);
describe('constructor', () => {
it('should create a route with correct properties', () => {
assert.strictEqual(route.getNbStops(), 2);
assert.strictEqual(route.serviceRoute(), serviceRouteId);
});
it('should handle empty route', () => {
const emptyRoute = new Route(
0,
new Uint16Array([]),
new Uint8Array([]),
new Uint32Array([]),
1,
);
assert.strictEqual(emptyRoute.getNbStops(), 0);
assert.strictEqual(emptyRoute.serviceRoute(), 1);
});
});
describe('serialize', () => {
it('should serialize route data correctly', () => {
const serialized = route.serialize();
assert.deepStrictEqual(serialized.stopTimes, stopTimes);
assert.deepStrictEqual(serialized.pickupDropOffTypes, pickupDropOffTypes);
assert.deepStrictEqual(serialized.stops, stops);
assert.strictEqual(serialized.serviceRouteId, serviceRouteId);
});
});
describe('getNbStops', () => {
it('should return correct number of stops', () => {
assert.strictEqual(route.getNbStops(), 2);
});
});
describe('serviceRoute', () => {
it('should return correct service route ID', () => {
assert.strictEqual(route.serviceRoute(), serviceRouteId);
});
});
describe('arrivalAt', () => {
it('should return correct arrival time for trip 0 at stop index 0', () => {
const arrival = route.arrivalAt(0, 0);
assert.strictEqual(arrival, timeFromHMS(8, 0, 0));
});
it('should return correct arrival time for trip 1 at stop index 1', () => {
const arrival = route.arrivalAt(1, 1);
assert.strictEqual(arrival, timeFromHMS(9, 30, 0));
});
it('should throw error for invalid stop index', () => {
assert.throws(
() => route.arrivalAt(999, 0),
/StopId for stop at index 999 not found/,
);
});
it('should throw error for invalid trip index', () => {
assert.throws(() => route.arrivalAt(0, 999), /Arrival time not found/);
});
});
describe('departureFrom', () => {
it('should return correct departure time for trip 0 at stop index 0', () => {
const departure = route.departureFrom(0, 0);
assert.strictEqual(departure, timeFromHMS(8, 1, 0));
});
it('should return correct departure time for trip 2 at stop index 1', () => {
const departure = route.departureFrom(1, 2);
assert.strictEqual(departure, timeFromHMS(10, 31, 0));
});
it('should throw error for invalid stop index', () => {
assert.throws(
() => route.departureFrom(999, 0),
/StopId for stop at index 999 not found/,
);
});
it('should throw error for invalid trip index', () => {
assert.throws(
() => route.departureFrom(0, 999),
/Departure time not found/,
);
});
});
describe('pickUpTypeFrom', () => {
it('should return REGULAR pickup type for trip 0 at stop index 0', () => {
const pickUpType = route.pickUpTypeFrom(0, 0);
assert.strictEqual(pickUpType, PickUpDropOffTypes.REGULAR);
});
it('should return NOT_AVAILABLE pickup type for trip 0 at stop index 1', () => {
const pickUpType = route.pickUpTypeFrom(1, 0);
assert.strictEqual(pickUpType, PickUpDropOffTypes.NOT_AVAILABLE);
});
it('should return MUST_PHONE_AGENCY pickup type for trip 2 at stop index 0', () => {
const pickUpType = route.pickUpTypeFrom(0, 2);
assert.strictEqual(pickUpType, PickUpDropOffTypes.MUST_PHONE_AGENCY);
});
it('should throw error for invalid stop index', () => {
assert.throws(
() => route.pickUpTypeFrom(999, 0),
/StopId for stop at index 999 not found/,
);
});
it('should throw error for invalid trip index', () => {
assert.throws(
() => route.pickUpTypeFrom(0, 999),
/Pick up type not found/,
);
});
});
describe('dropOffTypeAt', () => {
it('should return REGULAR drop off type for trip 0 at stop index 0', () => {
const dropOffType = route.dropOffTypeAt(0, 0);
assert.strictEqual(dropOffType, PickUpDropOffTypes.REGULAR);
});
it('should return REGULAR drop off type for trip 1 at stop index 1', () => {
const dropOffType = route.dropOffTypeAt(1, 1);
assert.strictEqual(dropOffType, PickUpDropOffTypes.REGULAR);
});
it('should throw error for invalid stop index', () => {
assert.throws(
() => route.dropOffTypeAt(999, 0),
/StopId for stop at index 999 not found/,
);
});
it('should throw error for invalid trip index', () => {
assert.throws(
() => route.dropOffTypeAt(0, 999),
/Drop off type not found/,
);
});
});
describe('findEarliestTrip', () => {
it('should find earliest trip without time constraint', () => {
const tripIndex = route.findEarliestTrip(0);
assert.strictEqual(tripIndex, 0);
});
it('should find earliest trip after specified time', () => {
const afterTime = timeFromHMS(8, 30, 0);
const tripIndex = route.findEarliestTrip(0, afterTime);
assert.strictEqual(tripIndex, 1);
});
it('should find earliest trip with exact match time', () => {
const afterTime = timeFromHMS(9, 1, 0);
const tripIndex = route.findEarliestTrip(0, afterTime);
assert.strictEqual(tripIndex, 1);
});
it('should return undefined when no trip is available after specified time', () => {
const afterTime = timeFromHMS(23, 0, 0);
const tripIndex = route.findEarliestTrip(0, afterTime);
assert.strictEqual(tripIndex, undefined);
});
it('should find earliest trip', () => {
const tripIndex = route.findEarliestTrip(1);
// findEarliestTrip only filters by time, not pickup type
assert.strictEqual(tripIndex, 0);
});
it('should respect beforeTrip constraint', () => {
const tripIndex = route.findEarliestTrip(0, timeFromHMS(8, 2, 0), 1);
assert.strictEqual(tripIndex, undefined);
});
it('should return undefined when beforeTrip is 0', () => {
const tripIndex = route.findEarliestTrip(0, TIME_ORIGIN, 0);
assert.strictEqual(tripIndex, undefined);
});
it('should handle MUST_PHONE_AGENCY pickup type', () => {
const afterTime = timeFromHMS(9, 30, 0);
const tripIndex = route.findEarliestTrip(0, afterTime);
// Should find trip 2 even though it requires phone agency
assert.strictEqual(tripIndex, 2);
});
it('should throw error for invalid stop index', () => {
assert.throws(
() => route.findEarliestTrip(999),
/StopId for stop at index 999 not found/,
);
});
});
describe('stopRouteIndices', () => {
it('should return correct stop route indices for existing stop', () => {
const indices = route.stopRouteIndices(1001);
assert.deepStrictEqual(indices, [0]);
});
it('should return correct stop route indices for second stop', () => {
const indices = route.stopRouteIndices(1002);
assert.deepStrictEqual(indices, [1]);
});
it('should return empty array for non-existent stop', () => {
const indices = route.stopRouteIndices(9999);
assert.deepStrictEqual(indices, []);
});
});
});