merge #57: rewrite the tests

This commit is contained in:
Jannis Redmann 2018-06-10 15:06:08 +02:00 committed by GitHub
commit 47155cdb83
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 1468 additions and 1752 deletions

View file

@ -62,7 +62,11 @@ const createParseJourney = (profile, stations, lines, remarks, polylines) => {
) { ) {
const tariff = j.trfRes.fareSetL[0].fareL[0] const tariff = j.trfRes.fareSetL[0].fareL[0]
if (tariff.prc >= 0) { // wat if (tariff.prc >= 0) { // wat
res.price = {amount: tariff.prc / 100, hint: null} res.price = {
amount: tariff.prc / 100,
currency: 'EUR',
hint: null
}
} }
} }

View file

@ -56,7 +56,7 @@
"tap-spec": "^4.1.1", "tap-spec": "^4.1.1",
"tape": "^4.8.0", "tape": "^4.8.0",
"tape-promise": "^3.0.0", "tape-promise": "^3.0.0",
"validate-fptf": "^1.2.1", "validate-fptf": "^2.0.1",
"vbb-stations-autocomplete": "^3.1.0" "vbb-stations-autocomplete": "^3.1.0"
}, },
"scripts": { "scripts": {

View file

@ -1,83 +1,55 @@
'use strict' 'use strict'
const getStations = require('db-stations').full const stations = require('db-stations/full.json')
const a = require('assert')
const tapePromise = require('tape-promise').default const tapePromise = require('tape-promise').default
const tape = require('tape') const tape = require('tape')
const isRoughlyEqual = require('is-roughly-equal') const isRoughlyEqual = require('is-roughly-equal')
const co = require('./co') const {createWhen} = require('./lib/util')
const co = require('./lib/co')
const createClient = require('..') const createClient = require('..')
const dbProfile = require('../p/db') const dbProfile = require('../p/db')
const allProducts = require('../p/db/products') const products = require('../p/db/products')
const { const {
assertValidStation, station: createValidateStation,
assertValidPoi, journeyLeg: createValidateJourneyLeg
assertValidAddress, } = require('./lib/validators')
assertValidLocation, const createValidate = require('./lib/validate-fptf-with')
assertValidLine, const testJourneysStationToStation = require('./lib/journeys-station-to-station')
assertValidStopover, const testJourneysStationToAddress = require('./lib/journeys-station-to-address')
createWhen, assertValidWhen const testJourneysStationToPoi = require('./lib/journeys-station-to-poi')
} = require('./util.js') const testEarlierLaterJourneys = require('./lib/earlier-later-journeys')
const journeysFailsWithNoProduct = require('./lib/journeys-fails-with-no-product')
const testDepartures = require('./lib/departures')
const testJourneysWithDetour = require('./lib/journeys-with-detour')
const isObj = o => o !== null && 'object' === typeof o && !Array.isArray(o)
const when = createWhen('Europe/Berlin', 'de-DE') const when = createWhen('Europe/Berlin', 'de-DE')
const assertValidStationProducts = (t, p) => { const cfg = {
t.ok(p) when,
t.equal(typeof p.nationalExp, 'boolean') stationCoordsOptional: false,
t.equal(typeof p.national, 'boolean') products
t.equal(typeof p.regionalExp, 'boolean')
t.equal(typeof p.regional, 'boolean')
t.equal(typeof p.suburban, 'boolean')
t.equal(typeof p.bus, 'boolean')
t.equal(typeof p.ferry, 'boolean')
t.equal(typeof p.subway, 'boolean')
t.equal(typeof p.tram, 'boolean')
t.equal(typeof p.taxi, 'boolean')
} }
const findStation = (id) => new Promise((yay, nay) => { const _validateStation = createValidateStation(cfg)
const stations = getStations() const validateStation = (validate, s, name) => {
stations _validateStation(validate, s, name)
.once('error', nay) const match = stations.some(station => (
.on('data', (s) => { station.id === s.id ||
if ( (station.additionalIds && station.additionalIds.includes(s.id))
s.id === id || ))
(s.additionalIds && s.additionalIds.includes(id)) if (!match) {
) { console.error(name + `.id: unknown ID "${s.id}"`)
yay(s)
stations.destroy()
}
})
.once('end', yay)
})
const isJungfernheide = (s) => {
return s.type === 'station' &&
(s.id === '008011167' || s.id === jungfernh) &&
s.name === 'Berlin Jungfernheide' &&
s.location &&
isRoughlyEqual(.0005, s.location.latitude, 52.530408) &&
isRoughlyEqual(.0005, s.location.longitude, 13.299424)
}
const assertIsJungfernheide = (t, s) => {
t.equal(s.type, 'station')
t.ok(s.id === '008011167' || s.id === jungfernh, 'id should be 8011167')
t.equal(s.name, 'Berlin Jungfernheide')
t.ok(s.location)
t.ok(isRoughlyEqual(.0005, s.location.latitude, 52.530408))
t.ok(isRoughlyEqual(.0005, s.location.longitude, 13.299424))
}
// todo: DRY with assertValidStationProducts
// todo: DRY with other tests
const assertValidProducts = (t, p) => {
for (let product of allProducts) {
product = product.id
t.equal(typeof p[product], 'boolean', 'product ' + p + ' must be a boolean')
} }
} }
const validate = createValidate(cfg, {
station: validateStation
})
const assertValidPrice = (t, p) => { const assertValidPrice = (t, p) => {
t.ok(p) t.ok(p)
if (p.amount !== null) { if (p.amount !== null) {
@ -93,226 +65,167 @@ const assertValidPrice = (t, p) => {
const test = tapePromise(tape) const test = tapePromise(tape)
const client = createClient(dbProfile) const client = createClient(dbProfile)
const jungfernh = '8011167'
const berlinHbf = '8011160' const berlinHbf = '8011160'
const münchenHbf = '8000261' const münchenHbf = '8000261'
const hannoverHbf = '8000152' const jungfernheide = '8011167'
const blnSchwedterStr = '732652'
const westhafen = '008089116'
const wedding = '008089131'
const württembergallee = '731084'
const regensburgHbf = '8000309' const regensburgHbf = '8000309'
test('Berlin Jungfernheide to München Hbf', co(function* (t) { test('journeys  Berlin Schwedter Str. to München Hbf', co(function* (t) {
const journeys = yield client.journeys(jungfernh, münchenHbf, { const journeys = yield client.journeys(blnSchwedterStr, münchenHbf, {
departure: when, passedStations: true results: 3, departure: when, passedStations: true
}) })
t.ok(Array.isArray(journeys)) yield testJourneysStationToStation({
t.ok(journeys.length > 0, 'no journeys') test: t,
journeys,
validate,
fromId: blnSchwedterStr,
toId: münchenHbf
})
// todo: find a journey where there pricing info is always available
for (let journey of journeys) { for (let journey of journeys) {
t.equal(journey.type, 'journey')
t.ok(Array.isArray(journey.legs))
t.ok(journey.legs.length > 0, 'no legs')
const leg = journey.legs[0] // todo: all legs
assertValidStation(t, leg.origin)
assertValidStationProducts(t, leg.origin.products)
if (!(yield findStation(leg.origin.id))) {
console.error('unknown station', leg.origin.id, leg.origin.name)
}
assertValidWhen(t, leg.departure, when)
t.equal(typeof leg.departurePlatform, 'string')
assertValidStation(t, leg.destination)
assertValidStationProducts(t, leg.origin.products)
if (!(yield findStation(leg.destination.id))) {
console.error('unknown station', leg.destination.id, leg.destination.name)
}
assertValidWhen(t, leg.arrival, when)
t.equal(typeof leg.arrivalPlatform, 'string')
assertValidLine(t, leg.line)
t.ok(Array.isArray(leg.passed))
for (let stopover of leg.passed) assertValidStopover(t, stopover)
if (journey.price) assertValidPrice(t, journey.price) if (journey.price) assertValidPrice(t, journey.price)
} }
t.end() t.end()
})) }))
test('Berlin Jungfernheide to Torfstraße 17', co(function* (t) { // todo: journeys, only one product
const journeys = yield client.journeys(jungfernh, {
type: 'location', address: 'Torfstraße 17',
latitude: 52.5416823, longitude: 13.3491223
}, {departure: when})
t.ok(Array.isArray(journeys)) test('journeys  fails with no product', (t) => {
t.ok(journeys.length >= 1, 'no journeys') journeysFailsWithNoProduct({
const journey = journeys[0] test: t,
const leg = journey.legs[journey.legs.length - 1] fetchJourneys: client.journeys,
fromId: blnSchwedterStr,
toId: münchenHbf,
when,
products
})
t.end()
})
assertValidStation(t, leg.origin) test('Berlin Schwedter Str. to Torfstraße 17', co(function* (t) {
assertValidStationProducts(t, leg.origin.products) const torfstr = {
if (!(yield findStation(leg.origin.id))) { type: 'location',
console.error('unknown station', leg.origin.id, leg.origin.name) address: 'Torfstraße 17',
latitude: 52.5416823,
longitude: 13.3491223
} }
if (leg.origin.products) assertValidProducts(t, leg.origin.products) const journeys = yield client.journeys(blnSchwedterStr, torfstr, {
assertValidWhen(t, leg.departure, when) results: 3,
assertValidWhen(t, leg.arrival, when) departure: when
})
const d = leg.destination
assertValidAddress(t, d)
t.equal(d.address, 'Torfstraße 17')
t.ok(isRoughlyEqual(.0001, d.latitude, 52.5416823))
t.ok(isRoughlyEqual(.0001, d.longitude, 13.3491223))
yield testJourneysStationToAddress({
test: t,
journeys,
validate,
fromId: blnSchwedterStr,
to: torfstr
})
t.end() t.end()
})) }))
test('Berlin Jungfernheide to ATZE Musiktheater', co(function* (t) { test('Berlin Schwedter Str. to ATZE Musiktheater', co(function* (t) {
const journeys = yield client.journeys(jungfernh, { const atze = {
type: 'location', id: '991598902', name: 'ATZE Musiktheater', type: 'location',
latitude: 52.542417, longitude: 13.350437 id: '991598902',
}, {departure: when}) name: 'ATZE Musiktheater',
latitude: 52.542417,
t.ok(Array.isArray(journeys)) longitude: 13.350437
t.ok(journeys.length >= 1, 'no journeys')
const journey = journeys[0]
const leg = journey.legs[journey.legs.length - 1]
assertValidStation(t, leg.origin)
assertValidStationProducts(t, leg.origin.products)
if (!(yield findStation(leg.origin.id))) {
console.error('unknown station', leg.origin.id, leg.origin.name)
} }
if (leg.origin.products) assertValidProducts(t, leg.origin.products) const journeys = yield client.journeys(blnSchwedterStr, atze, {
assertValidWhen(t, leg.departure, when) results: 3,
assertValidWhen(t, leg.arrival, when) departure: when
})
const d = leg.destination
assertValidPoi(t, d)
t.equal(d.name, 'ATZE Musiktheater')
t.ok(isRoughlyEqual(.0001, d.latitude, 52.542399))
t.ok(isRoughlyEqual(.0001, d.longitude, 13.350402))
yield testJourneysStationToPoi({
test: t,
journeys,
validate,
fromId: blnSchwedterStr,
to: atze
})
t.end() t.end()
})) }))
test('journeys: via works with detour', co(function* (t) { test('journeys: via works with detour', co(function* (t) {
// Going from Westhafen to Wedding via Württembergalle without detour // Going from Westhafen to Wedding via Württembergalle without detour
// is currently impossible. We check if the routing engine computes a detour. // is currently impossible. We check if the routing engine computes a detour.
const westhafen = '008089116' const journeys = yield client.journeys(westhafen, wedding, {
const wedding = '008089131'
const württembergallee = '731084'
const [journey] = yield client.journeys(westhafen, wedding, {
via: württembergallee, via: württembergallee,
results: 1, results: 1,
departure: when, departure: when,
passedStations: true passedStations: true
}) })
t.ok(journey) yield testJourneysWithDetour({
test: t,
const l = journey.legs.some(l => l.passed && l.passed.some(p => p.station.id === württembergallee)) journeys,
t.ok(l, 'Württembergalle is not being passed') validate,
detourIds: [württembergallee]
t.end()
}))
test('journeys: via works without detour', co(function* (t) {
// When going from Ruhleben to Zoo via Kastanienallee, there is *no need*
// to change trains / no need for a "detour".
const ruhleben = '000731058'
const zoo = '008010406'
const kastanienallee = '730983'
const [journey] = yield client.journeys(ruhleben, zoo, {
via: kastanienallee,
results: 1,
departure: when,
passedStations: true
}) })
t.ok(journey)
const l = journey.legs.some(l => l.passed && l.passed.some(p => p.station.id === kastanienallee))
t.ok(l, 'Kastanienallee is not being passed')
t.end() t.end()
})) }))
// todo: without detour
test('earlier/later journeys, Jungfernheide -> München Hbf', co(function* (t) { test('earlier/later journeys, Jungfernheide -> München Hbf', co(function* (t) {
const model = yield client.journeys(jungfernh, münchenHbf, { yield testEarlierLaterJourneys({
results: 3, departure: when test: t,
fetchJourneys: client.journeys,
validate,
fromId: jungfernheide,
toId: münchenHbf
}) })
t.equal(typeof model.earlierRef, 'string')
t.ok(model.earlierRef)
t.equal(typeof model.laterRef, 'string')
t.ok(model.laterRef)
// when and earlierThan/laterThan should be mutually exclusive
t.throws(() => {
client.journeys(jungfernh, münchenHbf, {
departure: when, earlierThan: model.earlierRef
})
})
t.throws(() => {
client.journeys(jungfernh, münchenHbf, {
departure: when, laterThan: model.laterRef
})
})
let earliestDep = Infinity, latestDep = -Infinity
for (let j of model) {
const dep = +new Date(j.legs[0].departure)
if (dep < earliestDep) earliestDep = dep
else if (dep > latestDep) latestDep = dep
}
const earlier = yield client.journeys(jungfernh, münchenHbf, {
results: 3,
// todo: single journey ref?
earlierThan: model.earlierRef
})
for (let j of earlier) {
t.ok(new Date(j.legs[0].departure) < earliestDep)
}
const later = yield client.journeys(jungfernh, münchenHbf, {
results: 3,
// todo: single journey ref?
laterThan: model.laterRef
})
for (let j of later) {
t.ok(new Date(j.legs[0].departure) > latestDep)
}
t.end() t.end()
})) }))
test('departures at Berlin Jungfernheide', co(function* (t) { test('journey leg details', co(function* (t) {
const deps = yield client.departures(jungfernh, { const journeys = yield client.journeys(berlinHbf, münchenHbf, {
results: 1, departure: when
})
const p = journeys[0].legs[0]
t.ok(p.id, 'precondition failed')
t.ok(p.line.name, 'precondition failed')
const leg = yield client.journeyLeg(p.id, p.line.name, {when})
const validateJourneyLeg = createValidateJourneyLeg(cfg)
const validate = createValidate(cfg, {
journeyLeg: (validate, leg, name) => {
if (!leg.direction) leg.direction = 'foo' // todo, see #49
validateJourneyLeg(validate, leg, name)
}
})
validate(t, leg, 'journeyLeg', 'leg')
t.end()
}))
test('departures at Berlin Schwedter Str.', co(function* (t) {
const departures = yield client.departures(blnSchwedterStr, {
duration: 5, when duration: 5, when
}) })
t.ok(Array.isArray(deps)) yield testDepartures({
for (let dep of deps) { test: t,
assertValidStation(t, dep.station) departures,
assertValidStationProducts(t, dep.station.products) validate,
if (!(yield findStation(dep.station.id))) { id: blnSchwedterStr
console.error('unknown station', dep.station.id, dep.station.name) })
}
if (dep.station.products) assertValidProducts(t, dep.station.products)
assertValidWhen(t, dep.when, when)
}
t.end() t.end()
})) }))
test('departures with station object', co(function* (t) { test('departures with station object', co(function* (t) {
yield client.departures({ const deps = yield client.departures({
type: 'station', type: 'station',
id: jungfernh, id: jungfernheide,
name: 'Berlin Jungfernheide', name: 'Berlin Jungfernheide',
location: { location: {
type: 'location', type: 'location',
@ -321,7 +234,7 @@ test('departures with station object', co(function* (t) {
} }
}, {when}) }, {when})
t.ok('did not fail') validate(t, deps, 'departures', 'departures')
t.end() t.end()
})) }))
@ -334,17 +247,18 @@ test('nearby Berlin Jungfernheide', co(function* (t) {
results: 2, distance: 400 results: 2, distance: 400
}) })
t.ok(Array.isArray(nearby)) validate(t, nearby, 'locations', 'nearby')
t.equal(nearby.length, 2) t.equal(nearby.length, 2)
assertIsJungfernheide(t, nearby[0]) const s0 = nearby[0]
t.ok(nearby[0].distance >= 0) // todo: trim IDs
t.ok(nearby[0].distance <= 100) t.ok(s0.id === '008011167' || s0.id === jungfernheide)
t.equal(s0.name, 'Berlin Jungfernheide')
for (let n of nearby) { t.ok(isRoughlyEqual(.0005, s0.location.latitude, 52.530408))
if (n.type === 'station') assertValidStation(t, n) t.ok(isRoughlyEqual(.0005, s0.location.longitude, 13.299424))
else assertValidLocation(t, n) t.ok(s0.distance >= 0)
} t.ok(s0.distance <= 100)
t.end() t.end()
})) }))
@ -354,29 +268,21 @@ test('locations named Jungfernheide', co(function* (t) {
results: 10 results: 10
}) })
t.ok(Array.isArray(locations)) validate(t, locations, 'locations', 'locations')
t.ok(locations.length > 0)
t.ok(locations.length <= 10) t.ok(locations.length <= 10)
t.ok(locations.some((l) => {
for (let l of locations) { // todo: trim IDs
if (l.type === 'station') assertValidStation(t, l) return l.id === '008011167' || l.id === jungfernheide
else assertValidLocation(t, l) }), 'Jungfernheide not found')
}
t.ok(locations.some(isJungfernheide))
t.end() t.end()
})) }))
test('station', co(function* (t) { test('station', co(function* (t) {
const loc = yield client.station(regensburgHbf) const s = yield client.station(regensburgHbf)
assertValidStation(t, loc) validate(t, s, 'station', 'station')
t.equal(loc.id, regensburgHbf) t.equal(s.id, regensburgHbf)
t.ok(Array.isArray(loc.lines))
if (Array.isArray(loc.lines)) {
for (let line of loc.lines) assertValidLine(t, line)
}
t.end() t.end()
})) }))

