From 2e269448c6b99f780b7fc08d474e01cbe0ce7fa4 Mon Sep 17 00:00:00 2001 From: Hidde Wieringa Date: Sun, 14 Jul 2024 21:56:04 +0200 Subject: [PATCH] Use Hurl for tests --- .github/workflows/test.yml | 9 ++- api/tests/api.hurl | 119 +++++++++++++++++++++++++++++++++++++ api/tests/testcases.yml | 106 --------------------------------- api/tests/tests.py | 88 --------------------------- 4 files changed, 127 insertions(+), 195 deletions(-) create mode 100644 api/tests/api.hurl delete mode 100644 api/tests/testcases.yml delete mode 100644 api/tests/tests.py diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1ccdb5b2..a1f08f05 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,6 +15,13 @@ jobs: - 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 @@ -57,4 +64,4 @@ jobs: - name: Run API tests working-directory: api run: | - python3 tests/tests.py --api http://localhost:5000/api --tests tests/testcases.yml \ No newline at end of file + hurl --test --verbose --variable base_url=http://localhost:5000/api test/api.hurl diff --git a/api/tests/api.hurl b/api/tests/api.hurl new file mode 100644 index 00000000..41f72188 --- /dev/null +++ b/api/tests/api.hurl @@ -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 diff --git a/api/tests/testcases.yml b/api/tests/testcases.yml deleted file mode 100644 index 6fca8ba8..00000000 --- a/api/tests/testcases.yml +++ /dev/null @@ -1,106 +0,0 @@ -- name: Berlin - request: - path: "/facility" - params: - name: "Berlin" - response: - entries: - - name: "Berlin Südkreuz" - - name: "Berlin-Spandau" - - name: "Berlin Gesundbrunnen" - - name: "Berlin Ostbahnhof" - - name: "Berlin Hauptbahnhof" - - name: "Ostkreuz" - - name: "Berlin Potsdamer Platz" - - name: "Warschauer Straße" - - name: "Bornholmer Straße" - - name: "Baumschulenweg" - - name: "Schöneweide" - - name: "Alexanderplatz" - - name: "Gesundbrunnen" - - name: "Berlin Alexanderplatz" - - name: "Berlin Zoologischer Garten" - - name: "Zoologischer Garten" - - name: "Ostbahnhof" - - name: "Westkreuz" - - name: "Brandenburger Tor" - - name: "Berlin Friedrichstraße" -- name: result limit 5 - request: - path: "/facility" - params: - name: "Berlin" - limit: 5 - response: - count: 5 -- name: result limit 25 - request: - path: "/facility" - params: - name: "Berlin" - limit: 25 - response: - count: 25 -- name: "hyphen" - request: - path: "/facility" - params: - name: "Berlin-Spandau" - response: - entries: - - name: "Berlin-Spandau" - - name: "Spandau" - - name: "Berlin-Spandau Gbf" - - name: "Berlin-Spandau West" - - name: "Berlin-Spandau Mitte" - - name: "Berlin-Spandau Ost" - - name: "Berlin-Spandau Johannesstift" -- name: "space" - request: - path: "/facility" - params: - name: "Berlin Spandau" - response: - entries: - - name: "Berlin-Spandau" - - name: "Spandau" - - name: "Berlin-Spandau Gbf" - - name: "Berlin-Spandau West" - - name: "Berlin-Spandau Mitte" - - name: "Berlin-Spandau Ost" - - name: "Berlin-Spandau Johannesstift" -- name: Spandau - request: - path: "/facility" - params: - name: Spandau - response: - entries: - - name: "Berlin-Spandau" - - name: "Spandau" - - name: "Altstadt Spandau" - - name: "Rathaus Spandau" - - name: "Berlin-Spandau Gbf" - - name: "Berlin-Spandau West" - - name: "Berlin-Spandau Mitte" - - name: "Berlin-Spandau Ost" - - name: "Berlin-Spandau Johannesstift" -- name: "'Karl-Marx-Straße' should return one result" - request: - path: "/facility" - params: - name: Karl-Marx-Straße - response: - entries: - - name: "Karl-Marx-Straße" -- name: "2160 km 9.4" - request: - path: "/milestone" - params: - ref: 6020 - position: 22.7 - limit: 1 - response: - entries: - - ref: "6020" - position: 22.7 diff --git a/api/tests/tests.py b/api/tests/tests.py deleted file mode 100644 index 614f8e2e..00000000 --- a/api/tests/tests.py +++ /dev/null @@ -1,88 +0,0 @@ -import argparse -import re -import requests -import sys -from termcolor import colored -import traceback -import yaml - - -class TestCase(): - - def __init__(self, **kwargs): - self.name = kwargs["name"] - self.request = kwargs["request"] - self.response = kwargs["response"] - - def run(self, api): - path = self.request["path"] - url = f"{api}{path}" - r = requests.get(url, params=self.request.get("params")) - self.test_equals("status code", r.status_code, self.response.get("status", 200)) - response_data = r.json() - # Test result count - expected_count = self.response.get("count") - if expected_count is not None: - self.test_equals("result count", len(response_data), expected_count) - # Test result names - expected_results = self.response.get("entries") - if expected_results is not None: - self.test_equals("result_count", len(expected_results), len(response_data)) - match_type = self.response.get("match", "str_equals") - for i in range(0, len(expected_results)): - expected = expected_results[i] - got = response_data[i] - if type(expected) is not dict: - self.fail(f"Expected result must be a dict, not {type(expected)}: {expected}") - for key, expected_value in expected.items(): - if match_type == "str_equals": - self.test_str_equals(f"result['{i + 1}']['{key}']", expected_value, response_data[i][key]) - elif match_type == "regexp_full": - self.test_regexp_full(f"result['{i + 1}']['{key}']", expected_value, response_data[i][key]) - else: - self.fail(f"Unsupported comparison {match_type}") - self.passed() - - def test_is_type(self, obj, expected_type): - if type(obj) is not expected_type: - self.fail(f" {type(obj)} but {expected_type} was expected.") - - def test_regexp_full(self, what, regexp, got): - """Compare provided string with a regular expression. The expression must match the full string. - """ - if not re.fullmatch(regexp, got): - print(f"pattern: {regexp} string: {got}") - self.fail(f"{what}: {got} does not match regular expression '{regexp}'") - - def test_equals(self, what, expected, got): - if got != expected: - self.fail(f"{what}: {got} but {expected} was expected.") - - def test_str_equals(self, what, expected, got): - if str(got) != str(expected): - self.fail(f"{what}: {got} but {expected} was expected.") - - def print_traceback(self, color): - for line in traceback.format_stack(): - print(colored(line.strip(), color)) - - def fail(self, message): - text = f"ERROR: Test '{self.name}' failed.\n{message}" - print(colored(text, 'red')) - self.print_traceback('red') - sys.exit(1) - - def passed(self): - text = f"PASSED: {self.name}" - print(colored(text, 'green')) - - -parser = argparse.ArgumentParser(description="Run test queries against OpenRailwayMap API and compare results.") -parser.add_argument("-a", "--api", type=str, help="API endpoint", default="http://127.0.0.1:5000") -parser.add_argument("-t", "--tests", type=argparse.FileType("r"), help="Test definitions (YAML)") -args = parser.parse_args() - -cases = yaml.safe_load(args.tests) -cases = [TestCase(**e) for e in cases] -for case in cases: - case.run(args.api)