diff --git a/test/db.js b/test/db.js index e20e11f0..dee3e6b1 100644 --- a/test/db.js +++ b/test/db.js @@ -1,71 +1,47 @@ '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 tape = require('tape') const isRoughlyEqual = require('is-roughly-equal') +const {createWhen} = require('./lib/util') const co = require('./lib/co') const createClient = require('..') const dbProfile = require('../p/db') -const allProducts = require('../p/db/products') +const products = require('../p/db/products') const { - assertValidStation: _assertValidStation, - assertValidPoi, - assertValidAddress, - assertValidLocation, - assertValidLine, - assertValidStopover, - createWhen, assertValidWhen -} = require('./lib/util.js') + station: createValidateStation +} = require('./lib/validators') +const createValidate = require('./lib/validate-fptf-with') + +const isObj = o => o !== null && 'object' === typeof o && !Array.isArray(o) const when = createWhen('Europe/Berlin', 'de-DE') -// todo: DRY with other tests, move into lib -const assertValidStation = (t, s) => { - _assertValidStation(t, s) - t.ok(s.products) - for (let product of allProducts) { - product = product.id - const msg = `station.products[${product}] must be a boolean` - t.equal(typeof s.products[product], 'boolean', msg) +const cfg = { + when, + stationCoordsOptional: false, + products +} + +const _validateStation = createValidateStation(cfg) +const validateStation = (validate, s, name) => { + _validateStation(validate, s, name) + const match = stations.some(station => ( + station.id === s.id || + (station.additionalIds && station.additionalIds.includes(s.id)) + )) + if (!match) { + console.error(name + `.id: unknown ID "${s.id}"`) } } -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 validate = createValidate(cfg, { + station: validateStation }) -const isJungfernheide = (s) => { - return s.type === 'station' && - (s.id === '008011167' || s.id === jungfernh) && - s.name === 'Berlin Jungfernheide' && - s.location && - isRoughlyEqual(s.location.latitude, 52.530408, .0005) && - isRoughlyEqual(s.location.longitude, 13.299424, .0005) -} - -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(s.location.latitude, 52.530408, .0005)) - t.ok(isRoughlyEqual(s.location.longitude, 13.299424, .0005)) -} - const assertValidPrice = (t, p) => { t.ok(p) if (p.amount !== null) { @@ -81,101 +57,67 @@ const assertValidPrice = (t, p) => { const test = tapePromise(tape) const client = createClient(dbProfile) -const jungfernh = '8011167' const berlinHbf = '8011160' const münchenHbf = '8000261' -const hannoverHbf = '8000152' +const jungfernheide = '8011167' +const atze = '991598902' +const westhafen = '008089116' +const wedding = '008089131' +const württembergallee = '731084' const regensburgHbf = '8000309' test('Berlin Jungfernheide to München Hbf', co(function* (t) { - const journeys = yield client.journeys(jungfernh, münchenHbf, { + const journeys = yield client.journeys(jungfernheide, münchenHbf, { when, passedStations: true }) - t.ok(Array.isArray(journeys)) - t.ok(journeys.length > 0, 'no journeys') + validate(t, journeys, 'journeys', '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) - 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) - 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() })) +// todo: journeys, only one product +// todo: journeys, fails with no product + test('Berlin Jungfernheide to Torfstraße 17', co(function* (t) { - const journeys = yield client.journeys(jungfernh, { + const latitude = 52.5416823 + const longitude = 13.3491223 + const journeys = yield client.journeys(jungfernheide, { type: 'location', address: 'Torfstraße 17', - latitude: 52.5416823, longitude: 13.3491223 + latitude, longitude }, {when}) - t.ok(Array.isArray(journeys)) - t.ok(journeys.length >= 1, 'no journeys') - const journey = journeys[0] - const leg = journey.legs[journey.legs.length - 1] + validate(t, journeys, 'journeys', 'journeys') - assertValidStation(t, leg.origin) - if (!(yield findStation(leg.origin.id))) { - console.error('unknown station', leg.origin.id, leg.origin.name) - } - assertValidWhen(t, leg.departure, when) - assertValidWhen(t, leg.arrival, 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)) + const i = journeys[0].legs.length - 1 + const d = journeys[0].legs[i].destination + const name = `journeys[0].legs[${i}].destination` + t.equal(d.address, 'Torfstraße 17', name + '.address is invalid') + t.ok(isRoughlyEqual(.0001, d.latitude, latitude), name + '.latitude is invalid') + t.ok(isRoughlyEqual(.0001, d.longitude, longitude), name + '.longitude is invalid') t.end() })) test('Berlin Jungfernheide to ATZE Musiktheater', co(function* (t) { - const journeys = yield client.journeys(jungfernh, { - type: 'location', id: '991598902', name: 'ATZE Musiktheater', - latitude: 52.542417, longitude: 13.350437 + const latitude = 52.542417 + const longitude = 13.350437 + const journeys = yield client.journeys(jungfernheide, { + type: 'location', id: atze, name: 'ATZE Musiktheater', + latitude, longitude }, {when}) - t.ok(Array.isArray(journeys)) - t.ok(journeys.length >= 1, 'no journeys') - const journey = journeys[0] - const leg = journey.legs[journey.legs.length - 1] + validate(t, journeys, 'journeys', 'journeys') - assertValidStation(t, leg.origin) - if (!(yield findStation(leg.origin.id))) { - console.error('unknown station', leg.origin.id, leg.origin.name) - } - assertValidWhen(t, leg.departure, when) - assertValidWhen(t, leg.arrival, 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)) + const i = journeys[0].legs.length - 1 + const d = journeys[0].legs[i].destination + const name = `journeys[0].legs[${i}].destination` + t.equal(d.name, 'ATZE Musiktheater', name + '.name is invalid') + t.ok(isRoughlyEqual(.0001, d.latitude, latitude), name + '.latitude is invalid') + t.ok(isRoughlyEqual(.0001, d.longitude, longitude), name + '.longitude is invalid') t.end() })) @@ -183,50 +125,31 @@ test('Berlin Jungfernheide to ATZE Musiktheater', co(function* (t) { test('journeys: via works – with detour', co(function* (t) { // Going from Westhafen to Wedding via Württembergalle without detour // is currently impossible. We check if the routing engine computes a detour. - const westhafen = '008089116' - const wedding = '008089131' - const württembergallee = '731084' - const [journey] = yield client.journeys(westhafen, wedding, { + const journeys = yield client.journeys(westhafen, wedding, { via: württembergallee, results: 1, when, 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 === württembergallee)) - t.ok(l, 'Württembergalle is not being passed') - - 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, - when, - passedStations: true + const leg = journeys[0].legs.some((leg) => { + return leg.passed && leg.passed.some((passed) => { + return passed.station.id === württembergallee + }) }) - - 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.ok(leg, 'Württembergalle is not being passed') t.end() })) test('earlier/later journeys, Jungfernheide -> München Hbf', co(function* (t) { - const model = yield client.journeys(jungfernh, münchenHbf, { + const model = yield client.journeys(jungfernheide, münchenHbf, { results: 3, when }) + // todo: move to journeys validator? t.equal(typeof model.earlierRef, 'string') t.ok(model.earlierRef) t.equal(typeof model.laterRef, 'string') @@ -234,14 +157,18 @@ test('earlier/later journeys, Jungfernheide -> München Hbf', co(function* (t) { // when and earlierThan/laterThan should be mutually exclusive t.throws(() => { - client.journeys(jungfernh, münchenHbf, { + client.journeys(jungfernheide, münchenHbf, { when, earlierThan: model.earlierRef }) + // silence rejections, we're only interested in exceptions + .catch(() => {}) }) t.throws(() => { - client.journeys(jungfernh, münchenHbf, { + client.journeys(jungfernheide, münchenHbf, { when, laterThan: model.laterRef }) + // silence rejections, we're only interested in exceptions + .catch(() => {}) }) let earliestDep = Infinity, latestDep = -Infinity @@ -251,7 +178,7 @@ test('earlier/later journeys, Jungfernheide -> München Hbf', co(function* (t) { else if (dep > latestDep) latestDep = dep } - const earlier = yield client.journeys(jungfernh, münchenHbf, { + const earlier = yield client.journeys(jungfernheide, münchenHbf, { results: 3, // todo: single journey ref? earlierThan: model.earlierRef @@ -260,7 +187,7 @@ test('earlier/later journeys, Jungfernheide -> München Hbf', co(function* (t) { t.ok(new Date(j.legs[0].departure) < earliestDep) } - const later = yield client.journeys(jungfernh, münchenHbf, { + const later = yield client.journeys(jungfernheide, münchenHbf, { results: 3, // todo: single journey ref? laterThan: model.laterRef @@ -272,27 +199,42 @@ test('earlier/later journeys, Jungfernheide -> München Hbf', co(function* (t) { t.end() })) +test.skip('journey leg details', co(function* (t) { + const journeys = yield client.journeys(berlinHbf, münchenHbf, { + results: 1, 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}) + + validate(t, leg, 'journeyLeg', 'leg') + t.end() +})) + test('departures at Berlin Jungfernheide', co(function* (t) { - const deps = yield client.departures(jungfernh, { + const deps = yield client.departures(jungfernheide, { duration: 5, when }) - t.ok(Array.isArray(deps)) - for (let dep of deps) { - assertValidStation(t, dep.station) - if (!(yield findStation(dep.station.id))) { - console.error('unknown station', dep.station.id, dep.station.name) - } - assertValidWhen(t, dep.when, when) + validate(t, deps, 'departures', 'departures') + for (let i = 0; i < deps.length; i++) { + const dep = deps[i] + const name = `deps[${i}]` + // todo: make this pass + // t.equal(dep.station.id, jungfernheide, name + '.station.id is invalid') } + // todo: move into deps validator + t.deepEqual(deps, deps.sort((a, b) => t.when > b.when)) t.end() })) test('departures with station object', co(function* (t) { - yield client.departures({ + const deps = yield client.departures({ type: 'station', - id: jungfernh, + id: jungfernheide, name: 'Berlin Jungfernheide', location: { type: 'location', @@ -301,7 +243,7 @@ test('departures with station object', co(function* (t) { } }, {when}) - t.ok('did not fail') + validate(t, deps, 'departures', 'departures') t.end() })) @@ -314,18 +256,20 @@ test('nearby Berlin Jungfernheide', co(function* (t) { results: 2, distance: 400 }) - t.ok(Array.isArray(nearby)) + validate(t, nearby, 'locations', 'nearby') + t.equal(nearby.length, 2) - assertIsJungfernheide(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) - } + const s0 = nearby[0] + // todo: trim IDs + t.ok(s0.id === '008011167' || s0.id === jungfernheide) + t.equal(s0.name, 'Berlin Jungfernheide') + t.ok(isRoughlyEqual(s0.location.latitude, 52.530408, .0005)) + t.ok(isRoughlyEqual(s0.location.longitude, 13.299424, .0005)) + t.ok(s0.distance >= 0) + t.ok(s0.distance <= 100) + // todo: nearby[0] t.end() })) @@ -334,29 +278,21 @@ test('locations named Jungfernheide', co(function* (t) { results: 10 }) - t.ok(Array.isArray(locations)) - t.ok(locations.length > 0) + validate(t, locations, 'locations', 'locations') t.ok(locations.length <= 10) - - for (let l of locations) { - if (l.type === 'station') assertValidStation(t, l) - else assertValidLocation(t, l) - } - t.ok(locations.some(isJungfernheide)) + t.ok(locations.some((l) => { + // todo: trim IDs + return l.id === '008011167' || l.id === jungfernheide + }), 'Jungfernheide not found') t.end() })) test('location', co(function* (t) { - const loc = yield client.location(regensburgHbf) + const s = yield client.location(regensburgHbf) - assertValidStation(t, loc) - t.equal(loc.id, regensburgHbf) - - t.ok(Array.isArray(loc.lines)) - if (Array.isArray(loc.lines)) { - for (let line of loc.lines) assertValidLine(t, line) - } + validate(t, s, 'station', 'station') + t.equal(s.id, regensburgHbf) t.end() })) diff --git a/test/lib/validators.js b/test/lib/validators.js index afc76d1e..97e9f352 100644 --- a/test/lib/validators.js +++ b/test/lib/validators.js @@ -159,6 +159,16 @@ const createValidateJourneyLeg = (cfg) => { const msg = name + '.departure is invalid' a.ok(isValidWhen(leg.departure, cfg.when), msg) } + if (leg.arrivalPlatform !== null) { + 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') + } + if (leg.departurePlatform !== null) { + 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') @@ -168,6 +178,12 @@ const createValidateJourneyLeg = (cfg) => { validate('stopover', 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 } @@ -214,6 +230,8 @@ const createValidateDeparture = (cfg) => { } validate(['line'], 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 } @@ -231,6 +249,8 @@ const validateMovement = (validate, m, name = 'movement') => { // todo: let hafas-client add a .type field validate(['line'], v.line, name + '.line') + a.strictEqual(typeof v.direction, 'string', name + '.direction must be a string') + a.ok(v.direction, name + '.direction must not be empty') const lName = name + '.location' validate(['location'], v.location, lName) diff --git a/test/vbb.js b/test/vbb.js index 7995a066..ac194542 100644 --- a/test/vbb.js +++ b/test/vbb.js @@ -32,8 +32,6 @@ const cfg = { } const validateDirection = (dir, name) => { - a.strictEqual(typeof dir, 'string', name + ' must be a string') - a.ok(dir, name + ' must not be empty') if (!stations(dir, true, false)[0]) { console.error(name + `: station "${dir}" is unknown`) } @@ -333,6 +331,7 @@ test('departures', co(function* (t) { t.equal(dep.station.name, 'U Spichernstr.', name + '.station.name is invalid') t.equal(dep.station.id, spichernstr, name + '.station.id is invalid') } + // todo: move into deps validator t.deepEqual(deps, deps.sort((a, b) => t.when > b.when)) t.end()