db-vendo-client/test/e2e/db.js
Jannis R 339d64e901
convert to ESM 💥📝
2022-11-18 19:20:03 +01:00

482 lines
12 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import tap from 'tap'
import isRoughlyEqual from 'is-roughly-equal'
import maxBy from 'lodash/maxBy.js'
import flatMap from 'lodash/flatMap.js'
import last from 'lodash/last.js'
import {createWhen} from './lib/util.js'
import {createClient} from '../../index.js'
import {profile as dbProfile} from '../../p/db/index.js'
import {
createValidateStation,
createValidateTrip
} from './lib/validators.js'
import {createValidateFptfWith as createValidate} from './lib/validate-fptf-with.js'
import {testJourneysStationToStation} from './lib/journeys-station-to-station.js'
import {testJourneysStationToAddress} from './lib/journeys-station-to-address.js'
import {testJourneysStationToPoi} from './lib/journeys-station-to-poi.js'
import {testEarlierLaterJourneys} from './lib/earlier-later-journeys.js'
import {testLegCycleAlternatives} from './lib/leg-cycle-alternatives.js'
import {testRefreshJourney} from './lib/refresh-journey.js'
import {journeysFailsWithNoProduct} from './lib/journeys-fails-with-no-product.js'
import {testDepartures} from './lib/departures.js'
import {testDeparturesInDirection} from './lib/departures-in-direction.js'
import {testArrivals} from './lib/arrivals.js'
import {testJourneysWithDetour} from './lib/journeys-with-detour.js'
import {testReachableFrom} from './lib/reachable-from.js'
import {testServerInfo} from './lib/server-info.js'
const isObj = o => o !== null && 'object' === typeof o && !Array.isArray(o)
const minute = 60 * 1000
const T_MOCK = 1657618200 * 1000 // 2022-07-12T11:30+02:00
const when = createWhen(dbProfile.timezone, dbProfile.locale, T_MOCK)
const cfg = {
when,
stationCoordsOptional: false,
products: dbProfile.products,
minLatitude: 46.673100,
maxLatitude: 55.030671,
minLongitude: 6.896517,
maxLongitude: 16.180237
}
const validate = createValidate(cfg)
const assertValidPrice = (t, p) => {
t.ok(p)
if (p.amount !== null) {
t.equal(typeof p.amount, 'number')
t.ok(p.amount > 0)
}
if (p.hint !== null) {
t.equal(typeof p.hint, 'string')
t.ok(p.hint)
}
}
const client = createClient(dbProfile, 'public-transport/hafas-client:test')
const berlinHbf = '8011160'
const münchenHbf = '8000261'
const jungfernheide = '8011167'
const blnSchwedterStr = '732652'
const westhafen = '8089116'
const wedding = '8089131'
const württembergallee = '731084'
const regensburgHbf = '8000309'
const blnOstbahnhof = '8010255'
const blnTiergarten = '8089091'
const blnJannowitzbrücke = '8089019'
const potsdamHbf = '8012666'
const berlinSüdkreuz = '8011113'
const kölnHbf = '8000207'
tap.test('journeys  Berlin Schwedter Str. to München Hbf', async (t) => {
const res = await client.journeys(blnSchwedterStr, münchenHbf, {
results: 4,
departure: when,
stopovers: true
})
await testJourneysStationToStation({
test: t,
res,
validate,
fromId: blnSchwedterStr,
toId: münchenHbf
})
// todo: find a journey where there pricing info is always available
for (let journey of res.journeys) {
if (journey.price) assertValidPrice(t, journey.price)
}
t.end()
})
// todo: journeys, only one product
tap.test('journeys fails with no product', async (t) => {
await journeysFailsWithNoProduct({
test: t,
fetchJourneys: client.journeys,
fromId: blnSchwedterStr,
toId: münchenHbf,
when,
products: dbProfile.products,
})
t.end()
})
tap.test('Berlin Schwedter Str. to Torfstraße 17', async (t) => {
const torfstr = {
type: 'location',
address: 'Torfstraße 17',
latitude: 52.5416823,
longitude: 13.3491223
}
const res = await client.journeys(blnSchwedterStr, torfstr, {
results: 3,
departure: when
})
await testJourneysStationToAddress({
test: t,
res,
validate,
fromId: blnSchwedterStr,
to: torfstr
})
t.end()
})
tap.test('Berlin Schwedter Str. to ATZE Musiktheater', async (t) => {
const atze = {
type: 'location',
id: '991598902',
poi: true,
name: 'Berlin, Atze Musiktheater für Kinder (Kultur und U',
latitude: 52.542417,
longitude: 13.350437
}
const res = await client.journeys(blnSchwedterStr, atze, {
results: 3,
departure: when
})
await testJourneysStationToPoi({
test: t,
res,
validate,
fromId: blnSchwedterStr,
to: atze
})
t.end()
})
tap.test('journeys: via works with detour', async (t) => {
// Going from Westhafen to Wedding via Württembergalle without detour
// is currently impossible. We check if the routing engine computes a detour.
const res = await client.journeys(westhafen, wedding, {
via: württembergallee,
results: 1,
departure: when,
stopovers: true
})
await testJourneysWithDetour({
test: t,
res,
validate,
detourIds: [württembergallee]
})
t.end()
})
// todo: walkingSpeed "Berlin - Charlottenburg, Hallerstraße" -> jungfernheide
// todo: without detour
tap.test('earlier/later journeys, Jungfernheide -> München Hbf', async (t) => {
await testEarlierLaterJourneys({
test: t,
fetchJourneys: client.journeys,
validate,
fromId: jungfernheide,
toId: münchenHbf,
when
})
t.end()
})
tap.skip('journeys  leg cycle & alternatives', async (t) => {
await testLegCycleAlternatives({
test: t,
fetchJourneys: client.journeys,
fromId: blnTiergarten,
toId: blnJannowitzbrücke,
when,
})
t.end()
})
tap.test('refreshJourney', async (t) => {
await testRefreshJourney({
test: t,
fetchJourneys: client.journeys,
refreshJourney: client.refreshJourney,
validate,
fromId: jungfernheide,
toId: münchenHbf,
when
})
t.end()
})
tap.skip('journeysFromTrip U Mehringdamm to U Naturkundemuseum, reroute to Spittelmarkt.', async (t) => {
const blnMehringdamm = '730939'
const blnStadtmitte = '732541'
const blnNaturkundemuseum = '732539'
const blnSpittelmarkt = '732543'
const isU6Leg = leg => (
leg.line && leg.line.name
&& leg.line.name.toUpperCase().replace(/\s+/g, '') === 'U6'
)
const sameStopOrStation = (stopA) => (stopB) => {
if (stopA.id && stopB.id && stopA.id === stopB.id) return true
const statA = stopA.stat && stopA.stat.id || NaN
const statB = stopB.stat && stopB.stat.id || NaN
return (statA === statB || stopA.id === statB || stopB.id === statA)
}
const departureOf = st => +new Date(st.departure || st.scheduledDeparture)
const arrivalOf = st => +new Date(st.arrival || st.scheduledArrival)
// `journeysFromTrip` only supports queries *right now*, so we can't use `when` as in all
// other tests. To make the test less brittle, we pick a connection that is served all night. 🙄
const when = new Date()
const validate = createValidate({...cfg, when})
const findTripBetween = async (stopAId, stopBId, products = {}) => {
const {journeys} = await client.journeys(stopAId, stopBId, {
departure: new Date(when - 10 * minute),
transfers: 0, products,
results: 8, stopovers: false, remarks: false,
})
for (const j of journeys) {
const l = j.legs.find(isU6Leg)
if (!l) continue
const t = await client.trip(l.tripId, {
stopovers: true, remarks: false
})
const pastStopovers = t.stopovers
.filter(st => departureOf(st) < Date.now()) // todo: <= ?
const hasStoppedAtA = pastStopovers
.find(sameStopOrStation({id: stopAId}))
const willStopAtB = t.stopovers
.filter(st => arrivalOf(st) > Date.now()) // todo: >= ?
.find(sameStopOrStation({id: stopBId}))
if (hasStoppedAtA && willStopAtB) {
const prevStopover = maxBy(pastStopovers, departureOf)
return {trip: t, prevStopover}
}
}
return {trip: null, prevStopover: null}
}
// Find a vehicle from U Mehringdamm to U Stadtmitte (to the north) that is currently
// between these two stations.
const {trip, prevStopover} = await findTripBetween(blnMehringdamm, blnStadtmitte, {
regionalExp: false, regional: false, suburban: false
})
t.ok(trip, 'precondition failed: trip not found')
t.ok(prevStopover, 'precondition failed: previous stopover missing')
// todo: "Error: Suche aus dem Zug: Vor Abfahrt des Zuges"
const newJourneys = await client.journeysFromTrip(trip.id, prevStopover, blnSpittelmarkt, {
results: 3, stopovers: true, remarks: false
})
// Validate with fake prices.
const withFakePrice = (j) => {
const clone = Object.assign({}, j)
clone.price = {amount: 123, currency: 'EUR'}
return clone
}
// todo: there is no such validator!
validate(t, newJourneys.map(withFakePrice), 'journeysFromTrip', 'newJourneys')
for (let i = 0; i < newJourneys.length; i++) {
const j = newJourneys[i]
const n = `newJourneys[${i}]`
const legOnTrip = j.legs.find(l => l.tripId === trip.id)
t.ok(legOnTrip, n + ': leg with trip ID not found')
t.equal(last(legOnTrip.stopovers).stop.id, blnStadtmitte)
}
})
tap.test('trip details', async (t) => {
const res = await client.journeys(berlinHbf, münchenHbf, {
results: 1, departure: when
})
const p = res.journeys[0].legs.find(l => !l.walking)
t.ok(p.tripId, 'precondition failed')
t.ok(p.line.name, 'precondition failed')
const tripRes = await client.trip(p.tripId, {when})
const validate = createValidate(cfg, {
trip: (cfg) => {
const validateTrip = createValidateTrip(cfg)
const validateTripWithFakeDirection = (val, trip, name) => {
validateTrip(val, {
...trip,
direction: trip.direction || 'foo', // todo, see #49
}, name)
}
return validateTripWithFakeDirection
}
})
validate(t, tripRes, 'tripResult', 'tripRes')
t.end()
})
tap.test('departures at Berlin Schwedter Str.', async (t) => {
const res = await client.departures(blnSchwedterStr, {
duration: 5, when,
})
await testDepartures({
test: t,
res,
validate,
id: blnSchwedterStr
})
t.end()
})
tap.test('departures with station object', async (t) => {
const res = await client.departures({
type: 'station',
id: jungfernheide,
name: 'Berlin Jungfernheide',
location: {
type: 'location',
latitude: 1.23,
longitude: 2.34
}
}, {when})
validate(t, res, 'departuresResponse', 'res')
t.end()
})
tap.test('departures at Berlin Hbf in direction of Berlin Ostbahnhof', async (t) => {
await testDeparturesInDirection({
test: t,
fetchDepartures: client.departures,
fetchTrip: client.trip,
id: berlinHbf,
directionIds: [blnOstbahnhof, '8089185', '732676'],
when,
validate
})
t.end()
})
tap.test('arrivals at Berlin Schwedter Str.', async (t) => {
const res = await client.arrivals(blnSchwedterStr, {
duration: 5, when,
})
await testArrivals({
test: t,
res,
validate,
id: blnSchwedterStr
})
t.end()
})
tap.test('nearby Berlin Jungfernheide', async (t) => {
const nearby = await client.nearby({
type: 'location',
latitude: 52.530273,
longitude: 13.299433
}, {
results: 2, distance: 400
})
validate(t, nearby, 'locations', 'nearby')
t.equal(nearby.length, 2)
const s0 = nearby[0]
t.equal(s0.id, jungfernheide)
t.equal(s0.name, 'Berlin Jungfernheide')
t.ok(isRoughlyEqual(.0005, s0.location.latitude, 52.530408))
t.ok(isRoughlyEqual(.0005, s0.location.longitude, 13.299424))
t.ok(s0.distance >= 0)
t.ok(s0.distance <= 100)
t.end()
})
tap.test('locations named Jungfernheide', async (t) => {
const locations = await client.locations('Jungfernheide', {
results: 10
})
validate(t, locations, 'locations', 'locations')
t.ok(locations.length <= 10)
t.ok(locations.some((l) => {
return l.station && l.station.id === jungfernheide || l.id === jungfernheide
}), 'Jungfernheide not found')
t.end()
})
tap.test('stop', async (t) => {
const s = await client.stop(regensburgHbf)
validate(t, s, ['stop', 'station'], 'stop')
t.equal(s.id, regensburgHbf)
t.end()
})
tap.test('line with additionalName', async (t) => {
const {departures} = await client.departures(potsdamHbf, {
when,
duration: 12 * 60, // 12 minutes
products: {bus: false, suburban: false, tram: false}
})
t.ok(departures.some(d => d.line && d.line.additionalName))
t.end()
})
tap.test('radar', async (t) => {
const res = await client.radar({
north: 52.52411,
west: 13.41002,
south: 52.51942,
east: 13.41709
}, {
duration: 5 * 60, when
})
validate(t, res, 'radarResult', 'res')
t.end()
})
tap.test('reachableFrom', {timeout: 20 * 1000}, async (t) => {
const torfstr17 = {
type: 'location',
address: 'Torfstraße 17',
latitude: 52.5416823,
longitude: 13.3491223
}
await testReachableFrom({
test: t,
reachableFrom: client.reachableFrom,
address: torfstr17,
when,
maxDuration: 15,
validate
})
t.end()
})
tap.test('serverInfo works', async (t) => {
await testServerInfo({
test: t,
fetchServerInfo: client.serverInfo,
})
})