Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API tests #73

Merged
merged 19 commits into from
Jul 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
name: Test

on:
push: ~

jobs:
test:
name: Test
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Install Hurl
env:
VERSION: '4.3.0'
run: |
curl --location --remote-name https://github.com/Orange-OpenSource/hurl/releases/download/$VERSION/hurl_${VERSION}_amd64.deb
sudo apt update && sudo apt install ./hurl_${VERSION}_amd64.deb

- name: Get Date
id: get-date
shell: bash
run: |
echo "date=$(/bin/date -u "+%Y%m")" >> $GITHUB_OUTPUT

- name: Cache data
uses: actions/cache@v4
id: cache-data
with:
path: |
data/germany.osm.pbf
data/filtered/germany.osm.pbf
key: ${{ runner.os }}-data-${{ steps.get-date.outputs.date }}-berlin

- name: Download Germany
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
run: |
curl --location --fail --output data/berlin.osm.pbf https://download.geofabrik.de/europe/germany/berlin-latest.osm.pbf

- name: Import data
env:
OSM2PGSQL_DATAFILE: berlin.osm.pbf
run: |
docker compose run --build import import

# - name: Start tile server
# run: |
# docker compose up --build --wait martin

- name: Prepare and start API
run: |
api/prepare-api.sh
docker compose up --wait api

# - name: Start web server
# run: |
# docker compose up --build martin-proxy

- name: Run API tests
working-directory: api
run: |
hurl --test --verbose --variable base_url=http://localhost:5000/api tests/api.hurl
3 changes: 3 additions & 0 deletions api/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ HEALTHCHECK CMD ["pg_isready", "--host", "localhost", "--user", "postgres", "--d
FROM postgis/postgis:16-3.4-alpine as runtime

RUN apk add --no-cache \
curl \
python3 \
py3-pip \
py3-werkzeug \
Expand All @@ -33,4 +34,6 @@ ENV POSTGRES_HOST 127.0.0.1
ENV POSTGRES_HOST_AUTH_METHOD trust
ENV PGDATA /var/lib/postgresql/postgres-data

HEALTHCHECK CMD ["curl", "--fail", "localhost:5000/api/status"]

CMD ["/app/start.sh"]
20 changes: 12 additions & 8 deletions api/openrailwaymap_api/facility_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,13 @@ def __call__(self, args):
if search_arg in args and args[search_arg]:
search_args_count += 1
if search_args_count > 1:
self.data = {'type': 'multiple_query_args', 'error': 'More than one argument with a search term provided.', 'detail': 'Provide only one of the following arguments: {}'.format(', '.join(self.search_args))}
args = ', '.join(self.search_args)
self.data = {'type': 'multiple_query_args', 'error': 'More than one argument with a search term provided.', 'detail': f'Provide only one of the following arguments: {args}'}
self.status_code = 400
return self.build_response()
elif search_args_count == 0:
self.data = {'type': 'no_query_arg', 'error': 'No argument with a search term provided.', 'detail': 'Provide one of the following arguments: {}'.format(', '.join(self.search_args))}
args = ', '.join(self.search_args)
self.data = {'type': 'no_query_arg', 'error': 'No argument with a search term provided.', 'detail': f'Provide one of the following arguments: {args}'}
self.status_code = 400
return self.build_response()
if 'limit' in args:
Expand Down Expand Up @@ -69,7 +71,8 @@ def search_by_name(self, q):
data = []
# TODO support filtering on state of feature: abandoned, in construction, disused, preserved, etc.
# We do not sort the result although we use DISTINCT ON because osm_id is sufficient to sort out duplicates.
sql_query = """SELECT
fields = self.sql_select_fieldlist()
sql_query = f"""SELECT
{fields}, latitude, longitude, rank
FROM (
SELECT DISTINCT ON (osm_id)
Expand All @@ -82,7 +85,7 @@ def search_by_name(self, q):
) AS a
) AS b
ORDER BY rank DESC NULLS LAST
LIMIT %s;""".format(fields=self.sql_select_fieldlist())
LIMIT %s;"""
cursor.execute(sql_query, (q, q, self.limit))
results = cursor.fetchall()
for r in results:
Expand All @@ -93,11 +96,12 @@ def _search_by_ref(self, search_key, ref):
with self.db_conn.cursor() as cursor:
data = []
# We do not sort the result although we use DISTINCT ON because osm_id is sufficient to sort out duplicates.
sql_query = """SELECT DISTINCT ON (osm_id)
{}, ST_X(ST_Transform(geom, 4326)) AS latitude, ST_Y(ST_Transform(geom, 4326)) AS longitude
fields = self.sql_select_fieldlist()
sql_query = f"""SELECT DISTINCT ON (osm_id)
{fields}, ST_X(ST_Transform(geom, 4326)) AS latitude, ST_Y(ST_Transform(geom, 4326)) AS longitude
FROM openrailwaymap_ref
WHERE {} = %s
LIMIT %s;""".format(self.sql_select_fieldlist(), search_key)
WHERE {search_key} = %s
LIMIT %s;"""
cursor.execute(sql_query, (ref, self.limit))
results = cursor.fetchall()
for r in results:
Expand Down
12 changes: 7 additions & 5 deletions api/prepare_facilities.sql
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
-- SPDX-License-Identifier: GPL-2.0-or-later

