Compare commits

...

43 commits
6.0.0 ... 6

Author SHA1 Message Date
renovate[bot]
f0901ed56c
chore(deps): lock file maintenance
Some checks failed
lint, build & test / lint, build & test (push) Has been cancelled
2024-09-23 02:05:06 +00:00
renovate[bot]
cf63dff36b
chore(deps): lock file maintenance 2024-09-16 01:38:20 +00:00
renovate[bot]
be8aee3fbf
chore(deps): lock file maintenance 2024-09-09 01:07:28 +00:00
renovate[bot]
58c18827ca
chore(deps): lock file maintenance 2024-09-02 00:14:38 +00:00
renovate[bot]
5fffdb88d2
chore(deps): lock file maintenance 2024-08-26 01:25:54 +00:00
renovate[bot]
4517d41e2b chore(deps): update dependency axios to v1.7.4 [security] 2024-08-14 01:34:29 +02:00
renovate[bot]
0d92cb7ed0 chore(deps): update actions/checkout action to v4 2024-08-08 16:11:32 +02:00
renovate[bot]
3e5dc8e733 chore(deps): update docker/login-action action to v3 2024-08-08 16:09:57 +02:00
renovate[bot]
4be921d5dd chore(deps): update docker/setup-buildx-action action to v3 2024-08-08 16:08:57 +02:00
renovate[bot]
399713695e chore(deps): lock file maintenance 2024-08-08 14:02:07 +00:00
Jannis R
66ca7d32e5
CI: lint & test on PRs too, with Node 18-22 💚 2024-08-08 16:01:45 +02:00
Jannis R
76358235ab
readme: use "docker.io" in image names; 6.0.5 2024-05-29 17:23:47 +02:00
Jannis R
6a94c19dc8
minor-upgrade deps & dev deps 2024-05-29 17:22:42 +02:00
dependabot[bot]
291006c81e Bump express from 4.18.2 to 4.19.2
Bumps [express](https://github.com/expressjs/express) from 4.18.2 to 4.19.2.
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/master/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.18.2...4.19.2)