View file

@ -3,374 +3,244 @@
const tapePromise = require('tape-promise').default const tapePromise = require('tape-promise').default
const tape = require('tape') const tape = require('tape')
const isRoughlyEqual = require('is-roughly-equal') const isRoughlyEqual = require('is-roughly-equal')
const validateFptf = require('validate-fptf')
const co = require('./co') const {createWhen} = require('./lib/util')
const co = require('./lib/co')
const createClient = require('..') const createClient = require('..')
const insaProfile = require('../p/insa') const insaProfile = require('../p/insa')
const allProducts = require('../p/insa/products') const products = require('../p/insa/products')
const { const createValidate = require('./lib/validate-fptf-with')
assertValidStation, const testJourneysStationToStation = require('./lib/journeys-station-to-station')
assertValidPoi, const testJourneysStationToAddress = require('./lib/journeys-station-to-address')
assertValidAddress, const testJourneysStationToPoi = require('./lib/journeys-station-to-poi')
assertValidLocation, const testEarlierLaterJourneys = require('./lib/earlier-later-journeys')
assertValidLine, const journeysFailsWithNoProduct = require('./lib/journeys-fails-with-no-product')
assertValidStopover, const testDepartures = require('./lib/departures')
hour, const testJourneysWithDetour = require('./lib/journeys-with-detour')
createWhen,
assertValidWhen const isObj = o => o !== null && 'object' === typeof o && !Array.isArray(o)
} = require('./util.js')
const when = createWhen('Europe/Berlin', 'de-DE') const when = createWhen('Europe/Berlin', 'de-DE')
const assertValidStationProducts = (t, p) => { const cfg = {
t.ok(p) when,
t.equal(typeof p.nationalExp, 'boolean') stationCoordsOptional: false,
t.equal(typeof p.national, 'boolean') products
t.equal(typeof p.regional, 'boolean')
t.equal(typeof p.suburban, 'boolean')
t.equal(typeof p.tram, 'boolean')
t.equal(typeof p.bus, 'boolean')
t.equal(typeof p.tourismTrain, 'boolean')
} }
const isMagdeburgHbf = s => { const validate = createValidate(cfg, {})
return (
s.type === 'station' &&
(s.id === '8010224' || s.id === '008010224') &&
s.name === 'Magdeburg Hbf' &&
s.location &&
isRoughlyEqual(.001, s.location.latitude, 52.130352) &&
isRoughlyEqual(.001, s.location.longitude, 11.626891)
)
}
const assertIsMagdeburgHbf = (t, s) => {
t.equal(s.type, 'station')
t.ok(s.id === '8010224' || s.id === '008010224', 'id should be 8010224')
t.equal(s.name, 'Magdeburg Hbf')
t.ok(s.location)
t.ok(isRoughlyEqual(.001, s.location.latitude, 52.130352))
t.ok(isRoughlyEqual(.001, s.location.longitude, 11.626891))
}
// todo: DRY with assertValidStationProducts
// todo: DRY with other tests
const assertValidProducts = (t, p) => {
for (let product of allProducts) {
product = product.id
t.equal(typeof p[product], 'boolean', 'product ' + p + ' must be a boolean')
}
}
const test = tapePromise(tape) const test = tapePromise(tape)
const client = createClient(insaProfile) const client = createClient(insaProfile)
test('Magdeburg Hbf to Magdeburg-Buckau', co(function*(t) { const magdeburgHbf = '8010224'
const magdeburgHbf = '8010224' const magdeburgBuckau = '8013456'
const magdeburgBuckau = '8013456' const leiterstr = '7464'
const hasselbachplatzSternstrasse = '000006545'
const stendal = '008010334'
const dessau = '008010077'
test('journeys  Magdeburg Hbf to Magdeburg-Buckau', co(function* (t) {
const journeys = yield client.journeys(magdeburgHbf, magdeburgBuckau, { const journeys = yield client.journeys(magdeburgHbf, magdeburgBuckau, {
results: 3,
departure: when, departure: when,
passedStations: true passedStations: true
}) })
t.ok(Array.isArray(journeys)) yield testJourneysStationToStation({
t.ok(journeys.length > 0, 'no journeys') test: t,
for (let journey of journeys) { journeys,
t.ok(Array.isArray(journey.legs)) validate,
t.ok(journey.legs.length > 0, 'no legs') fromId: magdeburgHbf,
const leg = journey.legs[0] // todo: all legs toId: magdeburgBuckau
})
assertValidStation(t, leg.origin)
assertValidStationProducts(t, leg.origin.products)
assertValidWhen(t, leg.departure, when)
t.equal(typeof leg.departurePlatform, 'string')
assertValidStation(t, leg.destination)
assertValidStationProducts(t, leg.origin.products)
assertValidWhen(t, leg.arrival, when)
t.equal(typeof leg.arrivalPlatform, 'string')
assertValidLine(t, leg.line)
t.ok(Array.isArray(leg.passed))
for (let stopover of leg.passed) assertValidStopover(t, stopover)
}
t.end() t.end()
})) }))
// todo: journeys, only one product
test('journeys  fails with no product', (t) => {
journeysFailsWithNoProduct({
test: t,
fetchJourneys: client.journeys,
fromId: magdeburgHbf,
toId: magdeburgBuckau,
when,
products
})
t.end()
})
test('Magdeburg Hbf to 39104 Magdeburg, Sternstr. 10', co(function*(t) { test('Magdeburg Hbf to 39104 Magdeburg, Sternstr. 10', co(function*(t) {
const magdeburgHbf = '8010224'
const sternStr = { const sternStr = {
type: 'location', type: 'location',
address: 'Magdeburg - Altenstadt, Sternstraße 10',
latitude: 52.118414, latitude: 52.118414,
longitude: 11.422332, longitude: 11.422332
address: 'Magdeburg - Altenstadt, Sternstraße 10'
} }
const journeys = yield client.journeys(magdeburgHbf, sternStr, { const journeys = yield client.journeys(magdeburgHbf, sternStr, {
results: 3,
departure: when departure: when
}) })
t.ok(Array.isArray(journeys)) yield testJourneysStationToAddress({
t.ok(journeys.length >= 1, 'no journeys') test: t,
const journey = journeys[0] journeys,
const firstLeg = journey.legs[0] validate,
const lastLeg = journey.legs[journey.legs.length - 1] fromId: magdeburgHbf,
to: sternStr
assertValidStation(t, firstLeg.origin) })
assertValidStationProducts(t, firstLeg.origin.products)
if (firstLeg.origin.products)
assertValidProducts(t, firstLeg.origin.products)
assertValidWhen(t, firstLeg.departure, when)
assertValidWhen(t, firstLeg.arrival, when)
assertValidWhen(t, lastLeg.departure, when)
assertValidWhen(t, lastLeg.arrival, when)
const d = lastLeg.destination
assertValidAddress(t, d)
t.equal(d.address, 'Magdeburg - Altenstadt, Sternstraße 10')
t.ok(isRoughlyEqual(0.0001, d.latitude, 52.118414))
t.ok(isRoughlyEqual(0.0001, d.longitude, 11.422332))
t.end() t.end()
})) }))
test('Kloster Unser Lieben Frauen to Magdeburg Hbf', co(function*(t) { test('Magdeburg Hbf to Kloster Unser Lieben Frauen', co(function*(t) {
const kloster = { const kloster = {
type: 'location', type: 'location',
latitude: 52.127601, id: '970012223',
longitude: 11.636437,
name: 'Magdeburg, Kloster Unser Lieben Frauen (Denkmal)', name: 'Magdeburg, Kloster Unser Lieben Frauen (Denkmal)',
id: '970012223' latitude: 52.127601,
longitude: 11.636437
} }
const magdeburgHbf = '8010224' const journeys = yield client.journeys(magdeburgHbf, kloster, {
const journeys = yield client.journeys(kloster, magdeburgHbf, { results: 3,
departure: when departure: when
}) })
t.ok(Array.isArray(journeys)) yield testJourneysStationToPoi({
t.ok(journeys.length >= 1, 'no journeys') test: t,
const journey = journeys[0] journeys,
const firstLeg = journey.legs[0] validate,
const lastLeg = journey.legs[journey.legs.length - 1] fromId: magdeburgHbf,
to: kloster
const o = firstLeg.origin })
assertValidPoi(t, o)
t.equal(o.name, 'Magdeburg, Kloster Unser Lieben Frauen (Denkmal)')
t.ok(isRoughlyEqual(0.0001, o.latitude, 52.127601))
t.ok(isRoughlyEqual(0.0001, o.longitude, 11.636437))
assertValidWhen(t, firstLeg.departure, when)
assertValidWhen(t, firstLeg.arrival, when)
assertValidWhen(t, lastLeg.departure, when)
assertValidWhen(t, lastLeg.arrival, when)
assertValidStation(t, lastLeg.destination)
assertValidStationProducts(t, lastLeg.destination.products)
if (lastLeg.destination.products)
assertValidProducts(t, lastLeg.destination.products)
t.end() t.end()
})) }))
test('journeys: via works with detour', co(function* (t) { test('journeys: via works with detour', co(function* (t) {
// Going from Magdeburg, Hasselbachplatz (Sternstr.) (Tram/Bus) to Stendal via Dessau without detour // Going from Magdeburg, Hasselbachplatz (Sternstr.) (Tram/Bus) to Stendal
// is currently impossible. We check if the routing engine computes a detour. // via Dessau without detour is currently impossible. We check if the routing
const hasselbachplatzSternstrasse = '000006545' // engine computes a detour.
const stendal = '008010334' const journeys = yield client.journeys(hasselbachplatzSternstrasse, stendal, {
const dessau = '008010077'
const dessauPassed = '8010077'
const [journey] = yield client.journeys(hasselbachplatzSternstrasse, stendal, {
via: dessau, via: dessau,
results: 1, results: 1,
departure: when, departure: when,
passedStations: true passedStations: true
}) })
t.ok(journey) yield testJourneysWithDetour({
test: t,
const l = journey.legs.some(l => l.passed && l.passed.some(p => p.station.id === dessauPassed)) journeys,
t.ok(l, 'Dessau is not being passed') validate,
detourIds: ['8010077', dessau] // todo: trim IDs
})
t.end() t.end()
})) }))
test('journeys: via works without detour', co(function* (t) { // todo: without detour
// When going from Magdeburg, Hasselbachplatz (Sternstr.) (Tram/Bus) to Magdeburg, Universität via Magdeburg, Breiter Weg, there is *no need*
// to change trains / no need for a "detour".
const hasselbachplatzSternstrasse = '000006545'
const universitaet = '000019686'
const breiterWeg = '000013519'
const breiterWegPassed = '13519'
const [journey] = yield client.journeys(hasselbachplatzSternstrasse, universitaet, { test('earlier/later journeys', co(function* (t) {
via: breiterWeg, yield testEarlierLaterJourneys({
results: 1, test: t,
departure: when, fetchJourneys: client.journeys,
passedStations: true validate,
fromId: magdeburgHbf,
toId: magdeburgBuckau
}) })
t.ok(journey)
const l = journey.legs.some(l => l.passed && l.passed.some(p => p.station.id === breiterWegPassed))
t.ok(l, 'Magdeburg, Breiter Weg is not being passed')
t.end()
}))
test('departures at Magdeburg Hbf', co(function*(t) {
const magdeburgHbf = '8010224'
const deps = yield client.departures(magdeburgHbf, {
duration: 5,
when
})
t.ok(Array.isArray(deps))
for (let dep of deps) {
assertValidStation(t, dep.station)
assertValidStationProducts(t, dep.station.products)
if (dep.station.products) {
assertValidProducts(t, dep.station.products)
}
assertValidWhen(t, dep.when, when)
}
t.end()
}))
test('nearby Magdeburg Hbf', co(function*(t) {
const magdeburgHbfPosition = {
type: 'location',
latitude: 52.130352,
longitude: 11.626891
}
const nearby = yield client.nearby(magdeburgHbfPosition, {
results: 2,
distance: 400
})
t.ok(Array.isArray(nearby))
t.equal(nearby.length, 2)
assertIsMagdeburgHbf(t, nearby[0])
t.ok(nearby[0].distance >= 0)
t.ok(nearby[0].distance <= 100)
for (let n of nearby) {
if (n.type === 'station') assertValidStation(t, n)
else assertValidLocation(t, n)
}
t.end() t.end()
})) }))
test('journey leg details', co(function* (t) { test('journey leg details', co(function* (t) {
const magdeburgHbf = '8010224' const journeys = yield client.journeys(magdeburgHbf, magdeburgBuckau, {
const magdeburgBuckau = '8013456'
const [journey] = yield client.journeys(magdeburgHbf, magdeburgBuckau, {
results: 1, departure: when results: 1, departure: when
}) })
const p = journey.legs[0] const p = journeys[0].legs[0]
t.ok(p, 'missing legs[0]') t.ok(p.id, 'precondition failed')
t.ok(p.id, 'missing legs[0].id') t.ok(p.line.name, 'precondition failed')
t.ok(p.line, 'missing legs[0].line')
t.ok(p.line.name, 'missing legs[0].line.name')
const leg = yield client.journeyLeg(p.id, p.line.name, {when}) const leg = yield client.journeyLeg(p.id, p.line.name, {when})
t.equal(typeof leg.id, 'string') validate(t, leg, 'journeyLeg', 'leg')
t.ok(leg.id)
assertValidLine(t, leg.line)
t.equal(typeof leg.direction, 'string')
t.ok(leg.direction)
t.ok(Array.isArray(leg.passed))
for (let passed of leg.passed) assertValidStopover(t, passed)
t.end() t.end()
})) }))
test('departures at Magdeburg Leiterstr.', co(function*(t) {
const departures = yield client.departures(leiterstr, {
duration: 5, when
})
yield testDepartures({
test: t,
departures,
validate,
id: leiterstr
})
t.end()
}))
test('departures with station object', co(function* (t) {
const deps = yield client.departures({
type: 'station',
id: magdeburgHbf,
name: 'Magdeburg Hbf',
location: {
type: 'location',
latitude: 1.23,
longitude: 2.34
}
}, {when})
validate(t, deps, 'departures', 'departures')
t.end()
}))
// todo: nearby
test('locations named Magdeburg', co(function*(t) { test('locations named Magdeburg', co(function*(t) {
const locations = yield client.locations('Magdeburg', { const locations = yield client.locations('Magdeburg', {
results: 10 results: 20
}) })
t.ok(Array.isArray(locations)) validate(t, locations, 'locations', 'locations')
t.ok(locations.length > 0) t.ok(locations.length <= 20)
t.ok(locations.length <= 10)
for (let l of locations) { t.ok(locations.find(s => s.type === 'station'))
if (l.type === 'station') assertValidStation(t, l) t.ok(locations.find(s => s.id && s.name)) // POIs
else assertValidLocation(t, l) t.ok(locations.some((loc) => {
} return (
t.ok(locations.some(isMagdeburgHbf)) loc.id === '008010224' || // todo: trim IDs
loc.id === magdeburgHbf
)
}))
t.end() t.end()
})) }))
test('station', co(function*(t) { test('station Magdeburg-Buckau', co(function* (t) {
const magdeburgBuckau = '8013456' const s = yield client.station(magdeburgBuckau)
const loc = yield client.station(magdeburgBuckau)
assertValidStation(t, loc) validate(t, s, 'station', 'station')
t.equal(loc.id, magdeburgBuckau) t.equal(s.id, magdeburgBuckau)
t.end() t.end()
})) }))
test('radar', co(function* (t) { test('radar', co(function* (t) {
const north = 52.148364 const vehicles = yield client.radar({
const west = 11.600826 north: 52.148364,
const south = 52.108486 west: 11.600826,
const east = 11.651451 south: 52.108486,
const vehicles = yield client.radar({north, west, south, east}, { east: 11.651451
}, {
duration: 5 * 60, when, results: 10 duration: 5 * 60, when, results: 10
}) })
t.ok(Array.isArray(vehicles)) const customCfg = Object.assign({}, cfg, {
t.ok(vehicles.length > 0) stationCoordsOptional: true, // see #28
for (let v of vehicles) { })
assertValidLine(t, v.line) const validate = createValidate(customCfg, {})
validate(t, vehicles, 'movements', 'vehicles')
t.equal(typeof v.location.latitude, 'number')
t.ok(v.location.latitude <= 57, 'vehicle is too far away')
t.ok(v.location.latitude >= 47, 'vehicle is too far away')
t.equal(typeof v.location.longitude, 'number')
t.ok(v.location.longitude >= 8, 'vehicle is too far away')
t.ok(v.location.longitude <= 14, 'vehicle is too far away')
t.ok(Array.isArray(v.nextStops))
for (let st of v.nextStops) {
assertValidStopover(t, st, true)
if (st.arrival) {
t.equal(typeof st.arrival, 'string')
const arr = +new Date(st.arrival)
// note that this can be an ICE train
t.ok(isRoughlyEqual(14 * hour, +when, arr))
}
if (st.departure) {
t.equal(typeof st.departure, 'string')
const dep = +new Date(st.departure)
// note that this can be an ICE train
t.ok(isRoughlyEqual(14 * hour, +when, dep))
}
}
t.ok(Array.isArray(v.frames))
for (let f of v.frames) {
// see #28
// todo: check if this works by now
assertValidStation(t, f.origin, true)
assertValidStationProducts(t, f.origin.products)
assertValidStation(t, f.destination, true)
assertValidStationProducts(t, f.destination.products)
t.equal(typeof f.t, 'number')
}
}
t.end() t.end()
})) }))

