diff --git a/.gitignore b/.gitignore index 610dbc178..7538833b2 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ docs/_build/ poetry.lock projects.cfg venv/ +.hypothesis/ diff --git a/annif/openapi/annif.yaml b/annif/openapi/annif.yaml index 7d39e1d1f..117faef9c 100644 --- a/annif/openapi/annif.yaml +++ b/annif/openapi/annif.yaml @@ -12,7 +12,7 @@ paths: summary: get basic information about the API service operationId: annif.rest.show_info responses: - 200: + "200": description: successful operation content: application/json: @@ -25,7 +25,7 @@ paths: summary: get a list of projects operationId: annif.rest.list_projects responses: - 200: + "200": description: successful operation content: application/json: @@ -40,13 +40,13 @@ paths: parameters: - $ref: '#/components/parameters/project_id' responses: - 200: + "200": description: successful operation content: application/json: schema: $ref: '#/components/schemas/Project' - 404: + "404": description: Project not found content: application/problem+json: @@ -89,25 +89,25 @@ paths: description: language of subject labels required: true responses: - 200: + "200": description: successful operation content: application/json: schema: $ref: '#/components/schemas/SuggestionResultList' - 400: + "400": description: Bad Request content: application/problem+json: schema: $ref: '#/components/schemas/Problem' - 404: + "404": description: Project not found content: application/problem+json: schema: $ref: '#/components/schemas/Problem' - 503: + "503": description: Service Unavailable content: application/problem+json: @@ -131,16 +131,16 @@ paths: $ref: '#/components/schemas/IndexedDocument' required: true responses: - 204: + "204": description: successful operation content: {} - 404: + "404": description: Project not found content: application/problem+json: schema: $ref: '#/components/schemas/Problem' - 503: + "503": description: Service Unavailable content: application/problem+json: @@ -193,10 +193,12 @@ components: is_trained: type: boolean example: true + nullable: true modification_time: type: string format: date-time example: 2020-05-19T16:42:42Z + nullable: true description: A project definition ProjectList: type: object diff --git a/pyproject.toml b/pyproject.toml index df6981341..4c7fb93a9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -70,6 +70,7 @@ flake8 = "*" bumpversion = "*" black = "23.*" isort = "*" +schemathesis = "3.18.*" [tool.poetry.extras] fasttext = ["fasttext-wheel"] diff --git a/tests/conftest.py b/tests/conftest.py index fcccb268f..ea53ba579 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -33,12 +33,6 @@ def app_with_initialize(): return app -@pytest.fixture -def app_client(app): - with app.test_client() as app_client: - yield app_client - - @pytest.fixture(scope="module") def registry(app): with app.app_context(): diff --git a/tests/projects.cfg b/tests/projects.cfg index f4c1a8bb2..5f7ce793c 100644 --- a/tests/projects.cfg +++ b/tests/projects.cfg @@ -72,6 +72,7 @@ name=Dummy with no backend language=en vocab=dummy analyzer=snowball(english) +access=private [noname] language=en diff --git a/tests/test_openapi.py b/tests/test_openapi.py new file mode 100644 index 000000000..e77230fd9 --- /dev/null +++ b/tests/test_openapi.py @@ -0,0 +1,17 @@ +"""Unit tests for Annif REST API / Swagger spec""" +import schemathesis +from hypothesis import settings + +schema = schemathesis.from_path("annif/openapi/annif.yaml") + + +@schemathesis.check +def check_cors(response, case): + assert response.headers["access-control-allow-origin"] == "*" + + +@schema.parametrize() +@settings(max_examples=10) +def test_api(case, app): + response = case.call_wsgi(app) + case.validate_response(response, additional_checks=(check_cors,)) diff --git a/tests/test_swagger.py b/tests/test_swagger.py deleted file mode 100644 index 037d17195..000000000 --- a/tests/test_swagger.py +++ /dev/null @@ -1,74 +0,0 @@ -"""Unit tests for Annif REST API / Swagger spec""" - - -def test_swagger_cors(app_client): - # test that the service supports CORS - req = app_client.get("http://localhost:8000/v1/projects") - assert req.headers["access-control-allow-origin"] == "*" - - -def test_swagger_list_projects(app_client): - req = app_client.get("http://localhost:8000/v1/projects") - assert req.status_code == 200 - assert "projects" in req.get_json() - - -def test_swagger_show_project(app_client): - req = app_client.get("http://localhost:8000/v1/projects/dummy-fi") - assert req.status_code == 200 - assert req.get_json()["project_id"] == "dummy-fi" - - -def test_swagger_show_project_nonexistent(app_client): - req = app_client.get("http://localhost:8000/v1/projects/nonexistent") - assert req.status_code == 404 - - -def test_swagger_suggest(app_client): - data = {"text": "example text"} - req = app_client.post( - "http://localhost:8000/v1/projects/dummy-fi/suggest", data=data - ) - assert req.status_code == 200 - assert "results" in req.get_json() - - -def test_swagger_suggest_nonexistent(app_client): - data = {"text": "example text"} - req = app_client.post( - "http://localhost:8000/v1/projects/nonexistent/suggest", data=data - ) - assert req.status_code == 404 - - -def test_swagger_suggest_novocab(app_client): - data = {"text": "example text"} - req = app_client.post( - "http://localhost:8000/v1/projects/novocab/suggest", data=data - ) - assert req.status_code == 503 - - -def test_swagger_learn(app_client): - data = [ - { - "text": "the quick brown fox", - "subjects": [{"uri": "http://example.org/fox", "label": "fox"}], - } - ] - req = app_client.post("http://localhost:8000/v1/projects/dummy-fi/learn", json=data) - assert req.status_code == 204 - - -def test_swagger_learn_nonexistent(app_client): - data = [] - req = app_client.post( - "http://localhost:8000/v1/projects/nonexistent/learn", json=data - ) - assert req.status_code == 404 - - -def test_swagger_learn_novocab(app_client): - data = [] - req = app_client.post("http://localhost:8000/v1/projects/novocab/learn", json=data) - assert req.status_code == 503