---
updated-dependencies:
- dependency-name: express
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-06 14:31:57 +02:00
Johannes
3941e5f4ea
readme: explain differences in realtime data between DB app and db-rest 📝
Co-Authored-By: Jannis R <mail@jannisr.de>
2024-03-19 13:41:27 +01:00
dependabot[bot]
e8b040e601 Bump follow-redirects from 1.15.5 to 1.15.6
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.5 to 1.15.6.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.5...v1.15.6)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-17 16:06:48 +01:00
Jannis R
d53ff22d53
readme: mention $REDIS_URL 📝 2024-01-26 17:38:20 +01:00
Jannis R
a1050c9d90
minor-upgrade deps & dev deps; 6.0.4 2024-01-25 14:51:00 +01:00
Jannis R
40ae288388
minor tweaks 📝 2024-01-25 14:49:59 +01:00
Jannis R
9793b471f2
CI: publish with "floating" Docker image tag too 💚 2024-01-25 14:49:41 +01:00
Jannis R
8de11ee2cd
API docs: minor fix 📝 2024-01-25 14:48:16 +01:00
Jannis R
a294db5cd4
add files and bin to package.json
Co-Authored-By: Marie Ramlow <me@nycode.dev>
2023-11-24 16:19:55 +01:00
Jannis R
1eff75e491
axios@1
closes #36
2023-11-11 14:13:43 +01:00
Jannis R
f0c0cc698b
renovate bot config: temporarily remove schedule 💚
https://github.com/derhuerst/db-rest/pull/34#issuecomment-1801918396
2023-11-08 17:07:12 +01:00
Jannis R
c087d49f16
6.0.3 2023-11-07 19:08:51 +01:00
Jannis R
8482862f76
/journeys docs: fix POI example 📝
fixes https://github.com/public-transport/hafas-client/issues/299
2023-11-07 18:56:32 +01:00
Jannis R
3fe3d4ac7b
tap-min@3 2023-11-07 18:54:43 +01:00
Jannis R
5b4a97a93c
minor-upgrade deps & dev deps 2023-11-07 18:53:49 +01:00
Jannis R
d1be6cb7af
package-lock.json: update db-rest version field 2023-11-07 18:52:32 +01:00
Jannis R
2339f1752a
CI: build & publish arm64 binaries 💚
v5 equivalent: 76c5885
2023-09-19 11:01:14 +02:00
Jannis R
11aa62d829
6.0.2 2023-09-06 16:48:26 +02:00
Jannis R
c34f218373
check in package-lock.json, adapt Dockerfile & CI config 💚 2023-09-06 14:41:51 +02:00
Marie
e038ce37de
configure renovate bot
#34 squashed
2023-09-06 14:38:00 +02:00
Jannis R
30a56869ce
docs: fix status page link/badge, other tweaks 📝; 6.0.1
db-rest equivalent of fb08b213ed
2023-05-08 14:58:13 +02:00
Jannis R
303544277b
CI: upgrade actions 💚
db-rest equivalent of 9a67cc00ad
2023-04-20 00:34:11 +02:00
Jannis R
a5688cabe6
CI: cache Docker layers 💚
db-rest equivalent of c8a06f0c6d
2023-04-20 00:33:19 +02:00
Jannis R
63964f91f3
docs: embed GoatCounter visits counter
db-rest equivalent of 5b1ade80ed
2023-04-20 00:17:51 +02:00
Jannis R
9879365e9b
use JS build script
db-rest equivalent of 605085229e
2023-04-20 00:14:23 +02:00
Jannis R
ba256397d0
CI: fix Docker build & push 💚 2023-03-26 19:31:18 +02:00
Jannis R
1047946a23
CI: minor tweaks 💚 2023-03-26 00:00:01 +01:00
Jannis R
5658c84c54
CI: build before testing 💚 2023-03-26 00:00:01 +01:00
jannes.t13@gmail.com
448294ba5c add travellers age parameter
Co-Authored-By: Jannis R <mail@jannisr.de>
2023-03-25 23:59:24 +01:00
Jannis R
779e67886b
adapt tests to latest data 2022-12-28 14:22:28 +01:00
14 changed files with 6456 additions and 73 deletions

13
.github/renovate.json vendored Normal file
View file

@ -0,0 +1,13 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended"
],
"configMigration": true,
"lockFileMaintenance": {
"enabled": true,
"automerge": true,
"automergeType": "branch",
"ignoreTests": true
}
}

View file

@ -5,18 +5,8 @@ on:
- 6
jobs:
lint-test:
name: lint & test
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v2
- name: setup Node
uses: actions/setup-node@v1
with:
node-version: 18.x
- run: npm install
- run: npm run lint
- run: npm test
name: lint, build & test
uses: './.github/workflows/lint-test.yml'
build-and-publish:
name: build & publish Docker image
@ -24,23 +14,32 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: check out the repo
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: set up QEMU
uses: docker/setup-qemu-action@v3
- name: configure Docker to use buildx
uses: docker/setup-buildx-action@v3
- name: log in to Docker Hub
uses: docker/login-action@v1
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
- name: build Docker image & push to Docker Hub
uses: docker/build-push-action@v2
uses: docker/build-push-action@v4
with:
context: .
push: true
platforms: linux/amd64,linux/arm64
tags: |
derhuerst/db-rest:6
derhuerst/db-rest:latest
# https://docs.docker.com/build/ci/github-actions/examples/#github-cache
cache-from: type=gha
cache-to: type=gha,mode=max,oci-mediatypes=true,compression=zstd
# this is for the public-transport/infrastructure cluster
- name: log in to GitHub Container Registry
uses: docker/login-action@v1
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
@ -52,7 +51,14 @@ jobs:
id: datetime
run: echo "::set-output name=datetime::$(date -u +'%Y-%m-%dT%H.%M.%SZ')"
- name: push Docker image to GitHub Registry
uses: docker/build-push-action@v2
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: ghcr.io/${{github.repository}}:v6_${{steps.hash.outputs.hash}}_${{steps.datetime.outputs.datetime}}
platforms: linux/amd64,linux/arm64
tags: |
ghcr.io/${{github.repository}}:v6
ghcr.io/${{github.repository}}:v6_${{steps.hash.outputs.hash}}_${{steps.datetime.outputs.datetime}}
# https://docs.docker.com/build/ci/github-actions/examples/#github-cache
cache-from: type=gha
cache-to: type=gha,mode=max,oci-mediatypes=true,compression=zstd

