From b39983df98e7f84a13fe2edffab944b86613c5b3 Mon Sep 17 00:00:00 2001 From: Francesco Bartoli Date: Sun, 3 Mar 2024 21:32:00 +0100 Subject: [PATCH] Add schemathesis tests to respect the OpenAPI contract --- .github/workflows/contract-tests.yml | 70 ++++++++++++++++++++++++++++ tests/conftest.py | 51 ++++++++++++++++++++ tests/test_cli.py | 6 --- tests/test_openapi_contract.py | 11 +++++ 4 files changed, 132 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/contract-tests.yml create mode 100644 tests/conftest.py create mode 100644 tests/test_openapi_contract.py diff --git a/.github/workflows/contract-tests.yml b/.github/workflows/contract-tests.yml new file mode 100644 index 0000000..2bdb26d --- /dev/null +++ b/.github/workflows/contract-tests.yml @@ -0,0 +1,70 @@ +name: Contract Tests + +on: + - push + - pull_request + +jobs: + contract-tests: + name: Validate and test the contract of fastgeoapi against the generated pygeoapi OpenAPI document + runs-on: ubuntu-latest + # strategy: + # fail-fast: false + # matrix: + # include: + # - { python: "3.10", os: "ubuntu-20.04", session: "contractests" } + # - { python: "3.9", os: "ubuntu-20.04", session: "contractests" } + + # env: + # NOXSESSION: ${{ matrix.session }} + # FORCE_COLOR: "1" + + steps: + - name: Check out the repository + uses: actions/checkout@v4.1.1 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5.0.0 + with: + python-version: "3.10" + + - name: Upgrade pip + run: | + pip install --constraint=.github/workflows/constraints.txt pip + pip --version + + - name: Upgrade pip in virtual environments + shell: python + run: | + import os + import pip + + with open(os.environ["GITHUB_ENV"], mode="a") as io: + print(f"VIRTUALENV_PIP={pip.__version__}", file=io) + + - name: Install Poetry + run: | + pipx install --pip-args=--constraint=.github/workflows/constraints.txt poetry + poetry --version + + # - name: Install Nox + # run: | + # pipx install --pip-args=--constraint=.github/workflows/constraints.txt nox + # pipx inject --pip-args=--constraint=.github/workflows/constraints.txt nox nox-poetry + # nox --version + + - name: Install GDAL + run: | + sudo apt-add-repository ppa:ubuntugis/ubuntugis-unstable + sudo apt-get update + sudo apt-get install python-numpy gdal-bin libgdal-dev + + # - name: Run Nox + # run: | + # nox --force-color --python=${{ matrix.python }} + + - name: Install fastgeoapi and run schemathesis tests + run: | + poetry install + poetry run uvicorn app.main:app --host 0.0.0.0 --port 6000 --reload --loop asyncio + poetry run st run --checks all "http://localhost:6000/openapi?f=json" diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..1ebdeb2 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,51 @@ +import os +import pytest +import schemathesis +import sys +from typer.testing import CliRunner +from unittest import mock + + +@pytest.fixture +def runner() -> CliRunner: + """Fixture for invoking command-line interfaces.""" + return CliRunner() + +def reload_app(): + if "app.main" in sys.modules: + del sys.modules["app.main"] + if "app.config.app" in sys.modules: + del sys.modules["app.config.app"] + from app.main import app + + return app + +@pytest.fixture +def create_app(): + """Return a new app that is being reloaded with + any environment variable has being set""" + + yield reload_app + +@pytest.fixture +def create_protected_with_apikey_app(create_app): + def _protected_app(): + with mock.patch.dict( + os.environ, + { + "API_KEY_ENABLED": "true", + "PYGEOAPI_KEY_GLOBAL": "pygeoapi", + "JWKS_ENABLED": "false", + "OPA_ENABLED": "false" + } + ): + app = create_app() + return app + + yield _protected_app + +@pytest.fixture +def protected_apikey_schema(create_protected_with_apikey_app): + app = create_protected_with_apikey_app() + + return schemathesis.from_asgi("/geoapi/openapi?f=json", app=app) diff --git a/tests/test_cli.py b/tests/test_cli.py index 579a934..8a3ba6a 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -4,12 +4,6 @@ from typer.testing import CliRunner -@pytest.fixture -def runner() -> CliRunner: - """Fixture for invoking command-line interfaces.""" - return CliRunner() - - def test_openapi_succeeds(runner: CliRunner) -> None: """It exits with a status code of zero.""" result = runner.invoke(app, ["openapi"]) diff --git a/tests/test_openapi_contract.py b/tests/test_openapi_contract.py new file mode 100644 index 0000000..c5d814e --- /dev/null +++ b/tests/test_openapi_contract.py @@ -0,0 +1,11 @@ +import pytest +import schemathesis + + +schema = schemathesis.from_pytest_fixture("protected_apikey_schema") + +@schema.parametrize() +def test_api(case): + case.headers = {"X-API-KEY": "pygeoapi"} + response = case.call_asgi() + case.validate_response(response)