diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index cc27323c..93a9f074 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,6 +1,10 @@ ### Submission Checklist -* [ ] Bumped version number +* [ ] Updated the changelog in file `doc/changes/unreleasd.md` + +If required * [ ] Updated Documentation * [ ] Updated API Documentation -* [ ] Updated the changelog \ No newline at end of file + +If doing a release +* [ ] Bumped version number using `poetry run nox -s prepare-release` diff --git a/.github/workflows/check-release-tag.yml b/.github/workflows/check-release-tag.yml index a3cc7142..5159d30f 100644 --- a/.github/workflows/check-release-tag.yml +++ b/.github/workflows/check-release-tag.yml @@ -11,7 +11,7 @@ jobs: steps: - name: SCM Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Python & Poetry Environment uses: exasol/python-toolbox/.github/actions/python-environment@0.12.0 diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index b104ebbb..452ac05b 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -10,7 +10,7 @@ jobs: steps: - name: SCM Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 @@ -27,7 +27,7 @@ jobs: steps: - name: SCM Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Python & Poetry Environment uses: exasol/python-toolbox/.github/actions/python-environment@0.12.0 @@ -47,7 +47,7 @@ jobs: steps: - name: SCM Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Python & Poetry Environment uses: exasol/python-toolbox/.github/actions/python-environment@0.12.0 @@ -74,7 +74,7 @@ jobs: steps: - name: SCM Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Python & Poetry Environment uses: exasol/python-toolbox/.github/actions/python-environment@0.12.0 @@ -96,7 +96,7 @@ jobs: steps: - name: SCM Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Python & Poetry Environment uses: exasol/python-toolbox/.github/actions/python-environment@0.12.0 diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index e4c8ed52..54b443c9 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -25,7 +25,7 @@ jobs: python-version: ["3.8", "3.9", "3.10"] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 53ecb630..c32bfe55 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: name: Checks uses: ./.github/workflows/checks.yml - tests-job: + fast-tests: name: Tests (Python-${{ matrix.python-version }}) runs-on: ubuntu-latest strategy: @@ -28,43 +28,69 @@ jobs: python-version: ["3.8", "3.9", "3.10"] steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v5 + - uses: actions/checkout@v4 + + - name: Setup Python & Poetry Environment + uses: exasol/python-toolbox/.github/actions/python-environment@0.12.0 with: python-version: ${{ matrix.python-version }} - - uses: abatilo/actions-poetry@v3.0.0 - with: poetry-version: 1.2.2 - - name: Install Project - run: poetry install - - name: Checkout ITDE run: git clone https://github.com/exasol/integration-test-docker-environment.git working-directory: .. - - name: Start EXASOL Test-Environment + - name: Start EXASOL Test-Docker-Environment (ITDE) run: ./start-test-env spawn-test-environment --environment-name test --database-port-forward 8888 --bucketfs-port-forward 6666 --db-mem-size 4GB working-directory: ../integration-test-docker-environment - name: Run Tests run: poetry run pytest tests - - name: Fail, if SaaS tests are not activated - if: "!contains(github.event.head_commit.message, '[run-saas-tests]') && (matrix.python-version == '3.10')" - run: | - echo "Failed because the SaaS tests are not activated" - exit 1 + metrics: + needs: [ fast-tests ] + uses: ./.github/workflows/report.yml + + gate-1: + name: Gate 1 - Regular CI + needs: [ fast-tests ] + runs-on: ubuntu-latest + steps: + - name: Branch Protection + run: true + + slow-test-detection: + runs-on: ubuntu-latest + steps: + - name: Detect Slow Tests + run: true + environment: + slow-tests + + run-slow-tests: + runs-on: ubuntu-latest + needs: [ slow-test-detection ] + + steps: + - uses: actions/checkout@v4 + + - name: Setup Python & Poetry Environment + uses: exasol/python-toolbox/.github/actions/python-environment@0.12.0 + with: + python-version: "3.10" + poetry-version: "1.2.2" - name: Run SaaS Tests - if: "contains(github.event.head_commit.message, '[run-saas-tests]') && (matrix.python-version == '3.10')" 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 }} run: poetry run pytest test_saas - - metrics: - needs: [ ci-job ] - uses: ./.github/workflows/report.yml + gate-2: + name: Gate 2 - Allow Merge + runs-on: ubuntu-latest + needs: [ run-slow-tests ] + steps: + - name: Branch Protection + run: true diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 0fe591b9..17bd2451 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -9,7 +9,7 @@ jobs: steps: - name: SCM Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Python & Poetry Environment uses: exasol/python-toolbox/.github/actions/python-environment@0.12.0 diff --git a/.github/workflows/pr-merge.yml b/.github/workflows/pr-merge.yml index c63d6397..b859f4f1 100644 --- a/.github/workflows/pr-merge.yml +++ b/.github/workflows/pr-merge.yml @@ -29,7 +29,7 @@ jobs: python-version: ["3.8", "3.9", "3.10"] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} diff --git a/.github/workflows/report.yml b/.github/workflows/report.yml index 4a006e87..26f3b337 100644 --- a/.github/workflows/report.yml +++ b/.github/workflows/report.yml @@ -16,7 +16,7 @@ jobs: steps: - name: SCM Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 diff --git a/.gitignore b/.gitignore index cb5d7dbd..fdfb6de9 100644 --- a/.gitignore +++ b/.gitignore @@ -140,3 +140,6 @@ doc/api .build_output .html-documentation itde/ + +# Emacs +TAGS diff --git a/doc/changes/unreleased.md b/doc/changes/unreleased.md index 79e701b8..e043d65d 100644 --- a/doc/changes/unreleased.md +++ b/doc/changes/unreleased.md @@ -1 +1,9 @@ # Unreleased + +## Summary + +The current release adds a dependency to plugin `pytest_exasol_saas` and replaces individual test fixtures by those provided by the plugin. + +## Refactorings + +* #141: Used plugin `pytest_exasol_saas` diff --git a/poetry.lock b/poetry.lock index 109edf26..c9eab55b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. [[package]] name = "alabaster" @@ -1412,6 +1412,22 @@ tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} [package.extras] testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +[[package]] +name = "pytest-exasol-saas" +version = "0.2.1" +description = "" +optional = false +python-versions = "<4,>=3.8" +files = [ + {file = "pytest_exasol_saas-0.2.1-py3-none-any.whl", hash = "sha256:d55090a47f856b626f66fc993e5ce8935b3d3633fb630cd492ac9e55a2bd698e"}, + {file = "pytest_exasol_saas-0.2.1.tar.gz", hash = "sha256:fe17e25b19afdea4fefd4253c16439c442e075ab098b4a9b9afb42aa6101028e"}, +] + +[package.dependencies] +exasol-saas-api = ">=0.6.0,<1.0.0" +pytest = ">=7,<9" +pyyaml = ">=6.0.1,<7.0.0" + [[package]] name = "pytest-localserver" version = "0.8.1" @@ -2070,4 +2086,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more [metadata] lock-version = "2.0" python-versions = ">=3.8,<4.0" -content-hash = "b60120805d4a92e557cf012c80bdc7811990f7796d48ab54513842863d987203" +content-hash = "6d396c798d83bb0c4c8608d568ef78679360dd58d6bace4e7a9db2399b11d142" diff --git a/pyproject.toml b/pyproject.toml index 5507dc5d..33ee52f3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,7 +38,6 @@ exasol-saas-api=">=0.3.0" httpx=">=0.27.0" attrs=">=23.2.0" - [tool.poetry.dev-dependencies] pyexasol = "^0.25.2" dill = "^0.3.4" @@ -47,6 +46,7 @@ toml = ">=0.10.2" sphinx-copybutton = "^0.5.0" exasol-toolbox = ">=0.12.0" pytest-localserver = "^0.8.1" +pytest-exasol-saas = ">=0.2.1" [tool.coverage.run] source = [ diff --git a/test_saas/integration/conftest.py b/test_saas/integration/conftest.py deleted file mode 100644 index ce19f3eb..00000000 --- a/test_saas/integration/conftest.py +++ /dev/null @@ -1,97 +0,0 @@ -from typing import Optional -import os -import time - -import pytest -from exasol.saas.client import openapi -from exasol.saas.client.openapi.api.databases.create_database import sync as create_saas_database -from exasol.saas.client.openapi.api.databases.delete_database import sync_detailed as delete_saas_database -from exasol.saas.client.openapi.api.databases.get_database import sync as get_saas_database -from exasol.saas.client.openapi.models.status import Status as SaasStatus - - -def create_saas_test_client(url: str, - token: str, - raise_on_unexpected_status: bool = True - ) -> openapi.AuthenticatedClient: - return openapi.AuthenticatedClient( - base_url=url, - token=token, - raise_on_unexpected_status=raise_on_unexpected_status - ) - - -def create_saas_test_database(account_id: str, - client: openapi.AuthenticatedClient - ) -> Optional[openapi.models.database.Database]: - cluster_spec = openapi.models.CreateCluster( - name="my-cluster", - size="XS", - ) - database_spec = openapi.models.CreateDatabase( - name=f"pytest-created-db", - initial_cluster=cluster_spec, - provider="aws", - region='us-east-1', - ) - return create_saas_database( - account_id=account_id, - body=database_spec, - client=client - ) - - -@pytest.fixture(scope='session') -def saas_test_service_url() -> str: - return os.environ["SAAS_HOST"] - - -@pytest.fixture(scope='session') -def saas_test_token() -> str: - return os.environ["SAAS_PAT"] - - -@pytest.fixture(scope='session') -def saas_test_account_id() -> str: - return os.environ["SAAS_ACCOUNT_ID"] - - -@pytest.fixture(scope='session') -def saas_test_database_id(saas_test_service_url, saas_test_token, saas_test_account_id) -> str: - - with create_saas_test_client( - url=saas_test_service_url, - token=saas_test_token - ) as client: - db: Optional[openapi.models.database.Database] = None - try: - db = create_saas_test_database( - account_id=saas_test_account_id, - client=client - ) - # Wait till the database gets to the running state. - sleep_time = 600 - small_interval = 20 - max_wait_time = 2400 - max_cycles = 1 + (max_wait_time - sleep_time) // small_interval - for _ in range(max_cycles): - time.sleep(sleep_time) - db = get_saas_database( - account_id=saas_test_account_id, - database_id=db.id, - client=client - ) - if db.status == SaasStatus.RUNNING: - break - sleep_time = 30 - else: - raise RuntimeError(f'Test SaaS database status is {db.status} ' - f'after {max_wait_time} seconds.') - yield db.id - finally: - if db is not None: - delete_saas_database( - account_id=saas_test_account_id, - database_id=db.id, - client=client - ) diff --git a/test_saas/integration/test_buckets.py b/test_saas/integration/test_buckets.py index c5c1fdeb..341430e6 100644 --- a/test_saas/integration/test_buckets.py +++ b/test_saas/integration/test_buckets.py @@ -1,33 +1,33 @@ from exasol.bucketfs import SaaSBucket -def test_write_bytes_to_saas_bucket(saas_test_service_url, saas_test_token, - saas_test_account_id, saas_test_database_id): +def test_write_bytes_to_saas_bucket(saas_host, saas_pat, + saas_account_id, operational_saas_database_id): """ Uploads some bytes into a SaaS bucket file and checks that the file is listed in the SaaS BucketFS. """ - bucket = SaaSBucket(url=saas_test_service_url, - account_id=saas_test_account_id, - database_id=saas_test_database_id, - pat=saas_test_token) + bucket = SaaSBucket(url=saas_host, + account_id=saas_account_id, + database_id=operational_saas_database_id, + pat=saas_pat) file_name = 'bucketfs_test/test_write_bytes_to_saas_bucket/the_file.dat' bucket.upload(path=file_name, data=b'abcd12345') assert file_name in bucket.files -def test_write_file_to_saas_bucket(saas_test_service_url, saas_test_token, - saas_test_account_id, saas_test_database_id, +def test_write_file_to_saas_bucket(saas_host, saas_pat, + saas_account_id, operational_saas_database_id, tmpdir): """ Uploads a file from a local file system into a SaaS bucket and checks that the file is listed in the SaaS BucketFS. """ - bucket = SaaSBucket(url=saas_test_service_url, - account_id=saas_test_account_id, - database_id=saas_test_database_id, - pat=saas_test_token) + bucket = SaaSBucket(url=saas_host, + account_id=saas_account_id, + database_id=operational_saas_database_id, + pat=saas_pat) tmp_file = tmpdir / 'the_file.dat' tmp_file.write_binary(b'abcd12345') @@ -37,16 +37,16 @@ def test_write_file_to_saas_bucket(saas_test_service_url, saas_test_token, assert file_name in bucket.files -def test_read_bytes_from_saas_bucket(saas_test_service_url, saas_test_token, - saas_test_account_id, saas_test_database_id): +def test_read_bytes_from_saas_bucket(saas_host, saas_pat, + saas_account_id, operational_saas_database_id): """ Uploads some bytes into a SaaS bucket file, reads them back and checks that they are unchanged. """ - bucket = SaaSBucket(url=saas_test_service_url, - account_id=saas_test_account_id, - database_id=saas_test_database_id, - pat=saas_test_token) + bucket = SaaSBucket(url=saas_host, + account_id=saas_account_id, + database_id=operational_saas_database_id, + pat=saas_pat) file_name = 'bucketfs_test/test_read_bytes_from_saas_bucket/the_file.dat' content = b'A string long enough to be downloaded in chunks.' @@ -55,17 +55,17 @@ def test_read_bytes_from_saas_bucket(saas_test_service_url, saas_test_token, assert received_content == content -def test_read_file_from_saas_bucket(saas_test_service_url, saas_test_token, - saas_test_account_id, saas_test_database_id, +def test_read_file_from_saas_bucket(saas_host, saas_pat, + saas_account_id, operational_saas_database_id, tmpdir): """ Uploads a file from a local file system into a SaaS bucket, reads its content back and checks that it's unchanged. """ - bucket = SaaSBucket(url=saas_test_service_url, - account_id=saas_test_account_id, - database_id=saas_test_database_id, - pat=saas_test_token) + bucket = SaaSBucket(url=saas_host, + account_id=saas_account_id, + database_id=operational_saas_database_id, + pat=saas_pat) content = b'A string long enough to be downloaded in chunks.' tmp_file = tmpdir / 'the_file.dat' @@ -77,16 +77,16 @@ def test_read_file_from_saas_bucket(saas_test_service_url, saas_test_token, assert received_content == content -def test_delete_file_from_saas_bucket(saas_test_service_url, saas_test_token, - saas_test_account_id, saas_test_database_id): +def test_delete_file_from_saas_bucket(saas_host, saas_pat, + saas_account_id, operational_saas_database_id): """ Creates a SaaS bucket file, then deletes it and checks that it is not listed in the SaaS BucketFS. """ - bucket = SaaSBucket(url=saas_test_service_url, - account_id=saas_test_account_id, - database_id=saas_test_database_id, - pat=saas_test_token) + bucket = SaaSBucket(url=saas_host, + account_id=saas_account_id, + database_id=operational_saas_database_id, + pat=saas_pat) file_name = 'bucketfs_test/test_delete_file_from_saas_bucket/the_file.dat' bucket.upload(path=file_name, data=b'abcd12345') diff --git a/test_saas/integration/test_path.py b/test_saas/integration/test_path.py index de7b3f2b..d7b0ea09 100644 --- a/test_saas/integration/test_path.py +++ b/test_saas/integration/test_path.py @@ -34,15 +34,15 @@ def _collect_all_names(path: bfs.path.PathLike) -> set[str]: return all_names -def test_write_read_back_saas(saas_test_service_url, saas_test_token, - saas_test_account_id, saas_test_database_id, +def test_write_read_back_saas(saas_host, saas_pat, + saas_account_id, operational_saas_database_id, children_poem): base_path = bfs.path.build_path(backend=bfs.path.StorageBackend.saas, - url=saas_test_service_url, - account_id=saas_test_account_id, - database_id=saas_test_database_id, - pat=saas_test_token) + url=saas_host, + account_id=saas_account_id, + database_id=operational_saas_database_id, + pat=saas_pat) file_name = 'test_bucket_path/test_write_read_back_saas/little_star.txt' poem_path = base_path / file_name @@ -51,15 +51,15 @@ def test_write_read_back_saas(saas_test_service_url, saas_test_token, assert data_back == children_poem -def test_write_list_files_saas(saas_test_service_url, saas_test_token, - saas_test_account_id, saas_test_database_id, +def test_write_list_files_saas(saas_host, saas_pat, + saas_account_id, operational_saas_database_id, children_poem, classic_poem): base_path = bfs.path.build_path(backend=bfs.path.StorageBackend.saas, - url=saas_test_service_url, - account_id=saas_test_account_id, - database_id=saas_test_database_id, - pat=saas_test_token, + url=saas_host, + account_id=saas_account_id, + database_id=operational_saas_database_id, + pat=saas_pat, path='test_bucket_path/test_write_list_files_saas') poem_path1 = base_path / 'children/little_star.txt' poem_path2 = base_path / 'classic/highlands.txt' @@ -70,15 +70,15 @@ def test_write_list_files_saas(saas_test_service_url, saas_test_token, assert _collect_all_names(base_path) == expected_names -def test_write_delete_saas(saas_test_service_url, saas_test_token, - saas_test_account_id, saas_test_database_id, +def test_write_delete_saas(saas_host, saas_pat, + saas_account_id, operational_saas_database_id, children_poem, classic_poem): base_path = bfs.path.build_path(backend=bfs.path.StorageBackend.saas, - url=saas_test_service_url, - account_id=saas_test_account_id, - database_id=saas_test_database_id, - pat=saas_test_token) + url=saas_host, + account_id=saas_account_id, + database_id=operational_saas_database_id, + pat=saas_pat) poems_root = base_path / 'test_bucket_path/test_write_delete_saas' poem_path1 = poems_root / 'children/little_star.txt' poem_path2 = poems_root / 'classic/highlands.txt'