35
.github/workflows/lint-test.yml vendored Normal file
View file

@ -0,0 +1,35 @@
name: lint, build & test
on:
push:
branches:
- '*'
pull_request:
branches:
- '*'
workflow_call:
jobs:
lint-test:
name: lint, build & test
runs-on: ubuntu-latest
strategy:
matrix:
node-version:
- '16.x'
- '18.x'
- '20.x'
- '22.x'
steps:
- name: checkout
uses: actions/checkout@v4
- name: setup Node
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm run lint
- run: npm run build
- run: npm test

1
.gitignore vendored
View file

@ -5,7 +5,6 @@ Thumbs.db
node_modules
npm-debug.log
/package-lock.json
/dump.rdb
/docs/*.html

View file

@ -3,8 +3,8 @@ WORKDIR /app
# install dependencies
RUN apk add --update git bash
ADD package.json /app
RUN npm install
ADD package.json package-lock.json /app
RUN npm ci
# build documentation
ADD . /app

9
api.js
View file

@ -12,7 +12,7 @@ import Redis from 'ioredis'
import {createCachedHafasClient} from 'cached-hafas-client'
import {createRedisStore} from 'cached-hafas-client/stores/redis.js'
import serveStatic from 'serve-static'
import {parseBoolean} from 'hafas-rest-api/lib/parse.js'
import {parseBoolean, parseInteger} from 'hafas-rest-api/lib/parse.js'
import {loyaltyCardParser} from './lib/loyalty-cards.js'
import {route as stations} from './routes/stations.js'
import {route as station} from './routes/station.js'
@ -24,6 +24,7 @@ const docsRoot = pathJoin(__dirname, 'docs')
const berlinHbf = '8011160'
// todo: use process.env.HAFAS_USER_AGENT if defined
let hafas = createHafas(pkg.name)
let healthCheck = createHealthCheck(hafas, berlinHbf)
@ -60,6 +61,12 @@ const mapRouteParsers = (route, parsers) => {
default: 'false',
parse: parseBoolean,
},
age: {
description: 'Age of traveller',
type: 'integer',
defaultStr: '*adult*',
parse: parseInteger
},
}
}

View file

@ -1,5 +1,5 @@
import {generateApiDocs} from 'hafas-rest-api/tools/generate-docs.js'
import {api} from './api.js'
import {api} from '../api.js'
const HEAD = `\
# \`v6.db.transport.rest\` API documentation
@ -61,7 +61,7 @@ Uses [\`hafasClient.journeys()\`](https://github.com/public-transport/hafas-clie
\`from\` (A), \`to\` (B), and the optional \`via\` must each have one of these formats:
- as stop/station ID (e.g. \`from=8010159\` for *Halle (Saale) Hbf*)
- as a POI (e.g. \`from.id=991561765&from.latitude=51.48364&from.longitude=11.98084\` for *Halle+(Saale),+Stadtpark+Halle+(Grünanlagen)*)
- as a POI (e.g. \`from.id=991561765&from.latitude=51.48364&from.longitude=11.98084&from.name=Halle+(Saale),+Stadtpark+Halle+(Grünanlagen)\` for *Halle (Saale), Stadtpark Halle (Grünanlagen)*)
- as an address (e.g. \`from.latitude=51.25639&from.longitude=7.46685&from.address=Hansestadt+Breckerfeld,+Hansering+3\` for *Hansestadt Breckerfeld, Hansering 3*)
### Pagination
@ -416,40 +416,42 @@ curl "https://v6.db.transport.rest/radar?$bbox&results=10" -s | jq
`,
}
const {
listOfRoutes,
routes,
tail,
} = generateApiDocs(api.routes)
const generateMarkdownApiDocs = async function* () {
const {
listOfRoutes,
routes,
tail,
} = generateApiDocs(api.routes)
const sortedRoutes = Object.entries(routes)
.sort(([routeA], [routeB]) => {
const iA = order.indexOf(routeA)
const iB = order.indexOf(routeB)
if (iA >= 0 && iB >= 0) return iA - iB
if (iA < 0 && iB >= 0) return 1
if (iB < 0 && iA >= 0) return -1
return 0
})
const sortedRoutes = Object.entries(routes)
.sort(([routeA], [routeB]) => {
const iA = order.indexOf(routeA)
const iB = order.indexOf(routeB)
if (iA >= 0 && iB >= 0) return iA - iB
if (iA < 0 && iB >= 0) return 1
if (iB < 0 && iA >= 0) return -1
return 0
})
const write = process.stdout.write.bind(process.stdout)
yield HEAD
yield `\n\n`
write(HEAD)
write(`\n\n`)
yield listOfRoutes
yield `\n\n`
write(listOfRoutes)
write(`\n\n`)
for (const [route, params] of sortedRoutes) {
write(`## \`GET ${route}\`\n\n`)
write(descriptions[route] || '')
write(`
### Query Parameters
`)
write(params)
if (examples[route]) {
write('\n' + examples[route])
for (const [route, params] of sortedRoutes) {
yield `## \`GET ${route}\`\n\n`
yield descriptions[route] || ''
yield `\n### Query Parameters\n`
yield params
if (examples[route]) {
yield '\n' + examples[route]
}
yield `\n\n`
}
write(`\n\n`)
yield tail
}
write(tail)
export {
generateMarkdownApiDocs,
}

86
build/index.js Executable file
View file

@ -0,0 +1,86 @@
#!/usr/bin/env node
// todo: use import assertions once they're supported by Node.js & ESLint
// https://github.com/tc39/proposal-import-assertions
import {createRequire} from 'module'
const require = createRequire(import.meta.url)
import {dirname, join} from 'node:path'
import {pipeline} from 'node:stream/promises'
import {createReadStream, createWriteStream} from 'node:fs'
import {copyFile} from 'node:fs/promises'
import _technicalDocsCli from '@derhuerst/technical-docs-cli'
import {config} from '../api.js'
const {
createMarkdownRenderer,
determineSyntaxStylesheetPath,
} = _technicalDocsCli
import {generateMarkdownApiDocs} from './api-docs.js'
const pkg = require('../package.json')
const BASE_URL = new URL('..', import.meta.url).href
const API_DOCS_DEST = 'docs/api.md'
const DOCS_TO_RENDER = [
['docs/readme.md', 'docs/index.html'],
['docs/getting-started.md', 'docs/getting-started.html'],
['docs/api.md', 'docs/api.html'],
]
const SYNTAX_STYLESHEET_URL = '/syntax.css'
const SYNTAX_STYLESHEET_SRC = determineSyntaxStylesheetPath('github')
const SYNTAX_STYLESHEET_DEST = 'docs/syntax.css'
{
console.info('writing Markdown API docs to ' + API_DOCS_DEST)
const destPath = new URL(API_DOCS_DEST, BASE_URL).pathname
await pipeline(
generateMarkdownApiDocs(),
createWriteStream(destPath),
)
}
const markdownRenderingCfg = {
syntaxStylesheetUrl: SYNTAX_STYLESHEET_URL,
additionalHeadChildren: (h) => {
if (!pkg.goatCounterUrl) return []
return [
// https://72afc0822cce0642af90.goatcounter.com/help/skip-dev#skip-loading-staging-beta-sites-312
h('script', `
if (window.location.host !== ${JSON.stringify(config.hostname)}) {
window.goatcounter = {no_onload: true}
}
`),
// https://72afc0822cce0642af90.goatcounter.com/help/start
h('script', {
src: '//gc.zgo.at/count.js',
async: true,
'data-goatcounter': pkg.goatCounterUrl,
}),
]
},
}
for (const [src, dest] of DOCS_TO_RENDER) {
console.info(`rendering Markdown file ${src} to HTML file ${dest}`)
const srcPath = new URL(src, BASE_URL).pathname
const destPath = new URL(dest, BASE_URL).pathname
// unfortunately, we can't use stream.pipeline right now
// see https://github.com/unifiedjs/unified-stream/issues/1
await new Promise((resolve, reject) => {
createReadStream(srcPath)
.once('error', reject)
.pipe(createMarkdownRenderer(markdownRenderingCfg))
.once('error', reject)
.pipe(createWriteStream(destPath))
.once('error', reject)
.once('finish', resolve)
})
}
{
const srcPath = SYNTAX_STYLESHEET_SRC
const destPath = new URL(SYNTAX_STYLESHEET_DEST, BASE_URL).pathname
console.info(`copying syntax stylesheet from ${srcPath} to ${destPath}`)
await copyFile(srcPath, destPath)
}

View file

@ -2,23 +2,27 @@
[`v6.db.transport.rest`](https://v6.db.transport.rest/) is a [REST API](https://restfulapi.net) for the public transportation system in Germany.
[![API status](https://badgen.net/uptime-robot/status/m784879516-8a977fa91b975fc3884a9857)](https://stats.uptimerobot.com/57wNLs39M/784879516)
[![API status](https://badgen.net/uptime-robot/status/m793274556-25c5e9bbab0297d91cda7134)](https://stats.uptimerobot.com/57wNLs39M/793274556)
Because it wraps [an API](https://github.com/public-transport/hafas-client/blob/6/readme.md#background) of [Deutsche Bahn](https://de.wikipedia.org/wiki/Deutsche_Bahn), it **includes most of the long-distance and regional traffic, as well as some international trains and local buses**. Essentially, it returns whatever data the [*DB Navigator* app](https://www.bahn.de/p/view/service/mobile/db-navigator.shtml) shows, **including realtime delays and disruptions**.
Because it wraps [an API](https://github.com/public-transport/hafas-client/blob/6/readme.md#background) of [Deutsche Bahn](https://de.wikipedia.org/wiki/Deutsche_Bahn), it **includes most of the long-distance and regional traffic, as well as some international trains and local buses**. Essentially, it returns whatever data the [*DB Navigator* app](https://www.bahn.de/p/view/service/mobile/db-navigator.shtml) shows*, **including realtime delays and disruptions**.
*When comparing results from this API to what the DB Navigator app shows there might be occasional differences due to them utilizing different, not 100% identical backends.*
- [Getting Started](getting-started.md)
- [API documentation](api.md)
- [OpenAPI playground](https://petstore.swagger.io/?url=https%3A%2F%2Fv6.db.transport.rest%2F.well-known%2Fservice-desc%0A)
- [API documentation](api.md) (run `npm run build` to generate)
- [OpenAPI playground with API documentation](https://petstore.swagger.io/?url=https%3A%2F%2Fv6.db.transport.rest%2F.well-known%2Fservice-desc%0A)
## Why use this API?
### Realtime Data
This API returns realtime data whenever its upstream, the [API for DB's mobile app](https://github.com/public-transport/hafas-client/blob/6/p/db/readme.md), provides it.
This API returns realtime data whenever it is upstream. The [API for DB's mobile app](https://github.com/public-transport/hafas-client/blob/6/p/db/readme.md) provides it.
*Note: Different endpoints might remove realtime data like delays and cancellations at different times, i.e. after a journey's arrival.*
### No API Key
You can just use the API without authentication. There's a [rate limit](https://apisyouwonthate.com/blog/what-is-api-rate-limiting-all-about) of 100 requests/minute (burst 150 requests/minute) set up.
You can just use the API without authentication. There's a [rate limit](https://apisyouwonthate.com/blog/what-is-api-rate-limiting-all-about) of 100 requests/minute set up.
### CORS

View file

@ -1,4 +1,4 @@
Copyright (c) 2022, Jannis R
Copyright (c) 2024, Jannis R
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.

6216
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -2,9 +2,19 @@
"private": true,
"name": "db-rest",
"description": "A clean REST API wrapping around the Deutsche Bahn API.",
"version": "6.0.0",
"version": "6.0.5",
"type": "module",
"bin": {
"db-rest": "./index.js"
},
"main": "index.js",
"files": [
"docs",
"lib",
"routes",
"api.js",
"index.js"
],
"author": "Jannis R <mail@jannisr.de>",
"homepage": "https://github.com/derhuerst/db-rest/tree/6",
"repository": "derhuerst/db-rest",
@ -36,20 +46,20 @@
"serve-static": "^1.14.1"
},
"devDependencies": {
"@derhuerst/technical-docs-cli": "^1.1.0",
"axios": "~1.1",
"@derhuerst/technical-docs-cli": "^1.5.0",
"axios": "^1.6.1",
"eslint": "^8.12.0",
"get-port": "^6.1.2",
"ndjson": "^2.0.0",
"pino-pretty": "^9.1.1",
"tap-min": "^2.0.0",
"tap-min": "^3.0.0",
"tape": "^5.5.2"
},
"scripts": {
"docs": "node api-docs.js >docs/api.md && build-technical-doc --syntax-stylesheet-url /syntax.css <docs/readme.md >docs/index.html && build-technical-doc --syntax-stylesheet-url /syntax.css <docs/getting-started.md >docs/getting-started.html && build-technical-doc --syntax-stylesheet-url /syntax.css <docs/api.md >docs/api.html && build-technical-doc --syntax-stylesheet github >docs/syntax.css",
"build": "npm run docs",
"build": "REDIS_URL='' ./build/index.js",
"start": "node index.js",
"lint": "eslint .",
"test": "node test/index.js | tap-min"
}
},
"goatCounterUrl": "https://37462fdee48390778258.goatcounter.com/count"
}

View file

@ -15,14 +15,16 @@
## installing & running
`db-rest` expects a [Redis](https://redis.io/) server running on `127.0.0.1:6379` (default port), but you can set the `REDIS_URL` environment variable to change this.
### access to Redis
It is recommended that you let `bvg-rest` cache HAFAS responses within a [Redis](https://redis.io/) cache. To use this feature, set `$REDIS_URL` (e.g. to `redis://localhost:6379/1` when running Redis locally).
### via Docker
A Docker image [is available as `derhuerst/db-rest:6`](https://hub.docker.com/r/derhuerst/db-rest:6).
A Docker image [is available as `docker.io/derhuerst/db-rest:6`](https://hub.docker.com/r/docker.io/derhuerst/db-rest:6).
```shell
docker run -d -p 3000:3000 derhuerst/db-rest:6
docker run -d -p 3000:3000 docker.io/derhuerst/db-rest:6
```
*Note:* The Docker image does not contain the Redis server.
@ -34,6 +36,8 @@ git clone https://github.com/derhuerst/db-rest.git
cd db-rest
git checkout 6
npm install
export HOSTNAME='my-vbb-rest-api.example.org'
npm run build
redis-server &
@ -45,6 +49,7 @@ To keep the API running permanently, use tools like [`forever`](https://github.c
## Related Projects
- [`DB-Adapter-v6`](https://github.com/olech2412/DB-Adapter-v6) A Java API client for `db-rest`.
- [`vbb-rest`](https://github.com/derhuerst/vbb-rest)  A clean REST API wrapping around the VBB API.
- [`bvg-rest`](https://github.com/derhuerst/bvg-rest)  A clean REST API wrapping around the BVG API.
- [`hvv-rest`](https://github.com/derhuerst/hvv-rest)  A clean REST API wrapping around the HVV API.

View file

@ -105,6 +105,6 @@ tape.test('/stations?query=frankfurt%20ha works', async (t) => {
for await (const station of parser) stations.push(station)
t.ok(stations.find(s => s.id === FRANKFURT_MAIN_HBF))
t.ok(Object.keys(stations).length > 0)
t.ok(stations.length > 0)
}
})