CREATE TABLE IF NOT EXISTS openrailwaymap_ref AS
DROP TABLE IF EXISTS openrailwaymap_ref;
CREATE TABLE openrailwaymap_ref AS
-- TODO add all available fields / tags from object
SELECT
osm_id,
Expand All @@ -19,15 +20,16 @@ CREATE TABLE IF NOT EXISTS openrailwaymap_ref AS
-- TODO support other states as well
);

CREATE INDEX IF NOT EXISTS openrailwaymap_ref_railway_ref_idx
CREATE INDEX openrailwaymap_ref_railway_ref_idx
ON openrailwaymap_ref
USING BTREE(railway_ref);

CREATE INDEX IF NOT EXISTS openrailwaymap_ref_uic_ref_idx
CREATE INDEX openrailwaymap_ref_uic_ref_idx
ON openrailwaymap_ref
USING BTREE(uic_ref);

CREATE TABLE IF NOT EXISTS openrailwaymap_facilities_for_search AS
DROP TABLE IF EXISTS openrailwaymap_facilities_for_search;
CREATE TABLE openrailwaymap_facilities_for_search AS
-- TODO add all available fields / tags from object
SELECT
osm_id,
Expand Down Expand Up @@ -68,4 +70,4 @@ CREATE TABLE IF NOT EXISTS openrailwaymap_facilities_for_search AS
) AS organised
) AS duplicated;

CREATE INDEX IF NOT EXISTS openrailwaymap_facilities_name_index ON openrailwaymap_facilities_for_search USING gin(terms);
CREATE INDEX openrailwaymap_facilities_name_index ON openrailwaymap_facilities_for_search USING gin(terms);
14 changes: 8 additions & 6 deletions api/prepare_milestones.sql
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ BEGIN
END;
$$ LANGUAGE plpgsql;

CREATE TABLE IF NOT EXISTS openrailwaymap_milestones AS
DROP TABLE IF EXISTS openrailwaymap_milestones;
CREATE TABLE openrailwaymap_milestones AS
-- TODO add all available fields / tags from object
SELECT DISTINCT ON (osm_id) osm_id, position, precision, railway, name, ref, geom
FROM (
Expand Down Expand Up @@ -42,15 +43,16 @@ CREATE TABLE IF NOT EXISTS openrailwaymap_milestones AS
ORDER BY osm_id ASC, precision DESC
) AS duplicates_merged;

CREATE INDEX IF NOT EXISTS openrailwaymap_milestones_geom_idx
CREATE INDEX openrailwaymap_milestones_geom_idx
ON openrailwaymap_milestones
USING gist(geom);

CREATE INDEX IF NOT EXISTS openrailwaymap_milestones_position_idx
CREATE INDEX openrailwaymap_milestones_position_idx
ON openrailwaymap_milestones
USING gist(geom);

CREATE TABLE IF NOT EXISTS openrailwaymap_tracks_with_ref AS
DROP TABLE IF EXISTS openrailwaymap_tracks_with_ref;
CREATE TABLE openrailwaymap_tracks_with_ref AS
SELECT
osm_id,
railway,
Expand All @@ -64,10 +66,10 @@ CREATE TABLE IF NOT EXISTS openrailwaymap_tracks_with_ref AS
AND ref IS NOT NULL
AND osm_id > 0;

CREATE INDEX IF NOT EXISTS planet_osm_line_ref_geom_idx
CREATE INDEX planet_osm_line_ref_geom_idx
ON openrailwaymap_tracks_with_ref
USING gist(geom);

