initial commit
This commit is contained in:
64
test/timetable.test.mjs
Normal file
64
test/timetable.test.mjs
Normal file
@@ -0,0 +1,64 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { loadTimetable } from '../server/timetable.mjs';
|
||||
import { isTimetable, localDate, timetableAvailable, timetableDepartures } from '../src/lib/timetable.mjs';
|
||||
import { passengerLink } from '../src/lib/passenger.mjs';
|
||||
|
||||
const now = Date.parse('2026-10-08T12:00:00+03:00');
|
||||
const route = { id: 37, route_number: '5', name: 'А — В', type: 'bus' };
|
||||
const stops = [{ stop_id: 1, sequence: 1, time_offset_minutes: 0, name: 'А' }, { stop_id: 2, sequence: 2, time_offset_minutes: 15, name: 'Б' }, { stop_id: 3, sequence: 3, time_offset_minutes: 30, name: 'В' }];
|
||||
const schedule = (day_type, trips, extra = {}) => ({ route_id: 37, day_type, trips, ...extra });
|
||||
async function bundle(schedules, options = {}) {
|
||||
return loadTimetable({ cityId: 1, cityName: 'Полоцк', stop: { id: 2, name: 'Б', routes: [route] }, now,
|
||||
upstream: async path => path.includes('holidays') ? options.holidays || [] : path.endsWith('/stops') ? stops : schedules });
|
||||
}
|
||||
test('full cache contains all trips and regenerates Friday and Saturday without network', async () => {
|
||||
const data = await bundle([schedule('weekday', ['08:00', '12:00', '15:00', '18:00', '22:00'].map(departureTime => ({ departureTime }))), schedule('saturday', [{ departureTime: '09:00' }])]);
|
||||
assert.equal(isTimetable(data, 1, 2), true);
|
||||
assert.equal(isTimetable(data, 1, 1), false);
|
||||
assert.equal(isTimetable({ ...data, routes: [{ ...data.routes[0], schedules: [{ dayType: 'weekday', trips: [{ arrivalSeconds: NaN }] }] }] }, 1, 2), false);
|
||||
const departures = timetableDepartures(data, '2026-10-09');
|
||||
assert.equal(departures.filter(item => localDate(Date.parse(item.scheduledAt)) === '2026-10-09').length, 5);
|
||||
assert.equal(departures.find(item => localDate(Date.parse(item.scheduledAt)) === '2026-10-10').scheduledTime, '09:15');
|
||||
assert.equal(timetableAvailable(data, now + 86400000), true);
|
||||
assert.equal(timetableAvailable(data, now + 7 * 86400000), false);
|
||||
});
|
||||
test('holiday timetable wins; missing holiday variant falls back to Sunday', async () => {
|
||||
const holidays = [{ date: '2026-10-09T00:00:00.000Z' }];
|
||||
const schedules = [schedule('weekday', [{ departureTime: '08:00' }]), schedule('sunday', [{ departureTime: '09:00' }])];
|
||||
const fallback = timetableDepartures(await bundle(schedules, { holidays }), '2026-10-09');
|
||||
assert.equal(fallback[0].scheduledTime, '09:15');
|
||||
const dedicated = timetableDepartures(await bundle([...schedules, schedule('holiday', [{ departureTime: '10:00' }])], { holidays }), '2026-10-09');
|
||||
assert.equal(dedicated[0].scheduledTime, '10:15');
|
||||
});
|
||||
test('trip restrictions and schedule validity apply to the actual service date', async () => {
|
||||
const data = await bundle([schedule('weekday', [{ departureTime: '08:00', daysOfWeek: [4] }, { departureTime: '09:00', exceptDaysOfWeek: [4] }], { valid_until: '2026-10-09' })]);
|
||||
assert.equal(timetableDepartures(data, '2026-10-08')[0].scheduledTime, '08:15');
|
||||
assert.equal(timetableDepartures(data, '2026-10-09')[0].scheduledTime, '09:15');
|
||||
assert.equal(timetableDepartures(data, '2026-10-12').length, 0);
|
||||
});
|
||||
test('previous service day retains after-midnight arrivals, with no time wrapping', async () => {
|
||||
const data = await bundle([schedule('weekday', [{ departureTime: '23:55', daysOfWeek: [4] }])]);
|
||||
const departures = timetableDepartures(data, '2026-10-09');
|
||||
assert.equal(departures.length, 1);
|
||||
assert.equal(departures[0].scheduledTime, '00:10');
|
||||
assert.equal(departures[0].scheduledAt, '2026-10-08T21:10:00.000Z');
|
||||
});
|
||||
test('partial and reverse trips use the start offset and correct terminal', async () => {
|
||||
const data = await bundle([schedule('weekday', [{ departureTime: '12:00', startStopId: 3, endStopId: 1, note: 'Обратно' }, { departureTime: '13:00', startStopId: 3, endStopId: 3 }])]);
|
||||
const departures = timetableDepartures(data, '2026-10-08').filter(item => localDate(Date.parse(item.scheduledAt)) === '2026-10-08');
|
||||
assert.equal(departures.length, 1); assert.equal(departures[0].scheduledTime, '12:15'); assert.equal(departures[0].destination, 'А');
|
||||
});
|
||||
test('failed calendar, route data, or malformed trips cannot publish a misleading complete cache', async () => {
|
||||
await assert.rejects(bundle([schedule('weekday', [{ departureTime: '12:00', startStopId: 999 }])]));
|
||||
await assert.rejects(bundle([schedule('weekday', [{ departureTime: '25:00' }])]));
|
||||
await assert.rejects(bundle([], { holidays: {} }));
|
||||
await assert.rejects(loadTimetable({ cityId: 1, cityName: 'Полоцк', stop: { id: 2, name: 'Б', routes: [route] }, now, upstream: async path => {
|
||||
if (path.endsWith('/schedules')) throw new Error('Upstream unavailable'); return path.includes('holidays') ? [] : stops;
|
||||
} }));
|
||||
});
|
||||
test('QR link identifies the exact city and platform, strips credentials and old query state', () => {
|
||||
assert.equal(passengerLink('https://pwa.ridebus.by/?demo=1', 1, 107), 'https://pwa.ridebus.by/?city=1&stop=107');
|
||||
assert.equal(passengerLink('javascript:alert(1)', 1, 1), 'https://pwa.ridebus.by/?city=1&stop=1');
|
||||
assert.equal(passengerLink('https://user:password@pwa.ridebus.by/#old', 2, 88), 'https://pwa.ridebus.by/?city=2&stop=88');
|
||||
});
|
||||
Reference in New Issue
Block a user