diff --git a/.github/workflows/check-api-outdated.yml b/.github/workflows/check-api-outdated.yml index 38818c3..227a05f 100644 --- a/.github/workflows/check-api-outdated.yml +++ b/.github/workflows/check-api-outdated.yml @@ -13,7 +13,6 @@ on: # “At 4 am on every day.” (https://crontab.guru) - cron: "0 4 * * *" - jobs: check-api-outdated: diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index a75d9a6..ccb4921 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -9,14 +9,13 @@ jobs: runs-on: ubuntu-latest steps: - - name: SCM Checkout uses: actions/checkout@v3 with: fetch-depth: 0 - name: Setup Python & Poetry Environment - uses: exasol/python-toolbox/.github/actions/python-environment@0.8.0 + uses: exasol/python-toolbox/.github/actions/python-environment@0.9.0 - name: Check Version(s) run: poetry run version-check version.py @@ -80,36 +79,13 @@ jobs: run: poetry run nox -s type-check tests-job: - name: Tests (Python-${{ matrix.python-version }}, Exasol-${{ matrix.exasol-version}}) + name: Tests (Python-${{ matrix.python-version }}) needs: [build-documentation-job, lint-job, type-check-job] - runs-on: ubuntu-latest strategy: fail-fast: false matrix: python-version: ["3.10"] - exasol-version: ["7.1.9"] - - steps: - - name: SCM Checkout - uses: actions/checkout@v3 - - - name: Setup Python & Poetry Environment - uses: exasol/python-toolbox/.github/actions/python-environment@0.9.0 - with: - python-version: ${{ matrix.python-version }} - - - name: Run Tests and Calculate Coverage - env: - SAAS_HOST: ${{ secrets.INTEGRATION_TEAM_SAAS_STAGING_HOST }} - SAAS_ACCOUNT_ID: ${{ secrets.INTEGRATION_TEAM_SAAS_STAGING_ACCOUNT_ID }} - SAAS_PAT: ${{ secrets.INTEGRATION_TEAM_SAAS_STAGING_PAT }} - PYTEST_ADDOPTS: -o log_cli=true -o log_cli_level=INFO - run: | - export PROJECT_SHORT_TAG=$(poetry run nox -s get-project-short-tag) - poetry run nox -s coverage -- -- - - - name: Upload Artifacts - uses: actions/upload-artifact@v3 - with: - name: .coverage - path: .coverage + uses: ./.github/workflows/run-tests.yml + secrets: inherit + with: + python-version: ${{ matrix.python-version }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8e5dc15..ccf5d52 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,3 +23,28 @@ jobs: metrics: needs: [ ci-job ] uses: ./.github/workflows/report.yml + + gate-1: + name: Regular + needs: [ ci-job ] + steps: + step: + - name: Branch Protection + run: true + + slow-tests: + uses: ./.github/workflows/run-tests.yml + secrets: inherit + with: + slow-tests: true + python-version: "3.10" + environment: + slow-tests + + gate-2: + name: Merge + needs: [ slow-tests ] + steps: + step: + - name: Branch Protection + run: true diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml new file mode 100644 index 0000000..c23e1f2 --- /dev/null +++ b/.github/workflows/run-tests.yml @@ -0,0 +1,47 @@ +name: Run Tests + +on: + workflow_call: + inputs: + slow-tests: + type: boolean + default: false + python-version: + type: string + required: true + +jobs: + + tests-job: + runs-on: ubuntu-latest + + steps: + - name: Set pytest markers + id: pytest-markers + if: ${{ ! inputs.slow-tests }} + run: echo slow-tests='-m "not slow"' >> "$GITHUB_OUTPUT" + + - name: SCM Checkout + uses: actions/checkout@v3 + + - name: Setup Python & Poetry Environment + uses: exasol/python-toolbox/.github/actions/python-environment@0.9.0 + with: + python-version: ${{ inputs.python-version }} + + - name: Run Tests and Calculate Coverage + env: + SAAS_HOST: ${{ secrets.INTEGRATION_TEAM_SAAS_STAGING_HOST }} + SAAS_ACCOUNT_ID: ${{ secrets.INTEGRATION_TEAM_SAAS_STAGING_ACCOUNT_ID }} + SAAS_PAT: ${{ secrets.INTEGRATION_TEAM_SAAS_STAGING_PAT }} + PYTEST_ADDOPTS: '-o log_cli=true -o log_cli_level=INFO ${{ steps.pytest-markers.outputs.slow-tests }}' + run: | + echo "PYTEST_ADDOPTS = $PYTEST_ADDOPTS" + export PROJECT_SHORT_TAG=$(poetry run nox -s get-project-short-tag) + poetry run nox -s coverage -- -- + + - name: Upload Artifacts + uses: actions/upload-artifact@v3 + with: + name: .coverage + path: .coverage diff --git a/doc/changes/unreleased.md b/doc/changes/unreleased.md index f3039fd..7f5653f 100644 --- a/doc/changes/unreleased.md +++ b/doc/changes/unreleased.md @@ -1,4 +1,9 @@ # Unreleased +## Refactoring + +* #53: Separated long-running tests + ## Features - - #55 Added publicly callable function finding the database id from its name. + +* #55 Added publicly callable function finding the database id from its name. diff --git a/doc/developer_guide/developer_guide.md b/doc/developer_guide/developer_guide.md index 25d9e08..bbe2849 100644 --- a/doc/developer_guide/developer_guide.md +++ b/doc/developer_guide/developer_guide.md @@ -58,6 +58,8 @@ After generating the API, ## Run Tests +### Integration Tests + Executing the integration tests requires the following environment variables to be set: | Variable | Description | @@ -66,6 +68,12 @@ Executing the integration tests requires the following environment variables to | `SAAS_ACCOUNT_ID` | ID of the Exasol SAAS account to be used by the tests | | `SAAS_PAT` | Personal access token to access the SAAS API | +### Slow Tests + +Some of the test cases verify connecting to a SaaS database instance and execution will take about 20 minutes. + +These test cases are disabled by default and will only be executed by adding a comment `[run-slow-tests]` to your pull request. + ## Creating a Release ### Prepare the Release diff --git a/exasol/saas/client/openapi/api/extensions/create_extension_instance.py b/exasol/saas/client/openapi/api/extensions/create_extension_instance.py index 5ab6f95..35a3a0c 100644 --- a/exasol/saas/client/openapi/api/extensions/create_extension_instance.py +++ b/exasol/saas/client/openapi/api/extensions/create_extension_instance.py @@ -15,6 +15,7 @@ AuthenticatedClient, Client, ) +from ...models.api_error import ApiError from ...models.create_extension_instance import CreateExtensionInstance from ...models.extension_instance import ExtensionInstance from ...types import ( @@ -54,20 +55,26 @@ def _get_kwargs( return _kwargs -def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[ExtensionInstance]: +def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Union[ApiError, ExtensionInstance]]: if response.status_code == HTTPStatus.OK: response_200 = ExtensionInstance.from_dict(response.json()) return response_200 + if response.status_code == HTTPStatus.UNPROCESSABLE_ENTITY: + response_422 = ApiError.from_dict(response.json()) + + + + return response_422 if client.raise_on_unexpected_status: raise errors.UnexpectedStatus(response.status_code, response.content) else: return None -def _build_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Response[ExtensionInstance]: +def _build_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Response[Union[ApiError, ExtensionInstance]]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -85,7 +92,7 @@ def sync_detailed( client: AuthenticatedClient, body: CreateExtensionInstance, -) -> Response[ExtensionInstance]: +) -> Response[Union[ApiError, ExtensionInstance]]: """ Args: account_id (str): @@ -99,7 +106,7 @@ def sync_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[ExtensionInstance] + Response[Union[ApiError, ExtensionInstance]] """ @@ -127,7 +134,7 @@ def sync( client: AuthenticatedClient, body: CreateExtensionInstance, -) -> Optional[ExtensionInstance]: +) -> Optional[Union[ApiError, ExtensionInstance]]: """ Args: account_id (str): @@ -141,7 +148,7 @@ def sync( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - ExtensionInstance + Union[ApiError, ExtensionInstance] """ @@ -164,7 +171,7 @@ async def asyncio_detailed( client: AuthenticatedClient, body: CreateExtensionInstance, -) -> Response[ExtensionInstance]: +) -> Response[Union[ApiError, ExtensionInstance]]: """ Args: account_id (str): @@ -178,7 +185,7 @@ async def asyncio_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[ExtensionInstance] + Response[Union[ApiError, ExtensionInstance]] """ @@ -206,7 +213,7 @@ async def asyncio( client: AuthenticatedClient, body: CreateExtensionInstance, -) -> Optional[ExtensionInstance]: +) -> Optional[Union[ApiError, ExtensionInstance]]: """ Args: account_id (str): @@ -220,7 +227,7 @@ async def asyncio( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - ExtensionInstance + Union[ApiError, ExtensionInstance] """ diff --git a/exasol/saas/client/openapi/api/extensions/get_extension.py b/exasol/saas/client/openapi/api/extensions/get_extension.py index c9060e5..f4645b3 100644 --- a/exasol/saas/client/openapi/api/extensions/get_extension.py +++ b/exasol/saas/client/openapi/api/extensions/get_extension.py @@ -15,6 +15,7 @@ AuthenticatedClient, Client, ) +from ...models.api_error import ApiError from ...models.extension_detail import ExtensionDetail from ...types import ( UNSET, @@ -44,20 +45,26 @@ def _get_kwargs( return _kwargs -def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[ExtensionDetail]: +def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Union[ApiError, ExtensionDetail]]: if response.status_code == HTTPStatus.OK: response_200 = ExtensionDetail.from_dict(response.json()) return response_200 + if response.status_code == HTTPStatus.UNPROCESSABLE_ENTITY: + response_422 = ApiError.from_dict(response.json()) + + + + return response_422 if client.raise_on_unexpected_status: raise errors.UnexpectedStatus(response.status_code, response.content) else: return None -def _build_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Response[ExtensionDetail]: +def _build_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Response[Union[ApiError, ExtensionDetail]]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -74,7 +81,7 @@ def sync_detailed( *, client: AuthenticatedClient, -) -> Response[ExtensionDetail]: +) -> Response[Union[ApiError, ExtensionDetail]]: """ Args: account_id (str): @@ -87,7 +94,7 @@ def sync_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[ExtensionDetail] + Response[Union[ApiError, ExtensionDetail]] """ @@ -113,7 +120,7 @@ def sync( *, client: AuthenticatedClient, -) -> Optional[ExtensionDetail]: +) -> Optional[Union[ApiError, ExtensionDetail]]: """ Args: account_id (str): @@ -126,7 +133,7 @@ def sync( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - ExtensionDetail + Union[ApiError, ExtensionDetail] """ @@ -147,7 +154,7 @@ async def asyncio_detailed( *, client: AuthenticatedClient, -) -> Response[ExtensionDetail]: +) -> Response[Union[ApiError, ExtensionDetail]]: """ Args: account_id (str): @@ -160,7 +167,7 @@ async def asyncio_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[ExtensionDetail] + Response[Union[ApiError, ExtensionDetail]] """ @@ -186,7 +193,7 @@ async def asyncio( *, client: AuthenticatedClient, -) -> Optional[ExtensionDetail]: +) -> Optional[Union[ApiError, ExtensionDetail]]: """ Args: account_id (str): @@ -199,7 +206,7 @@ async def asyncio( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - ExtensionDetail + Union[ApiError, ExtensionDetail] """ diff --git a/exasol/saas/client/openapi/api/extensions/list_extension_instances.py b/exasol/saas/client/openapi/api/extensions/list_extension_instances.py index fd1230c..94df9dd 100644 --- a/exasol/saas/client/openapi/api/extensions/list_extension_instances.py +++ b/exasol/saas/client/openapi/api/extensions/list_extension_instances.py @@ -15,6 +15,7 @@ AuthenticatedClient, Client, ) +from ...models.api_error import ApiError from ...models.extension_instance import ExtensionInstance from ...types import ( UNSET, @@ -44,7 +45,7 @@ def _get_kwargs( return _kwargs -def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[List['ExtensionInstance']]: +def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Union[ApiError, List['ExtensionInstance']]]: if response.status_code == HTTPStatus.OK: response_200 = [] _response_200 = response.json() @@ -56,13 +57,19 @@ def _parse_response(*, client: Union[AuthenticatedClient, Client], response: htt response_200.append(response_200_item) return response_200 + if response.status_code == HTTPStatus.UNPROCESSABLE_ENTITY: + response_422 = ApiError.from_dict(response.json()) + + + + return response_422 if client.raise_on_unexpected_status: raise errors.UnexpectedStatus(response.status_code, response.content) else: return None -def _build_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Response[List['ExtensionInstance']]: +def _build_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Response[Union[ApiError, List['ExtensionInstance']]]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -79,7 +86,7 @@ def sync_detailed( *, client: AuthenticatedClient, -) -> Response[List['ExtensionInstance']]: +) -> Response[Union[ApiError, List['ExtensionInstance']]]: """ Args: account_id (str): @@ -92,7 +99,7 @@ def sync_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[List['ExtensionInstance']] + Response[Union[ApiError, List['ExtensionInstance']]] """ @@ -118,7 +125,7 @@ def sync( *, client: AuthenticatedClient, -) -> Optional[List['ExtensionInstance']]: +) -> Optional[Union[ApiError, List['ExtensionInstance']]]: """ Args: account_id (str): @@ -131,7 +138,7 @@ def sync( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - List['ExtensionInstance'] + Union[ApiError, List['ExtensionInstance']] """ @@ -152,7 +159,7 @@ async def asyncio_detailed( *, client: AuthenticatedClient, -) -> Response[List['ExtensionInstance']]: +) -> Response[Union[ApiError, List['ExtensionInstance']]]: """ Args: account_id (str): @@ -165,7 +172,7 @@ async def asyncio_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[List['ExtensionInstance']] + Response[Union[ApiError, List['ExtensionInstance']]] """ @@ -191,7 +198,7 @@ async def asyncio( *, client: AuthenticatedClient, -) -> Optional[List['ExtensionInstance']]: +) -> Optional[Union[ApiError, List['ExtensionInstance']]]: """ Args: account_id (str): @@ -204,7 +211,7 @@ async def asyncio( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - List['ExtensionInstance'] + Union[ApiError, List['ExtensionInstance']] """ diff --git a/pyproject.toml b/pyproject.toml index 2508b8f..30ae86e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,6 +52,10 @@ testpaths = [ "test" ] +markers = [ + "slow: marks tests as slow (deselect with '-m \"not slow\"')", +] + [tool.coverage.run] relative_files = true source = [ diff --git a/test/integration/connection_test.py b/test/integration/connection_test.py index 59b57ae..525b96d 100644 --- a/test/integration/connection_test.py +++ b/test/integration/connection_test.py @@ -1,7 +1,9 @@ +import pytest import pyexasol from exasol.saas.client.api_access import get_connection_params +@pytest.mark.slow def test_get_connection_params_with_id(saas_host, saas_pat, saas_account_id, operational_saas_database_id, allow_connection): @@ -18,6 +20,7 @@ def test_get_connection_params_with_id(saas_host, saas_pat, saas_account_id, assert result == [(1,)] +@pytest.mark.slow def test_get_connection_params_with_name(saas_host, saas_pat, saas_account_id, operational_saas_database_id, database_name, allow_connection):