CREATE INDEX IF NOT EXISTS planet_osm_line_ref_idx
CREATE INDEX planet_osm_line_ref_idx
ON openrailwaymap_tracks_with_ref
USING btree(ref);
119 changes: 119 additions & 0 deletions api/tests/api.hurl
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# Facility request
GET {{base_url}}/facility
[QueryStringParams]
name: Berlin
HTTP 200
Content-Type: application/json
[Asserts]
jsonpath "$" count == 20
jsonpath "$[0].name" == "Berlin Südkreuz"
jsonpath "$[1].name" == "Berlin-Spandau"
jsonpath "$[2].name" == "Berlin Gesundbrunnen"
jsonpath "$[3].name" == "Berlin Ostbahnhof"
jsonpath "$[4].name" == "Berlin Hauptbahnhof"
jsonpath "$[5].name" == "Ostkreuz"
jsonpath "$[6].name" == "Berlin Potsdamer Platz"
jsonpath "$[7].name" == "Warschauer Straße"
jsonpath "$[8].name" == "Bornholmer Straße"
jsonpath "$[9].name" == "Baumschulenweg"
jsonpath "$[10].name" == "Schöneweide"
jsonpath "$[11].name" == "Alexanderplatz"
jsonpath "$[12].name" == "Gesundbrunnen"
jsonpath "$[13].name" == "Berlin Alexanderplatz"
jsonpath "$[14].name" == "Berlin Zoologischer Garten"
jsonpath "$[15].name" == "Zoologischer Garten"
jsonpath "$[16].name" == "Ostbahnhof"
jsonpath "$[17].name" == "Westkreuz"
jsonpath "$[18].name" == "Brandenburger Tor"
jsonpath "$[19].name" == "Berlin Friedrichstraße"

# Facility request with limit
GET {{base_url}}/facility
[QueryStringParams]
name: Berlin
limit: 5
HTTP 200
[Asserts]
jsonpath "$" count == 5

# Facility request with larger limit
GET {{base_url}}/facility
[QueryStringParams]
name: Berlin
limit: 25
HTTP 200
[Asserts]
jsonpath "$" count == 25

# TODO request with too large limit

# Facility request with hyphen
GET {{base_url}}/facility
[QueryStringParams]
name: Berlin-Spandau
HTTP 200
[Asserts]
jsonpath "$" count == 7
jsonpath "$[0].name" == "Berlin-Spandau"
jsonpath "$[1].name" == "Spandau"
jsonpath "$[2].name" == "Berlin-Spandau Gbf"
jsonpath "$[3].name" == "Berlin-Spandau West"
jsonpath "$[4].name" == "Berlin-Spandau Mitte"
jsonpath "$[5].name" == "Berlin-Spandau Ost"
jsonpath "$[6].name" == "Berlin-Spandau Johannesstift"

# Facility request with space
GET {{base_url}}/facility
[QueryStringParams]
name: Berlin Spandau
HTTP 200
[Asserts]
jsonpath "$" count == 7
jsonpath "$[0].name" == "Berlin-Spandau"
jsonpath "$[1].name" == "Spandau"
jsonpath "$[2].name" == "Berlin-Spandau Gbf"
jsonpath "$[3].name" == "Berlin-Spandau West"
jsonpath "$[4].name" == "Berlin-Spandau Mitte"
jsonpath "$[5].name" == "Berlin-Spandau Ost"
jsonpath "$[6].name" == "Berlin-Spandau Johannesstift"

# Facility request for Spandau
GET {{base_url}}/facility
[QueryStringParams]
name: Spandau
HTTP 200
[Asserts]
jsonpath "$" count == 9
jsonpath "$[0].name" == "Berlin-Spandau"
jsonpath "$[1].name" == "Spandau"
jsonpath "$[2].name" == "Altstadt Spandau"
jsonpath "$[3].name" == "Rathaus Spandau"
jsonpath "$[4].name" == "Berlin-Spandau Gbf"
jsonpath "$[5].name" == "Berlin-Spandau West"
jsonpath "$[6].name" == "Berlin-Spandau Mitte"
jsonpath "$[7].name" == "Berlin-Spandau Ost"
jsonpath "$[8].name" == "Berlin-Spandau Johannesstift"

# Facility request with with diacritics
GET {{base_url}}/facility
[QueryStringParams]
name: Karl-Marx-Straße
HTTP 200
[Asserts]
jsonpath "$" count == 1
jsonpath "$[0].name" == "Karl-Marx-Straße"

# TODO facility request with q query
# TODO facility request with UIC queries

# Milestone request for line 6020 milestone 22.7
GET {{base_url}}/milestone
[QueryStringParams]
ref: 6020
position: 22.7
limit: 1
HTTP 200
[Asserts]
jsonpath "$" count == 1
jsonpath "$[0].ref" == "6020"
jsonpath "$[0].position" == 22.7
Loading