diff --git a/package.json b/package.json index 7b55ba5e..7ff792c0 100644 --- a/package.json +++ b/package.json @@ -32,8 +32,16 @@ "query-string": "^5.0.0", "slugg": "^1.2.0" }, + "devDependencies": { + "db-stations": "^1.25.0", + "floordate": "^3.0.0", + "is-roughly-equal": "^0.1.0", + "tap-spec": "^4.1.1", + "tape": "^4.8.0", + "tape-promise": "^2.0.1" + }, "scripts": { - "test": "node -e \"require('.')\"", + "test": "node test/index.js | tap-spec", "prepublishOnly": "npm test" } } diff --git a/test/db.js b/test/db.js new file mode 100644 index 00000000..63bbd389 --- /dev/null +++ b/test/db.js @@ -0,0 +1,169 @@ +'use strict' + +const tapePromise = require('tape-promise').default +const tape = require('tape') +const isRoughlyEqual = require('is-roughly-equal') + +const createClient = require('..') +const { + findStation, + assertValidStation, + assertValidPoi, + assertValidAddress, + assertValidLocation, + assertValidLine, + assertValidStopover, + isJungfernheide, assertIsJungfernheide, + when, isValidWhen +} = require('./util.js') + +const test = tapePromise(tape) +const client = createClient(dbProfile) + +test('Berlin Jungfernheide to München Hbf', async (t) => { + const journeys = await client.journeys('8011167', '8000261', { + when, passedStations: true + }) + + t.ok(Array.isArray(journeys)) + t.ok(journeys.length > 0, 'no journeys') + for (let journey of journeys) { + assertValidStation(t, journey.origin) + if (!await findStation(journey.origin.id)) { + console.error('unknown station', journey.origin.id, journey.origin.name) + } + t.ok(isValidWhen(journey.departure)) + + assertValidStation(t, journey.destination) + if (!await findStation(journey.origin.id)) { + console.error('unknown station', journey.destination.id, journey.destination.name) + } + t.ok(isValidWhen(journey.arrival)) + + t.ok(Array.isArray(journey.parts)) + t.ok(journey.parts.length > 0, 'no parts') + const part = journey.parts[0] + + assertValidStation(t, part.origin) + if (!await findStation(part.origin.id)) { + console.error('unknown station', part.origin.id, part.origin.name) + } + t.ok(isValidWhen(part.departure)) + t.equal(typeof part.departurePlatform, 'string') + + assertValidStation(t, part.destination) + if (!await findStation(part.destination.id)) { + console.error('unknown station', part.destination.id, part.destination.name) + } + t.ok(isValidWhen(part.arrival)) + t.equal(typeof part.arrivalPlatform, 'string') + + assertValidLine(t, part.line) + + t.ok(Array.isArray(part.passed)) + for (let stopover of part.passed) assertValidStopover(t, stopover) + } + + t.end() +}) + +test('Berlin Jungfernheide to Torfstraße 17', async (t) => { + const journeys = await client.journeys('8011167', { + type: 'address', name: 'Torfstraße 17', + latitude: 52.5416823, longitude: 13.3491223 + }, {when}) + + t.ok(Array.isArray(journeys)) + t.ok(journeys.length >= 1, 'no journeys') + const journey = journeys[0] + const part = journey.parts[journey.parts.length - 1] + + assertValidStation(t, part.origin) + if (!await findStation(part.origin.id)) { + console.error('unknown station', part.origin.id, part.origin.name) + } + t.ok(isValidWhen(part.departure)) + t.ok(isValidWhen(part.arrival)) + + const d = part.destination + assertValidAddress(t, d) + t.equal(d.name, 'Torfstraße 17') + t.ok(isRoughlyEqual(.0001, d.coordinates.latitude, 52.5416823)) + t.ok(isRoughlyEqual(.0001, d.coordinates.longitude, 13.3491223)) + + t.end() +}) + +test('Berlin Jungfernheide to ATZE Musiktheater', async (t) => { + const journeys = await client.journeys('8011167', { + type: 'poi', name: 'ATZE Musiktheater', id: '991598902', + latitude: 52.542417, longitude: 13.350437 + }, {when}) + + t.ok(Array.isArray(journeys)) + t.ok(journeys.length >= 1, 'no journeys') + const journey = journeys[0] + const part = journey.parts[journey.parts.length - 1] + + assertValidStation(t, part.origin) + if (!await findStation(part.origin.id)) { + console.error('unknown station', part.origin.id, part.origin.name) + } + t.ok(isValidWhen(part.departure)) + t.ok(isValidWhen(part.arrival)) + + const d = part.destination + assertValidPoi(t, d) + t.equal(d.name, 'ATZE Musiktheater') + t.ok(isRoughlyEqual(.0001, d.coordinates.latitude, 52.542399)) + t.ok(isRoughlyEqual(.0001, d.coordinates.longitude, 13.350402)) + + t.end() +}) + +test('departures at Berlin Jungfernheide', async (t) => { + const deps = await client.departures('8011167', { + duration: 5, when + }) + + t.ok(Array.isArray(deps)) + for (let dep of deps) { + assertValidStation(t, dep.station) + if (!await findStation(dep.station.id)) { + console.error('unknown station', dep.station.id, dep.station.name) + } + t.ok(isValidWhen(dep.when)) + } + + t.end() +}) + +test('nearby Berlin Jungfernheide', async (t) => { + const nearby = await client.nearby(52.530273, 13.299433, { + results: 2, distance: 400 + }) + + t.ok(Array.isArray(nearby)) + t.equal(nearby.length, 2) + + assertIsJungfernheide(t, nearby[0]) + t.ok(nearby[0].distance >= 0) + t.ok(nearby[0].distance <= 100) + + t.end() +}) + +test('locations named Jungfernheide', async (t) => { + const locations = await client.locations('Jungfernheide', { + results: 10 + }) + + t.ok(Array.isArray(locations)) + t.ok(locations.length > 0) + t.ok(locations.length <= 10) + + for (let location of locations) assertValidLocation(t, location) + t.ok(locations.find(isJungfernheide)) + + t.end() +}) diff --git a/test/index.js b/test/index.js new file mode 100644 index 00000000..30db69dc --- /dev/null +++ b/test/index.js @@ -0,0 +1,3 @@ +'use strict' + +require('./db') diff --git a/test/util.js b/test/util.js new file mode 100644 index 00000000..6a7a7621 --- /dev/null +++ b/test/util.js @@ -0,0 +1,131 @@ +'use strict' + +const isRoughlyEqual = require('is-roughly-equal') +const getStations = require('db-stations').full +const floor = require('floordate') + +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 assertValidStation = (t, s) => { + t.equal(s.type, 'station') + t.equal(typeof s.id, 'string') +} + +const assertValidPoi = (t, p) => { + t.equal(p.type, 'poi') + t.equal(typeof p.id, 'string') +} + +const assertValidAddress = (t, a) => { + t.equal(a.type, 'address') +} + +const assertValidLocation = (t, l) => { + t.equal(typeof l.type, 'string') + if (l.type === 'station') assertValidStation(t, l) + else if (l.type === 'poi') assertValidPoi(t, l) + else if (l.type === 'address') assertValidAddress(t, l) + else t.fail('invalid type ' + l.type) + + t.equal(typeof s.name, 'string') + t.ok(s.coordinates) + t.equal(typeof s.coordinates.latitude, 'number') + t.equal(typeof s.coordinates.longitude, 'number') +} + +const isValidMode = (m) => { + return m === 'walking' || + m === 'train' || + m === 'bus' || + m === 'ferry' +} + +const assertValidLine = (t, l) => { + t.equal(l.type, 'line') + t.equal(typeof l.name, 'string') + t.ok(isValidMode(l.mode)) + t.equal(typeof l.product, 'string') +} + +const isValidDateTime = (w) => { + return !Number.isNaN(+new Date(w)) +} + +const assertValidStopover = (t, s) => { + if ('arrival' in s) t.ok(isValidDateTime(s.arrival)) + if ('departure' in s) t.ok(isValidDateTime(s.departure)) + if (!('arrival' in s) && !('departure' in s)) { + t.fail('stopover doesn\'t contain arrival or departure') + } + t.ok(s.station) + assertValidStation(t, s.station) +} + +const isJungfernheide = (s) => { + return s.type === 'station' && + s.id === '8011167' && + s.name === 'Berlin Jungfernheide' && + s.coordinates && + isRoughlyEqual(s.coordinates.latitude, 52.530408, .0005) && + isRoughlyEqual(s.coordinates.longitude, 13.299424, .0005) +} + +const assertIsJungfernheide = (t, s) => { + t.equal(s.type, 'station') + t.equal(s.id, '8011167') + t.equal(s.name, 'Berlin Jungfernheide') + t.ok(s.coordinates) + t.ok(isRoughlyEqual(s.coordinates.latitude, 52.530408, .0005)) + t.ok(isRoughlyEqual(s.coordinates.longitude, 13.299424, .0005)) +} + +const assertIsMünchenHbf = (s) => { + t.equal(s.type, 'station') + t.equal(s.id, '8000261') + t.equal(s.name, 'München Hbf') + t.ok(s.coordinates) + t.equal(s.coordinates.latitude, 48.140229) + t.equal(s.coordinates.longitude, 11.558339) +} + +const minute = 60 * 1000 +const hour = 60 * minute +const day = 24 * hour +const week = 7 * day + +// next Monday +const when = new Date(+floor(new Date(), 'week') + week + 10 * hour) +const isValidWhen = (w) => { + const ts = +new Date(w) + if (Number.isNaN(ts)) return false + return isRoughlyEqual(10 * hour, +when, ts) +} + +module.exports = { + findStation, + assertValidStation, + assertValidPoi, + assertValidAddress, + assertValidLocation, + isValidMode, + assertValidLine, + isValidDateTime, + assertValidStopover, + isJungfernheide, assertIsJungfernheide, + assertIsMünchenHbf, + when, isValidWhen +}