View file

@ -1,3 +1,5 @@
'use strict'
// https://github.com/babel/babel/blob/3c8d831fe41f502cbe2459a271d19c7329ffe369/packages/babel-helpers/src/helpers.js#L242-L270 // https://github.com/babel/babel/blob/3c8d831fe41f502cbe2459a271d19c7329ffe369/packages/babel-helpers/src/helpers.js#L242-L270
const co = (fn) => { const co = (fn) => {
return function run () { return function run () {

21
test/lib/departures.js Normal file
View file

@ -0,0 +1,21 @@
'use strict'
const co = require('./co')
const testDepartures = co(function* (cfg) {
const {test: t, departures: deps, validate, id} = cfg
validate(t, deps, 'departures', 'departures')
t.ok(deps.length > 0, 'must be >0 departures')
for (let i = 0; i < deps.length; i++) {
const dep = deps[i]
const name = `deps[${i}]`
t.equal(dep.station.id, id, name + '.station.id is invalid')
}
// todo: move into deps validator
t.deepEqual(deps, deps.sort((a, b) => t.when > b.when))
})
module.exports = testDepartures

View file

@ -0,0 +1,82 @@
'use strict'
const co = require('./co')
const testEarlierLaterJourneys = co(function* (cfg) {
const {
test: t,
fetchJourneys,
fromId,
toId,
when,
// todo: validate
} = cfg
const model = yield fetchJourneys(fromId, toId, {
results: 3, departure: when
})
// todo: move to journeys validator?
t.equal(typeof model.earlierRef, 'string')
t.ok(model.earlierRef)
t.equal(typeof model.laterRef, 'string')
t.ok(model.laterRef)
// departure/arrival and earlierThan/laterThan should be mutually exclusive
t.throws(() => {
fetchJourneys(fromId, toId, {
departure: when, earlierThan: model.earlierRef
})
// silence rejections, we're only interested in exceptions
.catch(() => {})
})
t.throws(() => {
fetchJourneys(fromId, toId, {
departure: when, laterThan: model.laterRef
})
// silence rejections, we're only interested in exceptions
.catch(() => {})
})
t.throws(() => {
fetchJourneys(fromId, toId, {
arrival: when, earlierThan: model.earlierRef
})
// silence rejections, we're only interested in exceptions
.catch(() => {})
})
t.throws(() => {
fetchJourneys(fromId, toId, {
arrival: when, laterThan: model.laterRef
})
// silence rejections, we're only interested in exceptions
.catch(() => {})
})
let earliestDep = Infinity, latestDep = -Infinity
for (let j of model) {
if (j.legs[0].departure === null) continue
const dep = +new Date(j.legs[0].departure)
if (dep < earliestDep) earliestDep = dep
else if (dep > latestDep) latestDep = dep
}
const earlier = yield fetchJourneys(fromId, toId, {
results: 3,
// todo: single journey ref?
earlierThan: model.earlierRef
})
for (let j of earlier) {
t.ok(new Date(j.legs[0].departure) < earliestDep)
}
const later = yield fetchJourneys(fromId, toId, {
results: 3,
// todo: single journey ref?
laterThan: model.laterRef
})
for (let j of later) {
t.ok(new Date(j.legs[0].departure) > latestDep)
}
})
module.exports = testEarlierLaterJourneys

View file

@ -0,0 +1,23 @@
'use strict'
const journeysFailsWithNoProduct = (cfg) => {
const {
test: t,
fetchJourneys,
fromId,
toId,
when,
products
} = cfg
const productsObj = Object.create(null)
for (let p of products) productsObj[p.id] = false
t.throws(() => {
client.journeys(fromId, toId, {departure: when, products})
// silence rejections, we're only interested in exceptions
.catch(() => {})
})
}
module.exports = journeysFailsWithNoProduct

View file

@ -0,0 +1,29 @@
'use strict'
const isRoughlyEqual = require('is-roughly-equal')
const co = require('./co')
const testJourneysStationToAddress = co(function* (cfg) {
const {test: t, journeys, validate, fromId} = cfg
const {address, latitude, longitude} = cfg.to
validate(t, journeys, 'journeys', 'journeys')
t.strictEqual(journeys.length, 3)
for (let i = 0; i < journeys.length; i++) {
const j = journeys[i]
const firstLeg = j.legs[0]
t.strictEqual(firstLeg.origin.id, fromId)
const d = j.legs[j.legs.length - 1].destination
const n = `journeys[0].legs[${i}].destination`
t.strictEqual(d.type, 'location', n + '.type is invalid')
t.strictEqual(d.address, address, n + '.address is invalid')
t.ok(isRoughlyEqual(.0001, d.latitude, latitude), n + '.latitude is invalid')
t.ok(isRoughlyEqual(.0001, d.longitude, longitude), n + '.longitude is invalid')
}
})
module.exports = testJourneysStationToAddress

View file

@ -0,0 +1,30 @@
'use strict'
const isRoughlyEqual = require('is-roughly-equal')
const co = require('./co')
const testJourneysStationToPoi = co(function* (cfg) {
const {test: t, journeys, validate, fromId} = cfg
const {id, name, latitude, longitude} = cfg.to
validate(t, journeys, 'journeys', 'journeys')
t.strictEqual(journeys.length, 3)
for (let i = 0; i < journeys.length; i++) {
const j = journeys[i]
const firstLeg = j.legs[0]
t.strictEqual(firstLeg.origin.id, fromId)
const d = j.legs[j.legs.length - 1].destination
const n = `journeys[0].legs[${i}].destination`
t.strictEqual(d.type, 'location', n + '.type is invalid')
t.strictEqual(d.id, id, n + '.id is invalid')
t.strictEqual(d.name, name, n + '.name is invalid')
t.ok(isRoughlyEqual(.0001, d.latitude, latitude), n + '.latitude is invalid')
t.ok(isRoughlyEqual(.0001, d.longitude, longitude), n + '.longitude is invalid')
}
})
module.exports = testJourneysStationToPoi

View file

@ -0,0 +1,20 @@
'use strict'
const co = require('./co')
const testJourneysStationToStation = co(function* (cfg) {
const {test: t, journeys, validate, fromId, toId} = cfg
validate(t, journeys, 'journeys', 'journeys')
t.strictEqual(journeys.length, 3)
for (let i = 0; i < journeys.length; i++) {
const j = journeys[i]
const firstLeg = j.legs[0]
const lastLeg = j.legs[j.legs.length - 1]
t.strictEqual(firstLeg.origin.id, fromId)
t.strictEqual(lastLeg.destination.id, toId)
}
})
module.exports = testJourneysStationToStation

View file

@ -0,0 +1,21 @@
'use strict'
const co = require('./co')
const testJourneysWithDetour = co(function* (cfg) {
const {test: t, journeys, validate, detourIds} = cfg
// We assume that going from A to B via C *without* detour is currently
// impossible. We check if the routing engine computes a detour.
validate(t, journeys, 'journeys', 'journeys')
const leg = journeys[0].legs.some((leg) => {
return leg.passed && leg.passed.some((passed) => {
return detourIds.includes(passed.station.id)
})
})
t.ok(leg, detourIds.join('/') + ' is not being passed')
})
module.exports = testJourneysWithDetour

28
test/lib/util.js Normal file
View file

@ -0,0 +1,28 @@
'use strict'
const isRoughlyEqual = require('is-roughly-equal')
const {DateTime} = require('luxon')
const a = require('assert')
const hour = 60 * 60 * 1000
const day = 24 * hour
const week = 7 * day
// next Monday 10 am
const createWhen = (timezone, locale) => {
return DateTime.fromMillis(Date.now(), {
zone: timezone,
locale,
}).startOf('week').plus({weeks: 1, hours: 10}).toJSDate()
}
const assertValidWhen = (actual, expected, name) => {
const ts = +new Date(actual)
a.ok(!Number.isNaN(ts), name + ' is not parsable by Date')
// the timestamps might be from long-distance trains
a.ok(isRoughlyEqual(day, +expected, ts), name + ' is out of range')
}
module.exports = {
hour, createWhen, assertValidWhen
}

View file

@ -0,0 +1,30 @@
'use strict'
const {defaultValidators} = require('validate-fptf')
const anyOf = require('validate-fptf/lib/any-of')
const validators = require('./validators')
const create = (cfg, customValidators = {}) => {
const val = Object.assign({}, defaultValidators)
for (let key of Object.keys(validators)) {
val[key] = validators[key](cfg)
}
Object.assign(val, customValidators)
const validateFptfWith = (t, item, allowedTypes, name) => {
try {
if ('string' === typeof allowedTypes) {
val[allowedTypes](val, item, name)
} else {
anyOf(allowedTypes, val, item, name)
}
t.pass(name + ' is valid')
} catch (err) {
t.ifError(err) // todo: improve error logging
}
}
return validateFptfWith
}
module.exports = create

306
test/lib/validators.js Normal file
View file

@ -0,0 +1,306 @@
'use strict'
const a = require('assert')
const {defaultValidators} = require('validate-fptf')
const anyOf = require('validate-fptf/lib/any-of')
const {assertValidWhen} = require('./util')
const isObj = o => o !== null && 'object' === typeof o && !Array.isArray(o)
const is = val => val !== null && val !== undefined
const createValidateStation = (cfg) => {
const validateStation = (val, s, name = 'station') => {
defaultValidators.station(val, s, name)
if (!cfg.stationCoordsOptional) {
a.ok(is(s.location), `missing ${name}.location`)
}
a.ok(isObj(s.products), name + '.products must be an object')
for (let product of cfg.products) {
const msg = name + `.products[${product.id}] must be a boolean`
a.strictEqual(typeof s.products[product.id], 'boolean', msg)
}
if ('lines' in s) {
a.ok(Array.isArray(s.lines), name + `.lines must be an array`)
for (let i = 0; i < s.lines.length; i++) {
val.line(val, s.lines[i], name + `.lines[${i}]`)
}
}
}
return validateStation
}
const validatePoi = (val, poi, name = 'location') => {
defaultValidators.location(val, poi, name)
val.ref(val, poi.id, name + '.id')
a.ok(poi.name, name + '.name must not be empty')
}
const validateAddress = (val, addr, name = 'location') => {
defaultValidators.location(val, addr, name)
a.strictEqual(typeof addr.address, 'string', name + '.address must be a string')
a.ok(addr.address, name + '.address must not be empty')
}
const validateLocation = (val, loc, name = 'location') => {
a.ok(isObj(loc), name + ' must be an object')
if (loc.type === 'station') val.station(val, loc, name)
else if ('id' in loc) validatePoi(val, loc, name)
else if (!('name' in loc) && ('address' in loc)) {
validateAddress(val, loc, name)
} else defaultValidators.location(val, loc, name)
}
const validateLocations = (val, locs, name = 'locations') => {
a.ok(Array.isArray(locs), name + ' must be an array')
a.ok(locs.length > 0, name + ' must not be empty')
for (let i = 0; i < locs.length; i++) {
val.location(val, locs[i], name + `[${i}]`)
}
}
const createValidateLine = (cfg) => {
const validLineModes = []
for (let product of cfg.products) {
if (!validLineModes.includes(product.mode)) {
validLineModes.push(product.mode)
}
}
const validateLine = (val, line, name = 'line') => {
defaultValidators.line(val, line, name)
a.ok(validLineModes.includes(line.mode), name + '.mode is invalid')
}
return validateLine
}
const createValidateStopover = (cfg) => {
const validateStopover = (val, s, name = 'stopover') => {
if (is(s.arrival)) {
val.date(val, s.arrival, name + '.arrival')
assertValidWhen(s.arrival, cfg.when, name)
}
if (is(s.departure)) {
val.date(val, s.departure, name + '.departure')
assertValidWhen(s.departure, cfg.when, name)
}
if (!is(s.arrival) && !is(s.departure)) {
a.fail(name + ' contains neither arrival nor departure')
}
if (is(s.arrivalDelay)) {
const msg = name + '.arrivalDelay must be a number'
a.strictEqual(typeof s.arrivalDelay, 'number', msg)
}
if (is(s.departureDelay)) {
const msg = name + '.departureDelay must be a number'
a.strictEqual(typeof s.departureDelay, 'number', msg)
}
val.station(val, s.station, name + '.station')
}
return validateStopover
}
const validateTicket = (val, ti, name = 'ticket') => {
a.strictEqual(typeof ti.name, 'string', name + '.name must be a string')
a.ok(ti.name, name + '.name must not be empty')
if (is(ti.price)) {
a.strictEqual(typeof ti.price, 'number', name + '.price must be a number')
a.ok(ti.price > 0, name + '.price must be >0')
}
if (is(ti.amount)) {
a.strictEqual(typeof ti.amount, 'number', name + '.amount must be a number')
a.ok(ti.amount > 0, name + '.amount must be >0')
}
// todo: move to VBB tests
if ('bike' in ti) {
a.strictEqual(typeof ti.bike, 'boolean', name + '.bike must be a boolean')
}
if ('shortTrip' in ti) {
a.strictEqual(typeof ti.shortTrip, 'boolean', name + '.shortTrip must be a boolean')
}
if ('group' in ti) {
a.strictEqual(typeof ti.group, 'boolean', name + '.group must be a boolean')
}
if ('fullDay' in ti) {
a.strictEqual(typeof ti.fullDay, 'boolean', name + '.fullDay must be a boolean')
}
if ('tariff' in ti) {
a.strictEqual(typeof ti.tariff, 'string', name + '.tariff must be a string')
a.ok(ti.tariff, name + '.tariff must not be empty')
}
if ('coverage' in ti) {
a.strictEqual(typeof ti.coverage, 'string', name + '.coverage must be a string')
a.ok(ti.coverage, name + '.coverage must not be empty')
}
if ('variant' in ti) {
a.strictEqual(typeof ti.variant, 'string', name + '.variant must be a string')
a.ok(ti.variant, name + '.variant must not be empty')
}
}
const createValidateJourneyLeg = (cfg) => {
const validateJourneyLeg = (val, leg, name = 'journeyLeg') => {
const withFakeScheduleAndOperator = Object.assign({
schedule: 'foo', // todo: let hafas-client parse a schedule ID
operator: 'bar' // todo: let hafas-client parse the operator
}, leg)
defaultValidators.journeyLeg(val, withFakeScheduleAndOperator, name)
if (leg.arrival !== null) {
assertValidWhen(leg.arrival, cfg.when, name + '.arrival')
}
if (leg.departure !== null) {
assertValidWhen(leg.departure, cfg.when, name + '.departure')
}
// todo: leg.arrivalPlatform !== null
if (is(leg.arrivalPlatform)) {
const msg = name + '.arrivalPlatform must be a string'
a.strictEqual(typeof leg.arrivalPlatform, 'string', msg)
a.ok(leg.arrivalPlatform, name + '.arrivalPlatform must not be empty')
}
// todo: leg.departurePlatform !== null
if (is(leg.departurePlatform)) {
const msg = name + '.departurePlatform must be a string'
a.strictEqual(typeof leg.departurePlatform, 'string', msg)
a.ok(leg.departurePlatform, name + '.departurePlatform must not be empty')
}
if ('passed' in leg) {
a.ok(Array.isArray(leg.passed), name + '.passed must be an array')
a.ok(leg.passed.length > 0, name + '.passed must not be empty')
for (let i = 0; i < leg.passed.length; i++) {
val.stopover(val, leg.passed[i], name + `.passed[${i}]`)
}
}
if (leg.mode !== 'walking') {
const msg = name + '.direction must be a string'
a.strictEqual(typeof leg.direction, 'string', msg)
a.ok(leg.direction, name + '.direction must not be empty')
}
}
return validateJourneyLeg
}
const validateJourney = (val, j, name = 'journey') => {
const withFakeId = Object.assign({
id: 'foo' // todo: let hafas-client parse a journey ID
}, j)
defaultValidators.journey(val, withFakeId, name)
if ('tickets' in j) {
a.ok(Array.isArray(j.tickets), name + '.tickets must be an array')
a.ok(j.tickets.length > 0, name + '.tickets must not be empty')
for (let i = 0; i < j.tickets.length; i++) {
val.ticket(val, j.tickets[i], name + `.tickets[${i}]`)
}
}
}
const validateJourneys = (val, js, name = 'journeys') => {
a.ok(Array.isArray(js), name + ' must be an array')
a.ok(js.length > 0, name + ' must not be empty')
for (let i = 0; i < js.length; i++) {
val.journey(val, js[i], name + `[${i}]`)
}
}
const createValidateDeparture = (cfg) => {
const validateDeparture = (val, dep, name = 'departure') => {
a.ok(isObj(dep), name + ' must be an object')
// todo: let hafas-client add a .type field
a.strictEqual(typeof dep.journeyId, 'string', name + '.journeyId must be a string')
a.ok(dep.journeyId, name + '.journeyId must not be empty')
a.strictEqual(typeof dep.trip, 'number', name + '.trip must be a number')
val.station(val, dep.station, name + '.station')
assertValidWhen(dep.when, cfg.when, name)
if (dep.delay !== null) {
const msg = name + '.delay must be a number'
a.strictEqual(typeof dep.delay, 'number', msg)
}
val.line(val, dep.line, name + '.line')
a.strictEqual(typeof dep.direction, 'string', name + '.direction must be a string')
a.ok(dep.direction, name + '.direction must not be empty')
}
return validateDeparture
}
const validateDepartures = (val, deps, name = 'departures') => {
a.ok(Array.isArray(deps), name + ' must be an array')
a.ok(deps.length > 0, name + ' must not be empty')
for (let i = 0; i < deps.length; i++) {
val.departure(val, deps[i], name + `[${i}]`)
}
}
const validateMovement = (val, m, name = 'movement') => {
a.ok(isObj(m), name + ' must be an object')
// todo: let hafas-client add a .type field
val.line(val, m.line, name + '.line')
a.strictEqual(typeof m.direction, 'string', name + '.direction must be a string')
a.ok(m.direction, name + '.direction must not be empty')
const lName = name + '.location'
val.location(val, m.location, lName)
a.ok(m.location.latitude <= 55, lName + '.latitude is too small')
a.ok(m.location.latitude >= 45, lName + '.latitude is too large')
a.ok(m.location.longitude >= 9, lName + '.longitude is too small')
a.ok(m.location.longitude <= 15, lName + '.longitude is too small')
a.ok(Array.isArray(m.nextStops), name + '.nextStops must be an array')
for (let i = 0; i < m.nextStops.length; i++) {
const st = m.nextStops[i]
val.stopover(val, m.nextStops[i], name + `.nextStops[${i}]`)
}
a.ok(Array.isArray(m.frames), name + '.frames must be an array')
a.ok(m.frames.length > 0, name + '.frames must not be empty')
for (let i = 0; i < m.frames.length; i++) {
const f = m.frames[i]
const fName = name + `.frames[${i}]`
a.ok(isObj(f), fName + ' must be an object')
anyOf(['location', 'station'], val, f.origin, fName + '.origin')
anyOf(['location', 'station'], val, f.destination, fName + '.destination')
a.strictEqual(typeof f.t, 'number', fName + '.frames must be a number')
}
}
const validateMovements = (val, ms, name = 'movements') => {
a.ok(Array.isArray(ms), name + ' must be an array')
a.ok(ms.length > 0, name + ' must not be empty')
for (let i = 0; i < ms.length; i++) {
val.movement(val, ms[i], name + `[${i}]`)
}
}
module.exports = {
station: createValidateStation,
location: () => validateLocation,
locations: () => validateLocations,
poi: () => validatePoi,
address: () => validateAddress,
line: createValidateLine,
stopover: createValidateStopover,
ticket: () => validateTicket,
journeyLeg: createValidateJourneyLeg,
journey: () => validateJourney,
journeys: () => validateJourneys,
departure: createValidateDeparture,
departures: () => validateDepartures,
movement: () => validateMovement,
movements: () => validateMovements
}

View file

@ -1,69 +1,49 @@
'use strict' 'use strict'
// todo
// const getStations = require('db-stations').full
const tapePromise = require('tape-promise').default const tapePromise = require('tape-promise').default
const tape = require('tape') const tape = require('tape')
const isRoughlyEqual = require('is-roughly-equal') const isRoughlyEqual = require('is-roughly-equal')
const validateFptf = require('validate-fptf')
const validateLineWithoutMode = require('./validate-line-without-mode') const {createWhen} = require('./lib/util')
const co = require('./lib/co')
const co = require('./co')
const createClient = require('..') const createClient = require('..')
const nahshProfile = require('../p/nahsh') const nahshProfile = require('../p/nahsh')
const allProducts = require('../p/nahsh/products') const products = require('../p/nahsh/products')
const { const {
assertValidStation, line: createValidateLine,
assertValidPoi, station: createValidateStation
assertValidAddress, } = require('./lib/validators')
assertValidLocation, const createValidate = require('./lib/validate-fptf-with')
assertValidStopover, const testJourneysStationToStation = require('./lib/journeys-station-to-station')
hour, createWhen, assertValidWhen const testJourneysStationToAddress = require('./lib/journeys-station-to-address')
} = require('./util.js') const testJourneysStationToPoi = require('./lib/journeys-station-to-poi')
const testEarlierLaterJourneys = require('./lib/earlier-later-journeys')
const journeysFailsWithNoProduct = require('./lib/journeys-fails-with-no-product')
const testDepartures = require('./lib/departures')
const when = createWhen('Europe/Berlin', 'de-DE') const when = createWhen('Europe/Berlin', 'de-DE')
const assertValidStationProducts = (t, p) => { const cfg = {
t.ok(p) when,
t.equal(typeof p.nationalExp, 'boolean') stationCoordsOptional: false,
t.equal(typeof p.national, 'boolean') products
t.equal(typeof p.interregional, 'boolean')
t.equal(typeof p.regional, 'boolean')
t.equal(typeof p.suburban, 'boolean')
t.equal(typeof p.bus, 'boolean')
t.equal(typeof p.ferry, 'boolean')
t.equal(typeof p.subway, 'boolean')
t.equal(typeof p.tram, 'boolean')
t.equal(typeof p.onCall, 'boolean')
} }
const isKielHbf = (s) => { const _validateLine = createValidateLine(cfg)
return s.type === 'station' && const validateLine = (validate, l, name) => {
(s.id === '8000199') && if (l && l.product === 'onCall') {
s.name === 'Kiel Hbf' && // skip line validation
s.location && // https://github.com/derhuerst/hafas-client/issues/8#issuecomment-355839965
isRoughlyEqual(.0005, s.location.latitude, 54.314982) && l = Object.assign({}, l)
isRoughlyEqual(.0005, s.location.longitude, 10.131976) l.mode = 'taxi'
}
const assertIsKielHbf = (t, s) => {
t.equal(s.type, 'station')
t.ok(s.id === '8000199', 'id should be 8000199')
t.equal(s.name, 'Kiel Hbf')
t.ok(s.location)
t.ok(isRoughlyEqual(.0005, s.location.latitude, 54.314982))
t.ok(isRoughlyEqual(.0005, s.location.longitude, 10.131976))
}
// todo: DRY with assertValidStationProducts
// todo: DRY with other tests
const assertValidProducts = (t, products) => {
for (let p of allProducts) {
t.equal(typeof products[p.id], 'boolean', `product ${p.id} must be a boolean`)
} }
_validateLine(validate, l, name)
} }
const validate = createValidate(cfg, {
line: validateLine
})
const assertValidPrice = (t, p) => { const assertValidPrice = (t, p) => {
t.ok(p) t.ok(p)
if (p.amount !== null) { if (p.amount !== null) {
@ -76,18 +56,6 @@ const assertValidPrice = (t, p) => {
} }
} }
const assertValidLine = (t, l) => { // with optional mode
const validators = Object.assign({}, validateFptf.defaultValidators, {
line: validateLineWithoutMode
})
const recurse = validateFptf.createRecurse(validators)
try {
recurse(['line'], l, 'line')
} catch (err) {
t.ifError(err)
}
}
const test = tapePromise(tape) const test = tapePromise(tape)
const client = createClient(nahshProfile) const client = createClient(nahshProfile)
@ -97,192 +65,125 @@ const luebeckHbf = '8000237'
const husum = '8000181' const husum = '8000181'
const schleswig = '8005362' const schleswig = '8005362'
test('Kiel Hbf to Flensburg', co(function* (t) { test('journeys  Kiel Hbf to Flensburg', co(function* (t) {
const journeys = yield client.journeys(kielHbf, flensburg, { const journeys = yield client.journeys(kielHbf, flensburg, {
departure: when, passedStations: true results: 3,
departure: when,
passedStations: true
}) })
t.ok(Array.isArray(journeys)) yield testJourneysStationToStation({
t.ok(journeys.length > 0, 'no journeys') test: t,
for (let journey of journeys) { journeys,
t.equal(journey.type, 'journey') validate,
fromId: kielHbf,
toId: flensburg
})
t.ok(Array.isArray(journey.legs)) for (let i = 0; i < journeys.length; i++) {
t.ok(journey.legs.length > 0, 'no legs') const j = journeys[i]
const leg = journey.legs[0] // todo: all legs // todo: find a journey where there pricing info is always available
if (j.price) assertValidPrice(t, j.price, `journeys[${i}].price`)
assertValidStation(t, leg.origin)
assertValidStationProducts(t, leg.origin.products)
// todo
// if (!(yield findStation(leg.origin.id))) {
// console.error('unknown station', leg.origin.id, leg.origin.name)
// }
assertValidWhen(t, leg.departure, when)
t.equal(typeof leg.departurePlatform, 'string')
assertValidStation(t, leg.destination)
assertValidStationProducts(t, leg.origin.products)
// todo
// if (!(yield findStation(leg.destination.id))) {
// console.error('unknown station', leg.destination.id, leg.destination.name)
// }
assertValidWhen(t, leg.arrival, when)
t.equal(typeof leg.arrivalPlatform, 'string')
assertValidLine(t, leg.line)
t.ok(Array.isArray(leg.passed))
for (let stopover of leg.passed) assertValidStopover(t, stopover)
if (journey.price) assertValidPrice(t, journey.price)
} }
t.end() t.end()
})) }))
// todo: journeys, only one product
test('journeys  fails with no product', (t) => {
journeysFailsWithNoProduct({
test: t,
fetchJourneys: client.journeys,
fromId: kielHbf,
toId: flensburg,
when,
products
})
t.end()
})
test('Kiel Hbf to Husum, Zingel 10', co(function* (t) { test('Kiel Hbf to Husum, Zingel 10', co(function* (t) {
const zingel = { const zingel = {
type: 'location', type: 'location',
address: 'Husum, Zingel 10',
latitude: 54.475359, latitude: 54.475359,
longitude: 9.050798, longitude: 9.050798
address: 'Husum, Zingel 10'
} }
const journeys = yield client.journeys(kielHbf, zingel, {
const journeys = yield client.journeys(kielHbf, zingel, {departure: when}) results: 3,
t.ok(Array.isArray(journeys))
t.ok(journeys.length >= 1, 'no journeys')
const journey = journeys[0]
const firstLeg = journey.legs[0]
const lastLeg = journey.legs[journey.legs.length - 1]
assertValidStation(t, firstLeg.origin)
assertValidStationProducts(t, firstLeg.origin.products)
// todo
// if (!(yield findStation(leg.origin.id))) {
// console.error('unknown station', leg.origin.id, leg.origin.name)
// }
if (firstLeg.origin.products) assertValidProducts(t, firstLeg.origin.products)
assertValidWhen(t, firstLeg.departure, when)
assertValidWhen(t, firstLeg.arrival, when)
assertValidWhen(t, lastLeg.departure, when)
assertValidWhen(t, lastLeg.arrival, when)
const d = lastLeg.destination
assertValidAddress(t, d)
t.equal(d.address, 'Husum, Zingel 10')
t.ok(isRoughlyEqual(.0001, d.latitude, 54.475359))
t.ok(isRoughlyEqual(.0001, d.longitude, 9.050798))
t.end()
}))
test('Holstentor to Kiel Hbf', co(function* (t) {
const holstentor = {
type: 'location',
latitude: 53.866321,
longitude: 10.679976,
name: 'Hansestadt Lübeck, Holstentor (Denkmal)',
id: '970003547'
}
const journeys = yield client.journeys(holstentor, kielHbf, {departure: when})
t.ok(Array.isArray(journeys))
t.ok(journeys.length >= 1, 'no journeys')
const journey = journeys[0]
const firstLeg = journey.legs[0]
const lastLeg = journey.legs[journey.legs.length - 1]
const o = firstLeg.origin
assertValidPoi(t, o)
t.equal(o.name, 'Hansestadt Lübeck, Holstentor (Denkmal)')
t.ok(isRoughlyEqual(.0001, o.latitude, 53.866321))
t.ok(isRoughlyEqual(.0001, o.longitude, 10.679976))
assertValidWhen(t, firstLeg.departure, when)
assertValidWhen(t, firstLeg.arrival, when)
assertValidWhen(t, lastLeg.departure, when)
assertValidWhen(t, lastLeg.arrival, when)
assertValidStation(t, lastLeg.destination)
assertValidStationProducts(t, lastLeg.destination.products)
if (lastLeg.destination.products) assertValidProducts(t, lastLeg.destination.products)
// todo
// if (!(yield findStation(leg.destination.id))) {
// console.error('unknown station', leg.destination.id, leg.destination.name)
// }
t.end()
}))
test('Husum to Lübeck Hbf with stopover at Husum', co(function* (t) {
const [journey] = yield client.journeys(husum, luebeckHbf, {
via: kielHbf,
results: 1,
departure: when departure: when
}) })
const i1 = journey.legs.findIndex(leg => leg.destination.id === kielHbf) yield testJourneysStationToAddress({
t.ok(i1 >= 0, 'no leg with Kiel Hbf as destination') test: t,
journeys,
validate,
fromId: kielHbf,
to: zingel
})
t.end()
}))
const i2 = journey.legs.findIndex(leg => leg.origin.id === kielHbf) test('Kiel Hbf to Holstentor', co(function* (t) {
t.ok(i2 >= 0, 'no leg with Kiel Hbf as origin') const holstentor = {
t.ok(i2 > i1, 'leg with Kiel Hbf as origin must be after leg to it') type: 'location',
id: '970003547',
name: 'Hansestadt Lübeck, Holstentor (Denkmal)',
latitude: 53.866321,
longitude: 10.679976
}
const journeys = yield client.journeys(kielHbf, holstentor, {
results: 3,
departure: when
})
yield testJourneysStationToPoi({
test: t,
journeys,
validate,
fromId: kielHbf,
to: holstentor
})
t.end()
}))
test('Husum to Lübeck Hbf with stopover at Kiel Hbf', co(function* (t) {
const journeys = yield client.journeys(husum, luebeckHbf, {
via: kielHbf,
results: 1,
departure: when,
passedStations: true
})
validate(t, journeys, 'journeys', 'journeys')
const leg = journeys[0].legs.some((leg) => {
return leg.passed && leg.passed.some((passed) => {
return passed.station.id === kielHbf
})
})
t.ok(leg, 'Kiel Hbf is not being passed')
t.end() t.end()
})) }))
test('earlier/later journeys, Kiel Hbf -> Flensburg', co(function* (t) { test('earlier/later journeys, Kiel Hbf -> Flensburg', co(function* (t) {
const model = yield client.journeys(kielHbf, flensburg, { yield testEarlierLaterJourneys({
results: 3, departure: when test: t,
fetchJourneys: client.journeys,
validate,
fromId: kielHbf,
toId: flensburg
}) })
t.equal(typeof model.earlierRef, 'string')
t.ok(model.earlierRef)
t.equal(typeof model.laterRef, 'string')
t.ok(model.laterRef)
// when and earlierThan/laterThan should be mutually exclusive
t.throws(() => {
client.journeys(kielHbf, flensburg, {
departure: when, earlierThan: model.earlierRef
})
})
t.throws(() => {
client.journeys(kielHbf, flensburg, {
departure: when, laterThan: model.laterRef
})
})
let earliestDep = Infinity, latestDep = -Infinity
for (let j of model) {
const dep = +new Date(j.legs[0].departure)
if (dep < earliestDep) earliestDep = dep
else if (dep > latestDep) latestDep = dep
}
const earlier = yield client.journeys(kielHbf, flensburg, {
results: 3,
// todo: single journey ref?
earlierThan: model.earlierRef
})
for (let j of earlier) {
t.ok(new Date(j.legs[0].departure) < earliestDep)
}
const later = yield client.journeys(kielHbf, flensburg, {
results: 3,
// todo: single journey ref?
laterThan: model.laterRef
})
for (let j of later) {
t.ok(new Date(j.legs[0].departure) > latestDep)
}
t.end() t.end()
})) }))
test('leg details for Flensburg to Husum', co(function* (t) { // todo: with detour test
// todo: without detour test
test('journey leg details for Flensburg to Husum', co(function* (t) {
const journeys = yield client.journeys(flensburg, husum, { const journeys = yield client.journeys(flensburg, husum, {
results: 1, departure: when results: 1, departure: when
}) })
@ -292,37 +193,39 @@ test('leg details for Flensburg to Husum', co(function* (t) {
t.ok(p.line.name, 'precondition failed') t.ok(p.line.name, 'precondition failed')
const leg = yield client.journeyLeg(p.id, p.line.name, {when}) const leg = yield client.journeyLeg(p.id, p.line.name, {when})
t.equal(typeof leg.id, 'string') validate(t, leg, 'journeyLeg', 'leg')
t.ok(leg.id)
assertValidLine(t, leg.line)
t.equal(typeof leg.direction, 'string')
t.ok(leg.direction)
t.ok(Array.isArray(leg.passed))
for (let passed of leg.passed) assertValidStopover(t, passed)
t.end() t.end()
})) }))
test('departures at Kiel Hbf', co(function* (t) { test('departures at Kiel Räucherei', co(function* (t) {
const deps = yield client.departures(kielHbf, { const kielRaeucherei = '3440091'
const departures = yield client.departures(kielRaeucherei, {
duration: 30, when duration: 30, when
}) })
t.ok(Array.isArray(deps)) yield testDepartures({
for (let dep of deps) { test: t,
assertValidStation(t, dep.station) departures,
assertValidStationProducts(t, dep.station.products) validate,
// todo id: kielRaeucherei
// if (!(yield findStation(dep.station.id))) { })
// console.error('unknown station', dep.station.id, dep.station.name) t.end()
// } }))
if (dep.station.products) assertValidProducts(t, dep.station.products)
assertValidWhen(t, dep.when, when)
}
test('departures with station object', co(function* (t) {
const deps = yield client.departures({
type: 'station',
id: kielHbf,
name: 'Kiel Hbf',
location: {
type: 'location',
latitude: 1.23,
longitude: 2.34
}
}, {when})
validate(t, deps, 'departures', 'departures')
t.end() t.end()
})) }))
@ -336,77 +239,44 @@ test('nearby Kiel Hbf', co(function* (t) {
results: 2, distance: 400 results: 2, distance: 400
}) })
validate(t, nearby, 'locations', 'nearby')
t.ok(Array.isArray(nearby)) t.ok(Array.isArray(nearby))
t.equal(nearby.length, 2) t.equal(nearby.length, 2)
assertIsKielHbf(t, nearby[0]) t.equal(nearby[0].id, kielHbf)
t.equal(nearby[0].name, 'Kiel Hbf')
t.ok(nearby[0].distance >= 0) t.ok(nearby[0].distance >= 0)
t.ok(nearby[0].distance <= 100) t.ok(nearby[0].distance <= 100)
for (let n of nearby) {
if (n.type === 'station') assertValidStation(t, n)
else assertValidLocation(t, n)
}
t.end() t.end()
})) }))
test('locations named Kiel', co(function* (t) { test('locations named Kiel', co(function* (t) {
const locations = yield client.locations('Kiel', { const locations = yield client.locations('Kiel', {
results: 10 results: 20
}) })
t.ok(Array.isArray(locations)) validate(t, locations, 'locations', 'locations')
t.ok(locations.length > 0) t.ok(locations.length <= 20)
t.ok(locations.length <= 10)
for (let l of locations) { t.ok(locations.find(s => s.type === 'station'))
if (l.type === 'station') assertValidStation(t, l) t.ok(locations.find(s => s.id && s.name)) // POIs
else assertValidLocation(t, l) t.ok(locations.some(l => l.id === kielHbf))
}
t.ok(locations.some(isKielHbf))
t.end() t.end()
})) }))
test('station', co(function* (t) { test('station', co(function* (t) {
const loc = yield client.station(schleswig) const s = yield client.station(kielHbf)
assertValidStation(t, loc) validate(t, s, 'station', 'station')
t.equal(loc.id, schleswig) t.equal(s.id, kielHbf)
t.end() t.end()
})) }))
test('radar Kiel', co(function* (t) { test('radar', co(function* (t) {
const fakeStation = (s) => {
const fake = Object.assign({
products: {
nationalExp: true,
national: false,
interregional: true,
regional: false,
suburban: true,
bus: false,
ferry: true,
subway: false,
tram: true,
onCall: false
}
}, s)
if (s.name === null) fake.name = 'foo'
return fake
}
const _assertValidStation = (t, s, coordsOptional = false) => {
assertValidStation(t, fakeStation(s), coordsOptional)
}
const _assertValidStopover = (t, s, coordsOptional = false) => {
const fake = Object.assign({}, s, {
station: fakeStation(s.station)
})
assertValidStopover(t, fake, coordsOptional)
}
const vehicles = yield client.radar({ const vehicles = yield client.radar({
north: 54.4, north: 54.4,
west: 10.0, west: 10.0,
@ -416,51 +286,19 @@ test('radar Kiel', co(function* (t) {
duration: 5 * 60, when duration: 5 * 60, when
}) })
t.ok(Array.isArray(vehicles)) // todo: cfg.stationProductsOptional option
t.ok(vehicles.length > 0) const allProducts = products.reduce((acc, p) => (acc[p.id] = true, acc), {})
for (let v of vehicles) { const validateStation = createValidateStation(cfg)
const validate = createValidate(cfg, {
// todo station: (validate, s, name) => {
// t.ok(findStation(v.direction)) s = Object.assign({
assertValidLine(t, v.line) products: allProducts // todo: fix station.products
}, s)
t.equal(typeof v.location.latitude, 'number') if (!s.name) s.name = 'foo' // todo, see #34
t.ok(v.location.latitude <= 57, 'vehicle is too far away') validateStation(validate, s, name)
t.ok(v.location.latitude >= 51, 'vehicle is too far away')
t.equal(typeof v.location.longitude, 'number')
t.ok(v.location.longitude >= 7, 'vehicle is too far away')
t.ok(v.location.longitude <= 13, 'vehicle is too far away')
t.ok(Array.isArray(v.nextStops))
for (let st of v.nextStops) {
_assertValidStopover(t, st, true)
if (st.arrival) {
t.equal(typeof st.arrival, 'string')
const arr = +new Date(st.arrival)
// note that this can be an ICE train
t.ok(isRoughlyEqual(14 * hour, +when, arr))
}
if (st.departure) {
t.equal(typeof st.departure, 'string')
const dep = +new Date(st.departure)
t.ok(isRoughlyEqual(14 * hour, +when, dep))
}
} }
})
validate(t, vehicles, 'movements', 'vehicles')
t.ok(Array.isArray(v.frames))
for (let f of v.frames) {
// todo: see #34
_assertValidStation(t, f.origin, true)
if (f.origin.products) {
assertValidStationProducts(t, f.origin.products)
}
_assertValidStation(t, f.destination, true)
if (f.destination.products) {
assertValidStationProducts(t, f.destination.products)
}
t.equal(typeof f.t, 'number')
}
}
t.end() t.end()
})) }))

View file

@ -1,86 +1,39 @@
'use strict' 'use strict'
// todo
// const getStations = require('db-stations').full
const tapePromise = require('tape-promise').default const tapePromise = require('tape-promise').default
const tape = require('tape') const tape = require('tape')
const isRoughlyEqual = require('is-roughly-equal') const isRoughlyEqual = require('is-roughly-equal')
const validateFptf = require('validate-fptf') const validateLine = require('validate-fptf/line')
const validateLineWithoutMode = require('./validate-line-without-mode') const {createWhen} = require('./lib/util')
const co = require('./lib/co')
const co = require('./co')
const createClient = require('..') const createClient = require('..')
const oebbProfile = require('../p/oebb') const oebbProfile = require('../p/oebb')
const allProducts = require('../p/oebb/products') const products = require('../p/oebb/products')
const { const {
assertValidStation, station: createValidateStation
assertValidPoi, } = require('./lib/validators')
assertValidAddress, const createValidate = require('./lib/validate-fptf-with')
assertValidLocation, const testJourneysStationToStation = require('./lib/journeys-station-to-station')
assertValidStopover, const testJourneysStationToAddress = require('./lib/journeys-station-to-address')
hour, createWhen, assertValidWhen const testJourneysStationToPoi = require('./lib/journeys-station-to-poi')
} = require('./util.js') const testEarlierLaterJourneys = require('./lib/earlier-later-journeys')
const journeysFailsWithNoProduct = require('./lib/journeys-fails-with-no-product')
const testJourneysWithDetour = require('./lib/journeys-with-detour')
const when = createWhen('Europe/Vienna', 'de-AT') const when = createWhen('Europe/Vienna', 'de-AT')
const assertValidStationProducts = (t, p) => { const cfg = {
t.ok(p) when,
t.equal(typeof p.nationalExp, 'boolean') stationCoordsOptional: false,
t.equal(typeof p.national, 'boolean') products
t.equal(typeof p.interregional, 'boolean')
t.equal(typeof p.regional, 'boolean')
t.equal(typeof p.suburban, 'boolean')
t.equal(typeof p.bus, 'boolean')
t.equal(typeof p.ferry, 'boolean')
t.equal(typeof p.subway, 'boolean')
t.equal(typeof p.tram, 'boolean')
t.equal(typeof p.onCall, 'boolean')
} }
// todo // todo validateDirection: search list of stations for direction
// const findStation = (id) => new Promise((yay, nay) => {
// const stations = getStations()
// stations
// .once('error', nay)
// .on('data', (s) => {
// if (
// s.id === id ||
// (s.additionalIds && s.additionalIds.includes(id))
// ) {
// yay(s)
// stations.destroy()
// }
// })
// .once('end', yay)
// })
const isSalzburgHbf = (s) => { const validate = createValidate(cfg, {
return s.type === 'station' && line: validateLine // bypass line validator in lib/validators
(s.id === '008100002' || s.id === '8100002') && })
s.name === 'Salzburg Hbf' &&
s.location &&
isRoughlyEqual(.0005, s.location.latitude, 47.812851) &&
isRoughlyEqual(.0005, s.location.longitude, 13.045604)
}
const assertIsSalzburgHbf = (t, s) => {
t.equal(s.type, 'station')
t.ok(s.id === '008100002' || s.id === '8100002', 'id should be 8100002')
t.equal(s.name, 'Salzburg Hbf')
t.ok(s.location)
t.ok(isRoughlyEqual(.0005, s.location.latitude, 47.812851))
t.ok(isRoughlyEqual(.0005, s.location.longitude, 13.045604))
}
// todo: DRY with assertValidStationProducts
// todo: DRY with other tests
const assertValidProducts = (t, p) => {
for (let product of allProducts) {
product = product.id
t.equal(typeof p[product], 'boolean', 'product ' + p + ' must be a boolean')
}
}
const assertValidPrice = (t, p) => { const assertValidPrice = (t, p) => {
t.ok(p) t.ok(p)
@ -94,145 +47,95 @@ const assertValidPrice = (t, p) => {
} }
} }
// todo: fix this upstream
// see https://github.com/public-transport/hafas-client/blob/c6e558be217667f1bcdac4a605898eb75ea80374/p/oebb/products.js#L71
const assertValidLine = (t, l) => { // with optional mode
const validators = Object.assign({}, validateFptf.defaultValidators, {
line: validateLineWithoutMode
})
const recurse = validateFptf.createRecurse(validators)
try {
recurse(['line'], l, 'line')
} catch (err) {
t.ifError(err)
}
}
const test = tapePromise(tape) const test = tapePromise(tape)
const client = createClient(oebbProfile) const client = createClient(oebbProfile)
const salzburgHbf = '8100002' const salzburgHbf = '8100002'
const wienWestbahnhof = '1291501' const wienFickeystr = '911014'
const wien = '1190100' const wien = '1190100'
const wienWestbahnhof = '1291501'
const klagenfurtHbf = '8100085' const klagenfurtHbf = '8100085'
const muenchenHbf = '8000261' const muenchenHbf = '8000261'
const grazHbf = '8100173' const wienRenngasse = '1390186'
test('Salzburg Hbf to Wien Westbahnhof', co(function* (t) { test.skip('journeys  Salzburg Hbf to Wien Westbahnhof', co(function* (t) {
const journeys = yield client.journeys(salzburgHbf, wienWestbahnhof, { const journeys = yield client.journeys(salzburgHbf, wienFickeystr, {
departure: when, passedStations: true results: 3,
departure: when,
passedStations: true
}) })
t.ok(Array.isArray(journeys)) yield testJourneysStationToStation({
t.ok(journeys.length > 0, 'no journeys') test: t,
for (let journey of journeys) { journeys,
t.equal(journey.type, 'journey') validate,
fromId: salzburgHbf,
toId: wienFickeystr
})
t.ok(Array.isArray(journey.legs)) for (let i = 0; i < journeys.length; i++) {
t.ok(journey.legs.length > 0, 'no legs') const j = journeys[i]
const leg = journey.legs[0] // todo: all legs if (j.price) assertValidPrice(t, j.price, `journeys[${i}].price`)
assertValidStation(t, leg.origin)
assertValidStationProducts(t, leg.origin.products)
// todo
// if (!(yield findStation(leg.origin.id))) {
// console.error('unknown station', leg.origin.id, leg.origin.name)
// }
assertValidWhen(t, leg.departure, when)
t.equal(typeof leg.departurePlatform, 'string')
assertValidStation(t, leg.destination)
assertValidStationProducts(t, leg.origin.products)
// todo
// if (!(yield findStation(leg.destination.id))) {
// console.error('unknown station', leg.destination.id, leg.destination.name)
// }
assertValidWhen(t, leg.arrival, when)
t.equal(typeof leg.arrivalPlatform, 'string')
assertValidLine(t, leg.line)
t.ok(Array.isArray(leg.passed))
for (let stopover of leg.passed) assertValidStopover(t, stopover)
if (journey.price) assertValidPrice(t, journey.price)
} }
t.end() t.end()
})) }))
// todo: journeys, only one product
test('journeys  fails with no product', (t) => {
journeysFailsWithNoProduct({
test: t,
fetchJourneys: client.journeys,
fromId: salzburgHbf,
toId: wienFickeystr,
when,
products
})
t.end()
})
test('Salzburg Hbf to 1220 Wien, Wagramer Straße 5', co(function* (t) { test('Salzburg Hbf to 1220 Wien, Wagramer Straße 5', co(function* (t) {
const wagramerStr = { const wagramerStr = {
type: 'location', type: 'location',
address: '1220 Wien, Wagramer Straße 5',
latitude: 48.236216, latitude: 48.236216,
longitude: 16.425863, longitude: 16.425863
address: '1220 Wien, Wagramer Straße 5'
} }
const journeys = yield client.journeys(salzburgHbf, wagramerStr, {
results: 3,
departure: when
})
const journeys = yield client.journeys(salzburgHbf, wagramerStr, {departure: when}) yield testJourneysStationToAddress({
test: t,
t.ok(Array.isArray(journeys)) journeys,
t.ok(journeys.length >= 1, 'no journeys') validate,
const journey = journeys[0] fromId: salzburgHbf,
const firstLeg = journey.legs[0] to: wagramerStr
const lastLeg = journey.legs[journey.legs.length - 1] })
assertValidStation(t, firstLeg.origin)
assertValidStationProducts(t, firstLeg.origin.products)
// todo
// if (!(yield findStation(leg.origin.id))) {
// console.error('unknown station', leg.origin.id, leg.origin.name)
// }
if (firstLeg.origin.products) assertValidProducts(t, firstLeg.origin.products)
assertValidWhen(t, firstLeg.departure, when)
assertValidWhen(t, firstLeg.arrival, when)
assertValidWhen(t, lastLeg.departure, when)
assertValidWhen(t, lastLeg.arrival, when)
const d = lastLeg.destination
assertValidAddress(t, d)
t.equal(d.address, '1220 Wien, Wagramer Straße 5')
t.ok(isRoughlyEqual(.0001, d.latitude, 48.236216))
t.ok(isRoughlyEqual(.0001, d.longitude, 16.425863))
t.end() t.end()
})) }))
test('Albertina to Salzburg Hbf', co(function* (t) { test('Salzburg Hbf to Albertina', co(function* (t) {
const albertina = { const albertina = {
type: 'location', type: 'location',
latitude: 48.204699, id: '975900003',
longitude: 16.368404,
name: 'Albertina', name: 'Albertina',
id: '975900003' latitude: 48.204699,
longitude: 16.368404
} }
const journeys = yield client.journeys(albertina, salzburgHbf, {departure: when}) const journeys = yield client.journeys(salzburgHbf, albertina, {
results: 3, departure: when
t.ok(Array.isArray(journeys)) })
t.ok(journeys.length >= 1, 'no journeys')
const journey = journeys[0]
const firstLeg = journey.legs[0]
const lastLeg = journey.legs[journey.legs.length - 1]
const o = firstLeg.origin
assertValidPoi(t, o)
t.equal(o.name, 'Albertina')
t.ok(isRoughlyEqual(.0001, o.latitude, 48.204699))
t.ok(isRoughlyEqual(.0001, o.longitude, 16.368404))
assertValidWhen(t, firstLeg.departure, when)
assertValidWhen(t, firstLeg.arrival, when)
assertValidWhen(t, lastLeg.departure, when)
assertValidWhen(t, lastLeg.arrival, when)
assertValidStation(t, lastLeg.destination)
assertValidStationProducts(t, lastLeg.destination.products)
if (lastLeg.destination.products) assertValidProducts(t, lastLeg.destination.products)
// todo
// if (!(yield findStation(leg.destination.id))) {
// console.error('unknown station', leg.destination.id, leg.destination.name)
// }
yield testJourneysStationToPoi({
test: t,
journeys,
validate,
fromId: salzburgHbf,
to: albertina
})
t.end() t.end()
})) }))
@ -243,91 +146,66 @@ test('journeys: via works with detour', co(function* (t) {
const schottenring = '001390163' const schottenring = '001390163'
const donauinsel = '001392277' const donauinsel = '001392277'
const donauinselPassed = '922001' const donauinselPassed = '922001'
const [journey] = yield client.journeys(stephansplatz, schottenring, { const journeys = yield client.journeys(stephansplatz, schottenring, {
via: donauinsel, via: donauinsel,
results: 1, results: 1,
departure: when, departure: when,
passedStations: true passedStations: true
}) })
t.ok(journey) yield testJourneysWithDetour({
test: t,
const l = journey.legs.some(l => l.passed && l.passed.some(p => p.station.id === donauinselPassed)) journeys,
t.ok(l, 'Donauinsel is not being passed') validate,
detourIds: [donauinsel, donauinselPassed]
})
t.end() t.end()
})) }))
test('journeys: via works without detour', co(function* (t) { test('journeys: via works without detour', co(function* (t) {
// When going from Karlsplatz to Praterstern via Museumsquartier, there is *no need* // When going from Karlsplatz to Praterstern via Museumsquartier, there is
// to change trains / no need for a "detour". // *no need* to change trains / no need for a "detour".
const karlsplatz = '001390461' const karlsplatz = '001390461'
const praterstern = '001290201' const praterstern = '001290201'
const museumsquartier = '001390171' const museumsquartier = '001390171'
const museumsquartierPassed = '901014' const museumsquartierPassed = '901014'
const [journey] = yield client.journeys(karlsplatz, praterstern, { const journeys = yield client.journeys(karlsplatz, praterstern, {
via: museumsquartier, via: museumsquartier,
results: 1, results: 1,
departure: when, departure: when,
passedStations: true passedStations: true
}) })
t.ok(journey) validate(t, journeys, 'journeys', 'journeys')
const l = journey.legs.some(l => l.passed && l.passed.some(p => p.station.id === museumsquartierPassed)) const l1 = journeys[0].legs.some((leg) => {
t.ok(l, 'Weihburggasse is not being passed') return (
leg.destination.id === museumsquartier ||
leg.destination.id === museumsquartierPassed
)
})
t.notOk(l1, 'transfer at Museumsquartier')
const l2 = journeys[0].legs.some((leg) => {
return leg.passed && leg.passed.some((passed) => {
return passed.station.id === museumsquartierPassed
})
})
t.ok(l2, 'Museumsquartier is not being passed')
t.end() t.end()
})) }))
test('earlier/later journeys, Salzburg Hbf -> Wien Westbahnhof', co(function* (t) { test('earlier/later journeys, Salzburg Hbf -> Wien Westbahnhof', co(function* (t) {
const model = yield client.journeys(salzburgHbf, wienWestbahnhof, { yield testEarlierLaterJourneys({
results: 3, departure: when test: t,
fetchJourneys: client.journeys,
validate,
fromId: salzburgHbf,
toId: wienWestbahnhof
}) })
t.equal(typeof model.earlierRef, 'string')
t.ok(model.earlierRef)
t.equal(typeof model.laterRef, 'string')
t.ok(model.laterRef)
// when and earlierThan/laterThan should be mutually exclusive
t.throws(() => {
client.journeys(salzburgHbf, wienWestbahnhof, {
departure: when, earlierThan: model.earlierRef
})
})
t.throws(() => {
client.journeys(salzburgHbf, wienWestbahnhof, {
departure: when, laterThan: model.laterRef
})
})
let earliestDep = Infinity, latestDep = -Infinity
for (let j of model) {
const dep = +new Date(j.legs[0].departure)
if (dep < earliestDep) earliestDep = dep
else if (dep > latestDep) latestDep = dep
}
const earlier = yield client.journeys(salzburgHbf, wienWestbahnhof, {
results: 3,
// todo: single journey ref?
earlierThan: model.earlierRef
})
for (let j of earlier) {
t.ok(new Date(j.legs[0].departure) < earliestDep)
}
const later = yield client.journeys(salzburgHbf, wienWestbahnhof, {
results: 3,
// todo: single journey ref?
laterThan: model.laterRef
})
for (let j of later) {
t.ok(new Date(j.legs[0].departure) > latestDep)
}
t.end() t.end()
})) }))
@ -341,144 +219,136 @@ test('leg details for Wien Westbahnhof to München Hbf', co(function* (t) {
t.ok(p.line.name, 'precondition failed') t.ok(p.line.name, 'precondition failed')
const leg = yield client.journeyLeg(p.id, p.line.name, {when}) const leg = yield client.journeyLeg(p.id, p.line.name, {when})
t.equal(typeof leg.id, 'string') validate(t, leg, 'journeyLeg', 'leg')
t.ok(leg.id) t.end()
}))
assertValidLine(t, leg.line) test('departures at Wien Leibenfrostgasse', co(function* (t) {
const wienLeibenfrostgasse = '1390469'
const ids = [
wienLeibenfrostgasse, // station
'904029', // stop "Wien Leibenfrostgasse (Phorusgasse)s"
'904030' // stop "Wien Leibenfrostgasse (Ziegelofengasse)"
]
t.equal(typeof leg.direction, 'string') const deps = yield client.departures(wienLeibenfrostgasse, {
t.ok(leg.direction) duration: 15, when
})
t.ok(Array.isArray(leg.passed)) validate(t, deps, 'departures', 'departures')
for (let passed of leg.passed) assertValidStopover(t, passed) t.ok(deps.length > 0, 'must be >0 departures')
// todo: move into deps validator
t.deepEqual(deps, deps.sort((a, b) => t.when > b.when))
for (let i = 0; i < deps.length; i++) {
const dep = deps[i]
const msg = `deps[${i}].station.id is invalid`
t.ok(ids.includes(dep.station.id, msg))
}
t.end() t.end()
})) }))
test('departures at Salzburg Hbf', co(function* (t) { test('departures with station object', co(function* (t) {
const deps = yield client.departures(salzburgHbf, { const deps = yield client.departures({
duration: 5, when type: 'station',
}) id: salzburgHbf,
name: 'Salzburg Hbf',
t.ok(Array.isArray(deps)) location: {
for (let dep of deps) { type: 'location',
assertValidStation(t, dep.station) latitude: 1.23,
assertValidStationProducts(t, dep.station.products) longitude: 2.34
// todo }
// if (!(yield findStation(dep.station.id))) { }, {when})
// console.error('unknown station', dep.station.id, dep.station.name)
// }
if (dep.station.products) assertValidProducts(t, dep.station.products)
assertValidWhen(t, dep.when, when)
}
validate(t, deps, 'departures', 'departures')
t.end() t.end()
})) }))
test('nearby Salzburg Hbf', co(function* (t) { test('nearby Salzburg Hbf', co(function* (t) {
const salzburgHbfPosition = { const nearby = yield client.nearby({
type: 'location', type: 'location',
longitude: 13.045604, longitude: 13.045604,
latitude: 47.812851 latitude: 47.812851
} }, {
const nearby = yield client.nearby(salzburgHbfPosition, { results: 5, distance: 400
results: 2, distance: 400
}) })
t.ok(Array.isArray(nearby)) validate(t, nearby, 'locations', 'nearby')
t.equal(nearby.length, 2) t.equal(nearby.length, 5)
assertIsSalzburgHbf(t, nearby[0]) const s = nearby[0]
t.ok(nearby[0].distance >= 0) t.ok(s.id === '008100002' || s.id === '8100002', 'id should be 8100002')
t.ok(nearby[0].distance <= 100) t.equal(s.name, 'Salzburg Hbf')
t.ok(isRoughlyEqual(.0005, s.location.latitude, 47.812851))
for (let n of nearby) { t.ok(isRoughlyEqual(.0005, s.location.longitude, 13.045604))
if (n.type === 'station') assertValidStation(t, n) t.ok(s.distance >= 0)
else assertValidLocation(t, n) t.ok(s.distance <= 100)
}
t.end() t.end()
})) }))
test('locations named Salzburg', co(function* (t) { test('locations named Salzburg', co(function* (t) {
const locations = yield client.locations('Salzburg', { const locations = yield client.locations('Salzburg', {
results: 10 results: 20
}) })
t.ok(Array.isArray(locations)) validate(t, locations, 'locations', 'locations')
t.ok(locations.length > 0) t.ok(locations.length <= 20)
t.ok(locations.length <= 10)
for (let l of locations) { t.ok(locations.find(s => s.type === 'station'))
if (l.type === 'station') assertValidStation(t, l) t.ok(locations.find(s => s.id && s.name)) // POIs
else assertValidLocation(t, l) t.ok(locations.some(s => s.id === '008100002' || s.id === '8100002'))
}
t.ok(locations.some(isSalzburgHbf))
t.end() t.end()
})) }))
test('station', co(function* (t) { test('station', co(function* (t) {
const loc = yield client.station(grazHbf) const loc = yield client.station(wienRenngasse)
assertValidStation(t, loc) // todo: find a way to always get products from the API
t.equal(loc.id, grazHbf) // todo: cfg.stationProductsOptional option
const allProducts = products.reduce((acc, p) => (acc[p.id] = true, acc), {})
const validateStation = createValidateStation(cfg)
const validate = createValidate(cfg, {
station: (validate, s, name) => {
const withFakeProducts = Object.assign({products: allProducts}, s)
validateStation(validate, withFakeProducts, name)
}
})
validate(t, loc, 'station', 'station')
t.equal(loc.id, wienRenngasse)
t.end() t.end()
})) }))
test('radar Salzburg', co(function* (t) { test('radar Salzburg', co(function* (t) {
const vehicles = yield client.radar({ let vehicles = yield client.radar({
north: 47.827203, north: 47.827203,
west: 13.001261, west: 13.001261,
south: 47.773278, south: 47.773278,
east: 13.07562 east: 13.07562
}, { }, {
duration: 5 * 60, when duration: 5 * 60,
// when
}) })
t.ok(Array.isArray(vehicles)) // todo: find a way to always get frames from the API
t.ok(vehicles.length > 0) vehicles = vehicles.filter(m => m.frames && m.frames.length > 0)
for (let v of vehicles) {
// todo // todo: find a way to always get products from the API
// t.ok(findStation(v.direction)) // todo: cfg.stationProductsOptional option
assertValidLine(t, v.line) const allProducts = products.reduce((acc, p) => (acc[p.id] = true, acc), {})
const validateStation = createValidateStation(cfg)
const validate = createValidate(cfg, {
station: (validate, s, name) => {
const withFakeProducts = Object.assign({products: allProducts}, s)
validateStation(validate, withFakeProducts, name)
},
line: validateLine
})
validate(t, vehicles, 'movements', 'vehicles')
t.equal(typeof v.location.latitude, 'number')
t.ok(v.location.latitude <= 52, 'vehicle is too far away')
t.ok(v.location.latitude >= 42, 'vehicle is too far away')
t.equal(typeof v.location.longitude, 'number')
t.ok(v.location.longitude >= 10, 'vehicle is too far away')
t.ok(v.location.longitude <= 16, 'vehicle is too far away')
t.ok(Array.isArray(v.nextStops))
for (let st of v.nextStops) {
assertValidStopover(t, st, true)
if (st.arrival) {
t.equal(typeof st.arrival, 'string')
const arr = +new Date(st.arrival)
// note that this can be an ICE train
t.ok(isRoughlyEqual(14 * hour, +when, arr))
}
if (st.departure) {
t.equal(typeof st.departure, 'string')
const dep = +new Date(st.departure)
t.ok(isRoughlyEqual(14 * hour, +when, dep))
}
}
t.ok(Array.isArray(v.frames))
for (let f of v.frames) {
assertValidStation(t, f.origin, true)
// can contain stations in germany which don't have a products property, would break
// assertValidStationProducts(t, f.origin.products)
assertValidStation(t, f.destination, true)
// can contain stations in germany which don't have a products property, would break
// assertValidStationProducts(t, f.destination.products)
t.equal(typeof f.t, 'number')
}
}
t.end() t.end()
})) }))

View file

@ -1,158 +0,0 @@
'use strict'
const validateFptf = require('validate-fptf')
const isRoughlyEqual = require('is-roughly-equal')
const {DateTime} = require('luxon')
const isValidWGS84 = require('is-coordinates')
const validateFptfWith = (t, item, allowedTypes, name) => {
try {
validateFptf.recurse(allowedTypes, item, name)
} catch (err) {
t.ifError(err)
}
}
const assertValidStation = (t, s, coordsOptional = false) => {
validateFptfWith(t, s, ['station'], 'station')
if (!coordsOptional || (s.location !== null && s.location !== undefined)) {
t.ok(s.location)
assertValidLocation(t, s.location, coordsOptional)
}
}
const assertValidPoi = (t, p) => {
assertValidLocation(t, p, true)
t.equal(typeof p.id, 'string')
t.equal(typeof p.name, 'string')
if (p.address !== null && p.address !== undefined) {
t.equal(typeof p.address, 'string')
t.ok(p.address)
}
}
const assertValidAddress = (t, a) => {
assertValidLocation(t, a, true)
t.equal(typeof a.address, 'string')
}
const assertValidLocation = (t, l, coordsOptional = false) => {
t.equal(l.type, 'location')
if (l.name !== null && l.name !== undefined) {
t.equal(typeof l.name, 'string')
t.ok(l.name)
}
if (l.address !== null && l.address !== undefined) {
t.equal(typeof l.address, 'string')
t.ok(l.address)
}
const hasLatitude = l.latitude !== null && l.latitude !== undefined
const hasLongitude = l.longitude !== null && l.longitude !== undefined
if (!coordsOptional && hasLatitude) t.equal(typeof l.latitude, 'number')
if (!coordsOptional && hasLongitude) t.equal(typeof l.longitude, 'number')
if ((hasLongitude && !hasLatitude) || (hasLatitude && !hasLongitude)) {
t.fail('should have both .latitude and .longitude')
}
if (hasLatitude && hasLongitude) isValidWGS84([l.longitude, l.latitude])
if (!coordsOptional && l.altitude !== null && l.altitude !== undefined) {
t.equal(typeof l.altitude, 'number')
}
}
const validLineModes = [
'train', 'bus', 'watercraft', 'taxi', 'gondola', 'aircraft',
'car', 'bicycle', 'walking'
]
const assertValidLine = (t, l) => {
validateFptfWith(t, l, ['line'], 'line')
}
const isValidDateTime = (w) => {
return !Number.isNaN(+new Date(w))
}
const assertValidStopover = (t, s, coordsOptional = false) => {
if ('arrival' in s) t.ok(isValidDateTime(s.arrival))
if ('departure' in s) t.ok(isValidDateTime(s.departure))
if (s.arrivalDelay !== null && s.arrivalDelay !== undefined) {
t.equal(typeof s.arrivalDelay, 'number')
}
if (s.departureDelay !== null && s.departureDelay !== undefined) {
t.equal(typeof s.departureDelay, 'number')
}
if (!('arrival' in s) && !('departure' in s)) {
t.fail('stopover doesn\'t contain arrival or departure')
}
t.ok(s.station)
assertValidStation(t, s.station, coordsOptional)
}
const hour = 60 * 60 * 1000
const week = 7 * 24 * hour
// next Monday 10 am
const createWhen = (timezone, locale) => {
return DateTime.fromMillis(Date.now(), {
zone: timezone,
locale,
}).startOf('week').plus({weeks: 1, hours: 10}).toJSDate()
}
const isValidWhen = (actual, expected) => {
const ts = +new Date(actual)
if (Number.isNaN(ts)) return false
return isRoughlyEqual(12 * hour, +expected, ts)
}
const assertValidWhen = (t, actual, expected) => {
t.ok(isValidWhen(actual, expected), 'invalid when')
}
const assertValidTicket = (t, ti) => {
t.strictEqual(typeof ti.name, 'string')
t.ok(ti.name.length > 0)
if (ti.price !== null) {
t.strictEqual(typeof ti.price, 'number')
t.ok(ti.price > 0)
}
if (ti.amount !== null) {
t.strictEqual(typeof ti.amount, 'number')
t.ok(ti.amount > 0)
}
if ('bike' in ti) t.strictEqual(typeof ti.bike, 'boolean')
if ('shortTrip' in ti) t.strictEqual(typeof ti.shortTrip, 'boolean')
if ('group' in ti) t.strictEqual(typeof ti.group, 'boolean')
if ('fullDay' in ti) t.strictEqual(typeof ti.fullDay, 'boolean')
if (ti.tariff !== null) {
t.strictEqual(typeof ti.tariff, 'string')
t.ok(ti.tariff.length > 0)
}
if (ti.coverage !== null) {
t.strictEqual(typeof ti.coverage, 'string')
t.ok(ti.coverage.length > 0)
}
if (ti.variant !== null) {
t.strictEqual(typeof ti.variant, 'string')
t.ok(ti.variant.length > 0)
}
}
module.exports = {
assertValidStation,
assertValidPoi,
assertValidAddress,
assertValidLocation,
assertValidLine,
isValidDateTime,
assertValidStopover,
hour, createWhen, isValidWhen, assertValidWhen,
assertValidTicket
}

View file

@ -1,38 +0,0 @@
'use strict'
const a = require('assert')
const is = require('@sindresorhus/is')
const validateItem = require('validate-fptf/lib/item')
const validateReference = require('validate-fptf/lib/reference')
// todo: this is copied code, DRY this up!
// see https://github.com/public-transport/validate-fptf/blob/373b4847ec9668c4a9ec9b0dbd50f8a70ffbe127/line.js
const validateLineWithoutMode = (validate, line, name) => {
validateItem(line, name)
a.strictEqual(line.type, 'line', name + '.type must be `line`')
validateReference(line.id, name + '.id')
a.strictEqual(typeof line.name, 'string', name + '.name must be a string')
a.ok(line.name.length > 0, name + '.name can\'t be empty')
// skipping line validation here
// see https://github.com/public-transport/hafas-client/issues/8#issuecomment-355839965
if (is.undefined(line.mode) || is.null(line.mode)) {
console.error(`ÖBB: Missing \`mode\` for line ${line.name} (at ${name}).`)
}
if (!is.undefined(line.subMode)) {
a.fail(name + '.subMode is reserved an should not be used for now')
}
// todo: routes
if (!is.null(line.operator) && !is.undefined(line.operator)) {
validate(['operator'], line.operator, name + '.operator')
}
}
module.exports = validateLineWithoutMode

View file

@ -1,55 +1,103 @@
'use strict' 'use strict'
const a = require('assert')
const isRoughlyEqual = require('is-roughly-equal')
const stations = require('vbb-stations-autocomplete') const stations = require('vbb-stations-autocomplete')
const a = require('assert')
const shorten = require('vbb-short-station-name')
const tapePromise = require('tape-promise').default const tapePromise = require('tape-promise').default
const tape = require('tape') const tape = require('tape')
const shorten = require('vbb-short-station-name') const isRoughlyEqual = require('is-roughly-equal')
const co = require('./co') const {createWhen} = require('./lib/util')
const co = require('./lib/co')
const createClient = require('..') const createClient = require('..')
const vbbProfile = require('../p/vbb') const vbbProfile = require('../p/vbb')
const products = require('../p/vbb/products')
const { const {
assertValidStation: _assertValidStation, station: createValidateStation,
assertValidPoi, line: createValidateLine,
assertValidAddress, journeyLeg: createValidateJourneyLeg,
assertValidLocation, departure: createValidateDeparture,
assertValidLine: _assertValidLine, movement: _validateMovement
assertValidStopover, } = require('./lib/validators')
hour, createWhen, const createValidate = require('./lib/validate-fptf-with')
assertValidWhen, const testJourneysStationToStation = require('./lib/journeys-station-to-station')
assertValidTicket const testJourneysStationToAddress = require('./lib/journeys-station-to-address')
} = require('./util') const testJourneysStationToPoi = require('./lib/journeys-station-to-poi')
const testEarlierLaterJourneys = require('./lib/earlier-later-journeys')
const journeysFailsWithNoProduct = require('./lib/journeys-fails-with-no-product')
const testDepartures = require('./lib/departures')
const testJourneysWithDetour = require('./lib/journeys-with-detour')
const when = createWhen('Europe/Berlin', 'de-DE') const when = createWhen('Europe/Berlin', 'de-DE')
const assertValidStation = (t, s, coordsOptional = false) => { const cfg = {
_assertValidStation(t, s, coordsOptional) when,
t.equal(s.name, shorten(s.name)) stationCoordsOptional: false,
products
} }
const assertValidStationProducts = (t, p) => { const validateDirection = (dir, name) => {
t.ok(p) if (!stations(dir, true, false)[0]) {
t.equal(typeof p.suburban, 'boolean') console.error(name + `: station "${dir}" is unknown`)
t.equal(typeof p.subway, 'boolean') }
t.equal(typeof p.tram, 'boolean')
t.equal(typeof p.bus, 'boolean')
t.equal(typeof p.ferry, 'boolean')
t.equal(typeof p.express, 'boolean')
t.equal(typeof p.regional, 'boolean')
} }
const assertValidLine = (t, l) => { // todo: coordsOptional = false
_assertValidLine(t, l) const _validateStation = createValidateStation(cfg)
if (l.symbol !== null) t.equal(typeof l.symbol, 'string') const validateStation = (validate, s, name) => {
if (l.nr !== null) t.equal(typeof l.nr, 'number') _validateStation(validate, s, name)
if (l.metro !== null) t.equal(typeof l.metro, 'boolean') // todo: find station by ID
if (l.express !== null) t.equal(typeof l.express, 'boolean') a.equal(s.name, shorten(s.name), name + '.name must be shortened')
if (l.night !== null) t.equal(typeof l.night, 'boolean')
} }
const findStation = (query) => stations(query, true, false)[0] const _validateLine = createValidateLine(cfg)
const validateLine = (validate, l, name) => {
_validateLine(validate, l, name)
if (l.symbol !== null) {
a.strictEqual(typeof l.symbol, 'string', name + '.symbol must be a string')
a.ok(l.symbol, name + '.symbol must not be empty')
}
if (l.nr !== null) {
a.strictEqual(typeof l.nr, 'number', name + '.nr must be a string')
a.ok(l.nr, name + '.nr must not be empty')
}
if (l.metro !== null) {
a.strictEqual(typeof l.metro, 'boolean', name + '.metro must be a boolean')
}
if (l.express !== null) {
a.strictEqual(typeof l.express, 'boolean', name + '.express must be a boolean')
}
if (l.night !== null) {
a.strictEqual(typeof l.night, 'boolean', name + '.night must be a boolean')
}
}
const _validateJourneyLeg = createValidateJourneyLeg(cfg)
const validateJourneyLeg = (validate, l, name) => {
_validateJourneyLeg(validate, l, name)
if (l.mode !== 'walking') {
validateDirection(l.direction, name + '.direction')
}
}
const _validateDeparture = createValidateDeparture(cfg)
const validateDeparture = (validate, dep, name) => {
_validateDeparture(validate, dep, name)
validateDirection(dep.direction, name + '.direction')
}
const validateMovement = (validate, m, name) => {
_validateMovement(validate, m, name)
validateDirection(m.direction, name + '.direction')
}
const validate = createValidate(cfg, {
station: validateStation,
line: validateLine,
journeyLeg: validateJourneyLeg,
departure: validateDeparture,
movement: validateMovement
})
const test = tapePromise(tape) const test = tapePromise(tape)
const client = createClient(vbbProfile) const client = createClient(vbbProfile)
@ -57,58 +105,33 @@ const client = createClient(vbbProfile)
const amrumerStr = '900000009101' const amrumerStr = '900000009101'
const spichernstr = '900000042101' const spichernstr = '900000042101'
const bismarckstr = '900000024201' const bismarckstr = '900000024201'
const westhafen = '900000001201'
const wedding = '900000009104'
const württembergallee = '900000026153'
test('journeys  station to station', co(function* (t) { test('journeys  Spichernstr. to Bismarckstr.', co(function* (t) {
const journeys = yield client.journeys(spichernstr, amrumerStr, { const journeys = yield client.journeys(spichernstr, bismarckstr, {
results: 3, departure: when, passedStations: true results: 3,
departure: when,
passedStations: true
}) })
t.ok(Array.isArray(journeys)) yield testJourneysStationToStation({
t.strictEqual(journeys.length, 3) test: t,
journeys,
validate,
fromId: spichernstr,
toId: bismarckstr
})
// todo: find a journey where there ticket info is always available
for (let journey of journeys) {
t.equal(journey.type, 'journey')
t.ok(Array.isArray(journey.legs))
t.strictEqual(journey.legs.length, 1)
const leg = journey.legs[0] // todo: all legs
t.equal(typeof leg.id, 'string')
t.ok(leg.id)
assertValidStation(t, leg.origin)
assertValidStationProducts(t, leg.origin.products)
t.ok(leg.origin.name.indexOf('(Berlin)') === -1)
t.strictEqual(leg.origin.id, spichernstr)
assertValidWhen(t, leg.departure, when)
assertValidStation(t, leg.destination)
assertValidStationProducts(t, leg.destination.products)
t.strictEqual(leg.destination.id, amrumerStr)
assertValidWhen(t, leg.arrival, when)
assertValidLine(t, leg.line)
if (!findStation(leg.direction)) {
const err = new Error('unknown direction: ' + leg.direction)
err.stack = err.stack.split('\n').slice(0, 2).join('\n')
console.error(err)
}
t.ok(leg.direction.indexOf('(Berlin)') === -1)
t.ok(Array.isArray(leg.passed))
for (let passed of leg.passed) assertValidStopover(t, passed)
// todo: find a journey where there ticket info is always available
if (journey.tickets) {
t.ok(Array.isArray(journey.tickets))
for (let ticket of journey.tickets) assertValidTicket(t, ticket)
}
}
t.end() t.end()
})) }))
test('journeys  only subway', co(function* (t) { test('journeys  only subway', co(function* (t) {
const journeys = yield client.journeys(spichernstr, bismarckstr, { const journeys = yield client.journeys(spichernstr, bismarckstr, {
results: 20, departure: when, results: 20,
departure: when,
products: { products: {
suburban: false, suburban: false,
subway: true, subway: true,
@ -120,118 +143,46 @@ test('journeys  only subway', co(function* (t) {
} }
}) })
t.ok(Array.isArray(journeys)) validate(t, journeys, 'journeys', 'journeys')
t.ok(journeys.length > 1) t.ok(journeys.length > 1)
for (let i = 0; i < journeys.length; i++) {
const journey = journeys[i]
for (let j = 0; j < journey.legs.length; j++) {
const leg = journey.legs[j]
for (let journey of journeys) { const name = `journeys[${i}].legs[${i}].line`
for (let leg of journey.legs) {
if (leg.line) { if (leg.line) {
assertValidLine(t, leg.line) t.equal(leg.line.mode, 'train', name + '.mode is invalid')
t.equal(leg.line.mode, 'train') t.equal(leg.line.product, 'subway', name + '.product is invalid')
t.equal(leg.line.product, 'subway')
} }
t.ok(journey.legs.some(l => l.line), name + '.legs has no subway leg')
} }
} }
t.end() t.end()
})) }))
test('journeys  fails with no product', co(function* (t) { test('journeys  fails with no product', (t) => {
t.plan(1) journeysFailsWithNoProduct({
try { test: t,
client.journeys(spichernstr, bismarckstr, { fetchJourneys: client.journeys,
departure: when, fromId: spichernstr,
products: { toId: bismarckstr,
suburban: false, when,
subway: false, products
tram: false,
bus: false,
ferry: false,
express: false,
regional: false
}
})
// silence rejections, we're only interested in exceptions
.catch(() => {})
} catch (err) {
t.ok(err, 'error thrown')
t.end()
}
}))
test('journeys before date/time', co(function* (t) {
const journeys = yield client.journeys(spichernstr, bismarckstr, {
results: 3, arrival: when
}) })
for (let i = 0; i < journeys.length; i++) {
const j = journeys[i]
const name = `journeys[${i}]`
const lastLeg = j.legs[j.legs.length - 1]
const arr = +new Date(lastLeg.arrival)
t.ok(arr <= when, name + '.arrival is after `when`')
}
t.end() t.end()
})) })
test('earlier/later journeys', co(function* (t) { test('earlier/later journeys', co(function* (t) {
const model = yield client.journeys(spichernstr, bismarckstr, { yield testEarlierLaterJourneys({
results: 3, departure: when test: t,
fetchJourneys: client.journeys,
validate,
fromId: spichernstr,
toId: bismarckstr
}) })
t.equal(typeof model.earlierRef, 'string')
t.ok(model.earlierRef)
t.equal(typeof model.laterRef, 'string')
t.ok(model.laterRef)
// departure/arrival and earlierThan/laterThan should be mutually exclusive
t.throws(() => {
client.journeys(spichernstr, bismarckstr, {
departure: when, earlierThan: model.earlierRef
})
})
t.throws(() => {
client.journeys(spichernstr, bismarckstr, {
departure: when, laterThan: model.laterRef
})
})
t.throws(() => {
client.journeys(spichernstr, bismarckstr, {
arrival: when, earlierThan: model.earlierRef
})
})
t.throws(() => {
client.journeys(spichernstr, bismarckstr, {
arrival: when, laterThan: model.laterRef
})
})
let earliestDep = Infinity, latestDep = -Infinity
for (let j of model) {
const dep = +new Date(j.legs[0].departure)
if (dep < earliestDep) earliestDep = dep
else if (dep > latestDep) latestDep = dep
}
const earlier = yield client.journeys(spichernstr, bismarckstr, {
results: 3,
// todo: single journey ref?
earlierThan: model.earlierRef
})
for (let j of earlier) {
t.ok(new Date(j.legs[0].departure) < earliestDep)
}
const later = yield client.journeys(spichernstr, bismarckstr, {
results: 3,
// todo: single journey ref?
laterThan: model.laterRef
})
for (let j of later) {
t.ok(new Date(j.legs[0].departure) > latestDep)
}
t.end() t.end()
})) }))
@ -245,147 +196,92 @@ test('journey leg details', co(function* (t) {
t.ok(p.line.name, 'precondition failed') t.ok(p.line.name, 'precondition failed')
const leg = yield client.journeyLeg(p.id, p.line.name, {when}) const leg = yield client.journeyLeg(p.id, p.line.name, {when})
t.equal(typeof leg.id, 'string') validate(t, leg, 'journeyLeg', 'leg')
t.ok(leg.id)
assertValidLine(t, leg.line)
t.equal(typeof leg.direction, 'string')
t.ok(leg.direction)
t.ok(Array.isArray(leg.passed))
for (let passed of leg.passed) assertValidStopover(t, passed)
t.end() t.end()
})) }))
test('journeys  station to address', co(function* (t) { test('journeys  station to address', co(function* (t) {
const journeys = yield client.journeys(spichernstr, { const torfstr = {
type: 'location', type: 'location',
address: 'Torfstr. 17, Berlin', address: '13353 Berlin-Wedding, Torfstr. 17',
latitude: 52.541797, longitude: 13.350042 latitude: 52.541797,
}, {results: 1, departure: when}) longitude: 13.350042
}
t.ok(Array.isArray(journeys)) const journeys = yield client.journeys(spichernstr, torfstr, {
t.strictEqual(journeys.length, 1) results: 3,
const journey = journeys[0] departure: when
const leg = journey.legs[journey.legs.length - 1] })
assertValidStation(t, leg.origin)
assertValidStationProducts(t, leg.origin.products)
assertValidWhen(t, leg.departure, when)
const dest = leg.destination
assertValidAddress(t, dest)
t.strictEqual(dest.address, '13353 Berlin-Wedding, Torfstr. 17')
t.ok(isRoughlyEqual(.0001, dest.latitude, 52.541797))
t.ok(isRoughlyEqual(.0001, dest.longitude, 13.350042))
assertValidWhen(t, leg.arrival, when)
yield testJourneysStationToAddress({
test: t,
journeys,
validate,
fromId: spichernstr,
to: torfstr
})
t.end() t.end()
})) }))
test('journeys  station to POI', co(function* (t) { test('journeys  station to POI', co(function* (t) {
const journeys = yield client.journeys(spichernstr, { const atze = {
type: 'location', type: 'location',
id: '900980720', id: '900980720',
name: 'Berlin, Atze Musiktheater für Kinder', name: 'Berlin, Atze Musiktheater für Kinder',
latitude: 52.543333, longitude: 13.351686 latitude: 52.543333,
}, {results: 1, departure: when}) longitude: 13.351686
}
t.ok(Array.isArray(journeys)) const journeys = yield client.journeys(spichernstr, atze, {
t.strictEqual(journeys.length, 1) results: 3,
const journey = journeys[0] departure: when
const leg = journey.legs[journey.legs.length - 1] })
assertValidStation(t, leg.origin)
assertValidStationProducts(t, leg.origin.products)
assertValidWhen(t, leg.departure, when)
const dest = leg.destination
assertValidPoi(t, dest)
t.strictEqual(dest.id, '900980720')
t.strictEqual(dest.name, 'Berlin, Atze Musiktheater für Kinder')
t.ok(isRoughlyEqual(.0001, dest.latitude, 52.543333))
t.ok(isRoughlyEqual(.0001, dest.longitude, 13.351686))
assertValidWhen(t, leg.arrival, when)
yield testJourneysStationToPoi({
test: t,
journeys,
validate,
fromId: spichernstr,
to: atze
})
t.end() t.end()
})) }))
test('journeys: via works with detour', co(function* (t) { test('journeys: via works with detour', co(function* (t) {
// Going from Westhafen to Wedding via Württembergalle without detour // Going from Westhafen to Wedding via Württembergalle without detour
// is currently impossible. We check if the routing engine computes a detour. // is currently impossible. We check if the routing engine computes a detour.
const westhafen = '900000001201' const journeys = yield client.journeys(westhafen, wedding, {
const wedding = '900000009104'
const württembergallee = '900000026153'
const [journey] = yield client.journeys(westhafen, wedding, {
via: württembergallee, via: württembergallee,
results: 1, results: 1,
departure: when, departure: when,
passedStations: true passedStations: true
}) })
t.ok(journey) yield testJourneysWithDetour({
test: t,
const l = journey.legs.some(l => l.passed && l.passed.some(p => p.station.id === württembergallee)) journeys,
t.ok(l, 'Württembergalle is not being passed') validate,
detourIds: [württembergallee]
t.end()
}))
test('journeys: via works without detour', co(function* (t) {
// When going from Ruhleben to Zoo via Kastanienallee, there is *no need*
// to change trains / no need for a "detour".
const ruhleben = '900000025202'
const zoo = '900000023201'
const kastanienallee = '900000020152'
const [journey] = yield client.journeys(ruhleben, zoo, {
via: kastanienallee,
results: 1,
departure: when,
passedStations: true
}) })
t.ok(journey)
const l = journey.legs.some(l => l.passed && l.passed.some(p => p.station.id === kastanienallee))
t.ok(l, 'Kastanienallee is not being passed')
t.end() t.end()
})) }))
// todo: without detour test
test('departures', co(function* (t) { test('departures', co(function* (t) {
const deps = yield client.departures(spichernstr, {duration: 5, when}) const departures = yield client.departures(spichernstr, {
duration: 5, when
})
t.ok(Array.isArray(deps)) yield testDepartures({
t.deepEqual(deps, deps.sort((a, b) => t.when > b.when)) test: t,
for (let dep of deps) { departures,
t.equal(typeof dep.journeyId, 'string') validate,
t.ok(dep.journeyId) id: spichernstr
})
t.equal(dep.station.name, 'U Spichernstr.')
assertValidStation(t, dep.station)
assertValidStationProducts(t, dep.station.products)
t.strictEqual(dep.station.id, spichernstr)
assertValidWhen(t, dep.when, when)
if (!findStation(dep.direction)) {
const err = new Error('unknown direction: ' + dep.direction)
err.stack = err.stack.split('\n').slice(0, 2).join('\n')
console.error(err)
}
assertValidLine(t, dep.line)
}
t.end() t.end()
})) }))
test('departures with station object', co(function* (t) { test('departures with station object', co(function* (t) {
yield client.departures({ const deps = yield client.departures({
type: 'station', type: 'station',
id: spichernstr, id: spichernstr,
name: 'U Spichernstr', name: 'U Spichernstr',
@ -396,7 +292,7 @@ test('departures with station object', co(function* (t) {
} }
}, {when}) }, {when})
t.ok('did not fail') validate(t, deps, 'departures', 'departures')
t.end() t.end()
})) }))
@ -404,13 +300,13 @@ test('departures at 7-digit station', co(function* (t) {
const eisenach = '8010097' // see derhuerst/vbb-hafas#22 const eisenach = '8010097' // see derhuerst/vbb-hafas#22
yield client.departures(eisenach, {when}) yield client.departures(eisenach, {when})
t.pass('did not fail') t.pass('did not fail')
t.end() t.end()
})) }))
test('nearby', co(function* (t) { test('nearby', co(function* (t) {
const berlinerStr = '900000044201'
const landhausstr = '900000043252'
// Berliner Str./Bundesallee // Berliner Str./Bundesallee
const nearby = yield client.nearby({ const nearby = yield client.nearby({
type: 'location', type: 'location',
@ -418,18 +314,14 @@ test('nearby', co(function* (t) {
longitude: 13.3310411 longitude: 13.3310411
}, {distance: 200}) }, {distance: 200})
t.ok(Array.isArray(nearby)) validate(t, nearby, 'locations', 'nearby')
for (let n of nearby) {
if (n.type === 'station') assertValidStation(t, n)
else assertValidLocation(t, n, false)
}
t.equal(nearby[0].id, '900000044201') t.equal(nearby[0].id, berlinerStr)
t.equal(nearby[0].name, 'U Berliner Str.') t.equal(nearby[0].name, 'U Berliner Str.')
t.ok(nearby[0].distance > 0) t.ok(nearby[0].distance > 0)
t.ok(nearby[0].distance < 100) t.ok(nearby[0].distance < 100)
t.equal(nearby[1].id, '900000043252') t.equal(nearby[1].id, landhausstr)
t.equal(nearby[1].name, 'Landhausstr.') t.equal(nearby[1].name, 'Landhausstr.')
t.ok(nearby[1].distance > 100) t.ok(nearby[1].distance > 100)
t.ok(nearby[1].distance < 200) t.ok(nearby[1].distance < 200)
@ -437,18 +329,12 @@ test('nearby', co(function* (t) {
t.end() t.end()
})) }))
test('locations', co(function* (t) { test('locations', co(function* (t) {
const locations = yield client.locations('Alexanderplatz', {results: 20}) const locations = yield client.locations('Alexanderplatz', {results: 20})
t.ok(Array.isArray(locations)) validate(t, locations, 'locations', 'locations')
t.ok(locations.length > 0)
t.ok(locations.length <= 20) t.ok(locations.length <= 20)
for (let l of locations) {
if (l.type === 'station') assertValidStation(t, l)
else assertValidLocation(t, l)
}
t.ok(locations.find(s => s.type === 'station')) t.ok(locations.find(s => s.type === 'station'))
t.ok(locations.find(s => s.id && s.name)) // POIs t.ok(locations.find(s => s.id && s.name)) // POIs
t.ok(locations.find(s => !s.name && s.address)) // addresses t.ok(locations.find(s => !s.name && s.address)) // addresses
@ -457,21 +343,14 @@ test('locations', co(function* (t) {
})) }))
test('station', co(function* (t) { test('station', co(function* (t) {
const loc = yield client.station(spichernstr) const s = yield client.station(spichernstr)
assertValidStation(t, loc) validate(t, s, 'station', 'station')
t.equal(loc.id, spichernstr) t.equal(s.id, spichernstr)
t.ok(Array.isArray(loc.lines))
if (Array.isArray(loc.lines)) {
for (let line of loc.lines) assertValidLine(t, line)
}
t.end() t.end()
})) }))
test('radar', co(function* (t) { test('radar', co(function* (t) {
const vehicles = yield client.radar({ const vehicles = yield client.radar({
north: 52.52411, north: 52.52411,
@ -482,53 +361,6 @@ test('radar', co(function* (t) {
duration: 5 * 60, when duration: 5 * 60, when
}) })
t.ok(Array.isArray(vehicles)) validate(t, vehicles, 'movements', 'vehicles')
t.ok(vehicles.length > 0)
for (let v of vehicles) {
if (!findStation(v.direction)) {
const err = new Error('unknown direction: ' + v.direction)
err.stack = err.stack.split('\n').slice(0, 2).join('\n')
console.error(err)
}
assertValidLine(t, v.line)
t.equal(typeof v.location.latitude, 'number')
t.ok(v.location.latitude <= 55, 'vehicle is too far away')
t.ok(v.location.latitude >= 45, 'vehicle is too far away')
t.equal(typeof v.location.longitude, 'number')
t.ok(v.location.longitude >= 9, 'vehicle is too far away')
t.ok(v.location.longitude <= 15, 'vehicle is too far away')
t.ok(Array.isArray(v.nextStops))
for (let st of v.nextStops) {
assertValidStopover(t, st, true)
t.strictEqual(st.station.name.indexOf('(Berlin)'), -1)
if (st.arrival) {
t.equal(typeof st.arrival, 'string')
const arr = +new Date(st.arrival)
// note that this can be an ICE train
t.ok(isRoughlyEqual(14 * hour, +when, arr))
}
if (st.departure) {
t.equal(typeof st.departure, 'string')
const dep = +new Date(st.departure)
// note that this can be an ICE train
t.ok(isRoughlyEqual(14 * hour, +when, dep))
}
}
t.ok(Array.isArray(v.frames))
for (let f of v.frames) {
assertValidStation(t, f.origin, true)
assertValidStationProducts(t, f.origin.products)
t.strictEqual(f.origin.name.indexOf('(Berlin)'), -1)
assertValidStation(t, f.destination, true)
assertValidStationProducts(t, f.destination.products)
t.strictEqual(f.destination.name.indexOf('(Berlin)'), -1)
t.equal(typeof f.t, 'number')
}
}
t.end() t.end()
})) }))