diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 279be0e07a..ef29c54e4f 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -22,7 +22,7 @@ on: jobs: lint: name: Lint - uses: canonical/data-platform-workflows/.github/workflows/lint.yaml@v6.3.1 + uses: canonical/data-platform-workflows/.github/workflows/lint.yaml@v6.3.3 unit-test: name: Unit test charm @@ -45,83 +45,35 @@ jobs: build: name: Build charm - uses: canonical/data-platform-workflows/.github/workflows/build_charms_with_cache.yaml@v6.3.1 + uses: canonical/data-platform-workflows/.github/workflows/build_charms_with_cache.yaml@v6.3.3 permissions: actions: write # Needed to manage GitHub Actions cache - gh-hosted-integration-test: + integration-test: strategy: fail-fast: false matrix: - tox-environment: - - backup-integration - - charm-integration - - database-relation-integration - - db-relation-integration - - db-admin-relation-integration - - ha-replication-integration - - ha-self-healing-integration - - password-rotation-integration - - plugins-integration - - tls-integration - - upgrade-integration - - upgrade-from-stable-integration - juju-snap-channel: ["2.9/stable", "3.1/stable"] - include: - - juju-snap-channel: "3.1/stable" - agent-version: "3.1.6" - libjuju-version: "3.2.2" - exclude-mark: "juju2" - - juju-snap-channel: "2.9/stable" - agent-version: "2.9.45" - libjuju-version: "2.9.45.0" - exclude-mark: "juju3" - name: ${{ matrix.juju-snap-channel }} - (GH hosted) ${{ matrix.tox-environment }} + juju: + - agent: 2.9.45 + libjuju: ^2 + - agent: 3.1.6 + name: Integration test charm | ${{ matrix.juju.agent }} needs: - lint - unit-test - build - runs-on: ubuntu-latest - timeout-minutes: 120 - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Install tox & poetry - run: | - pipx install tox - pipx install poetry - - name: Setup operator environment - uses: charmed-kubernetes/actions-operator@main - with: - provider: microk8s - channel: "1.28-strict/stable" - bootstrap-options: "--agent-version ${{ matrix.agent-version }}" - juju-channel: ${{ matrix.juju-snap-channel }} - - name: Update python-libjuju version - if: ${{ matrix.juju-snap-channel == '2.9/stable' }} - run: poetry add --lock --group integration juju@'${{ matrix.libjuju-version }}' - - name: Download packed charm(s) - uses: actions/download-artifact@v3 - with: - name: ${{ needs.build.outputs.artifact-name }} - - name: Select test stability level - id: select-test-stability - run: | - if [[ "${{ github.event_name }}" == "schedule" ]] - then - echo Running unstable and stable tests - echo "mark_expression=" >> "$GITHUB_OUTPUT" - else - echo Skipping unstable tests - echo "mark_expression=and not unstable" >> "$GITHUB_OUTPUT" - fi - - name: Run integration tests - run: tox run -e ${{ matrix.tox-environment }} -- -m 'not ${{ matrix.exclude-mark }} ${{ steps.select-test-stability.outputs.mark_expression }}' --keep-models - env: - SECRETS_FROM_GITHUB: | - { - "AWS_ACCESS_KEY": "${{ secrets.AWS_ACCESS_KEY }}", - "AWS_SECRET_KEY": "${{ secrets.AWS_SECRET_KEY }}", - "GCP_ACCESS_KEY": "${{ secrets.GCP_ACCESS_KEY }}", - "GCP_SECRET_KEY": "${{ secrets.GCP_SECRET_KEY }}", - } + uses: canonical/data-platform-workflows/.github/workflows/integration_test_charm.yaml@v6.3.3 + with: + artifact-name: ${{ needs.build.outputs.artifact-name }} + cloud: microk8s + microk8s-snap-channel: 1.28-strict/stable + juju-agent-version: ${{ matrix.juju.agent }} + libjuju-version-constraint: ${{ matrix.juju.libjuju }} + secrets: + integration-test: | + { + "AWS_ACCESS_KEY": "${{ secrets.AWS_ACCESS_KEY }}", + "AWS_SECRET_KEY": "${{ secrets.AWS_SECRET_KEY }}", + "GCP_ACCESS_KEY": "${{ secrets.GCP_ACCESS_KEY }}", + "GCP_SECRET_KEY": "${{ secrets.GCP_SECRET_KEY }}", + } diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index fe31a61b94..5eeeca6868 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -41,14 +41,14 @@ jobs: build: name: Build charm - uses: canonical/data-platform-workflows/.github/workflows/build_charm_without_cache.yaml@v6.3.1 + uses: canonical/data-platform-workflows/.github/workflows/build_charm_without_cache.yaml@v6.3.3 release: name: Release charm needs: - ci-tests - build - uses: canonical/data-platform-workflows/.github/workflows/release_charm.yaml@v6.3.1 + uses: canonical/data-platform-workflows/.github/workflows/release_charm.yaml@v6.3.3 with: channel: 14/edge artifact-name: ${{ needs.build.outputs.artifact-name }} diff --git a/.github/workflows/sync_issue_to_jira.yaml b/.github/workflows/sync_issue_to_jira.yaml index cdf98a4b47..a2af5e94c7 100644 --- a/.github/workflows/sync_issue_to_jira.yaml +++ b/.github/workflows/sync_issue_to_jira.yaml @@ -9,7 +9,7 @@ on: jobs: sync: name: Sync GitHub issue to Jira - uses: canonical/data-platform-workflows/.github/workflows/sync_issue_to_jira.yaml@v6.3.1 + uses: canonical/data-platform-workflows/.github/workflows/sync_issue_to_jira.yaml@v6.3.3 with: jira-base-url: https://warthogs.atlassian.net jira-project-key: DPE diff --git a/poetry.lock b/poetry.lock index 55783b6786..b3c224505f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -937,16 +937,6 @@ files = [ {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, @@ -1231,8 +1221,6 @@ files = [ {file = "psycopg2-2.9.9-cp310-cp310-win_amd64.whl", hash = "sha256:426f9f29bde126913a20a96ff8ce7d73fd8a216cfb323b1f04da402d452853c3"}, {file = "psycopg2-2.9.9-cp311-cp311-win32.whl", hash = "sha256:ade01303ccf7ae12c356a5e10911c9e1c51136003a9a1d92f7aa9d010fb98372"}, {file = "psycopg2-2.9.9-cp311-cp311-win_amd64.whl", hash = "sha256:121081ea2e76729acfb0673ff33755e8703d45e926e416cb59bae3a86c6a4981"}, - {file = "psycopg2-2.9.9-cp312-cp312-win32.whl", hash = "sha256:d735786acc7dd25815e89cc4ad529a43af779db2e25aa7c626de864127e5a024"}, - {file = "psycopg2-2.9.9-cp312-cp312-win_amd64.whl", hash = "sha256:a7653d00b732afb6fc597e29c50ad28087dcb4fbfb28e86092277a559ae4e693"}, {file = "psycopg2-2.9.9-cp37-cp37m-win32.whl", hash = "sha256:5e0d98cade4f0e0304d7d6f25bbfbc5bd186e07b38eac65379309c4ca3193efa"}, {file = "psycopg2-2.9.9-cp37-cp37m-win_amd64.whl", hash = "sha256:7e2dacf8b009a1c1e843b5213a87f7c544b2b042476ed7755be813eaf4e8347a"}, {file = "psycopg2-2.9.9-cp38-cp38-win32.whl", hash = "sha256:ff432630e510709564c01dafdbe996cb552e0b9f3f065eb89bdce5bd31fabf4c"}, @@ -1477,8 +1465,8 @@ develop = false [package.source] type = "git" url = "https://github.com/canonical/data-platform-workflows" -reference = "v6.3.1" -resolved_reference = "d4714db330bcc40eb2cd549f8ebcc671a5c5e512" +reference = "v6.3.3" +resolved_reference = "25b5344e7a8c5c90f436732b5f4539b42f667f54" subdirectory = "python/pytest_plugins/github_secrets" [[package]] @@ -1532,8 +1520,8 @@ pyyaml = "*" [package.source] type = "git" url = "https://github.com/canonical/data-platform-workflows" -reference = "v6.3.1" -resolved_reference = "d4714db330bcc40eb2cd549f8ebcc671a5c5e512" +reference = "v6.3.3" +resolved_reference = "25b5344e7a8c5c90f436732b5f4539b42f667f54" subdirectory = "python/pytest_plugins/pytest_operator_cache" [[package]] @@ -1551,8 +1539,8 @@ pytest = "*" [package.source] type = "git" url = "https://github.com/canonical/data-platform-workflows" -reference = "v6.3.1" -resolved_reference = "d4714db330bcc40eb2cd549f8ebcc671a5c5e512" +reference = "v6.3.3" +resolved_reference = "25b5344e7a8c5c90f436732b5f4539b42f667f54" subdirectory = "python/pytest_plugins/pytest_operator_groups" [[package]] @@ -1592,7 +1580,6 @@ files = [ {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, - {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, @@ -1600,15 +1587,8 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, - {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, - {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, - {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, - {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, @@ -1625,7 +1605,6 @@ files = [ {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, - {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, @@ -1633,7 +1612,6 @@ files = [ {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, - {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, @@ -2103,4 +2081,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "37962f142578c2f6ad8e22659fc6d323349d568d30ff2999f577365b0d0e508a" +content-hash = "9a918be121e6f9d79c8c191411a5d6808291440b2bddbd7d8ae9708e47589fad" diff --git a/pyproject.toml b/pyproject.toml index a78a6236a8..e1d2f9d5d1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -70,16 +70,14 @@ optional = true [tool.poetry.group.integration.dependencies] lightkube = "^0.15.0" pytest = "^7.4.3" -pytest-github-secrets = {git = "https://github.com/canonical/data-platform-workflows", tag = "v6.3.1", subdirectory = "python/pytest_plugins/github_secrets"} +pytest-github-secrets = {git = "https://github.com/canonical/data-platform-workflows", tag = "v6.3.3", subdirectory = "python/pytest_plugins/github_secrets"} pytest-operator = "^0.31.0" -pytest-operator-cache = {git = "https://github.com/canonical/data-platform-workflows", tag = "v6.3.1", subdirectory = "python/pytest_plugins/pytest_operator_cache"} -pytest-operator-groups = {git = "https://github.com/canonical/data-platform-workflows", tag = "v6.3.1", subdirectory = "python/pytest_plugins/pytest_operator_groups"} +pytest-operator-cache = {git = "https://github.com/canonical/data-platform-workflows", tag = "v6.3.3", subdirectory = "python/pytest_plugins/pytest_operator_cache"} +pytest-operator-groups = {git = "https://github.com/canonical/data-platform-workflows", tag = "v6.3.3", subdirectory = "python/pytest_plugins/pytest_operator_groups"} juju = "^3.2.2" psycopg2 = {version = "^2.9.9", extras = ["binary"]} boto3 = "^1.33.1" tenacity = "^8.2.3" -ops = "^2.8.0" -pytest-mock = "^3.12.0" [build-system] requires = ["poetry-core>=1.0.0"] @@ -96,7 +94,7 @@ show_missing = true minversion = "6.0" log_cli_level = "INFO" asyncio_mode = "auto" -markers = ["unstable"] +markers = ["unstable", "juju2", "juju3", "juju_secrets"] # Formatting tools configuration [tool.black] diff --git a/tests/integration/__init__.py b/tests/integration/__init__.py new file mode 100644 index 0000000000..db3bfe1a65 --- /dev/null +++ b/tests/integration/__init__.py @@ -0,0 +1,2 @@ +# Copyright 2023 Canonical Ltd. +# See LICENSE file for licensing details. diff --git a/tests/integration/ha_tests/__init__.py b/tests/integration/ha_tests/__init__.py new file mode 100644 index 0000000000..db3bfe1a65 --- /dev/null +++ b/tests/integration/ha_tests/__init__.py @@ -0,0 +1,2 @@ +# Copyright 2023 Canonical Ltd. +# See LICENSE file for licensing details. diff --git a/tests/integration/ha_tests/conftest.py b/tests/integration/ha_tests/conftest.py index f3bdd0da3c..db02cdf5f8 100644 --- a/tests/integration/ha_tests/conftest.py +++ b/tests/integration/ha_tests/conftest.py @@ -6,7 +6,8 @@ from pytest_operator.plugin import OpsTest from tenacity import Retrying, stop_after_delay, wait_fixed -from tests.integration.ha_tests.helpers import ( +from ..helpers import app_name +from .helpers import ( APPLICATION_NAME, change_patroni_setting, change_wal_settings, @@ -17,7 +18,6 @@ modify_pebble_restart_delay, remove_instance_isolation, ) -from tests.integration.helpers import app_name @pytest.fixture() diff --git a/tests/integration/ha_tests/helpers.py b/tests/integration/ha_tests/helpers.py index e1786ca1f9..4eb50d1883 100644 --- a/tests/integration/ha_tests/helpers.py +++ b/tests/integration/ha_tests/helpers.py @@ -27,7 +27,7 @@ wait_fixed, ) -from tests.integration.helpers import ( +from ..helpers import ( APPLICATION_NAME, app_name, db_connect, diff --git a/tests/integration/ha_tests/test_replication.py b/tests/integration/ha_tests/test_replication.py index 506ed00dba..05fd305d3d 100644 --- a/tests/integration/ha_tests/test_replication.py +++ b/tests/integration/ha_tests/test_replication.py @@ -7,13 +7,7 @@ from pytest_operator.plugin import OpsTest from tenacity import Retrying, stop_after_delay, wait_fixed -from tests.integration.ha_tests.helpers import ( - are_writes_increasing, - check_writes, - is_cluster_updated, - start_continuous_writes, -) -from tests.integration.helpers import ( +from ..helpers import ( APPLICATION_NAME, CHARM_SERIES, app_name, @@ -24,8 +18,15 @@ get_unit_address, scale_application, ) +from .helpers import ( + are_writes_increasing, + check_writes, + is_cluster_updated, + start_continuous_writes, +) +@pytest.mark.group(1) @pytest.mark.abort_on_fail async def test_build_and_deploy(ops_test: OpsTest) -> None: """Build and deploy three unit of PostgreSQL.""" @@ -51,6 +52,7 @@ async def test_build_and_deploy(ops_test: OpsTest) -> None: await ops_test.model.wait_for_idle(status="active", timeout=1000) +@pytest.mark.group(1) async def test_reelection(ops_test: OpsTest, continuous_writes, primary_start_timeout) -> None: """Kill primary unit, check reelection.""" app = await app_name(ops_test) @@ -82,6 +84,7 @@ async def test_reelection(ops_test: OpsTest, continuous_writes, primary_start_ti await is_cluster_updated(ops_test, primary_name) +@pytest.mark.group(1) async def test_consistency(ops_test: OpsTest, continuous_writes) -> None: """Write to primary, read data from secondaries (check consistency).""" # Locate primary unit. @@ -99,6 +102,7 @@ async def test_consistency(ops_test: OpsTest, continuous_writes) -> None: await check_writes(ops_test) +@pytest.mark.group(1) async def test_no_data_replicated_between_clusters(ops_test: OpsTest, continuous_writes) -> None: """Check that writes in one cluster are not replicated to another cluster.""" # Locate primary unit. diff --git a/tests/integration/ha_tests/test_self_healing.py b/tests/integration/ha_tests/test_self_healing.py index 50f113cd25..aba0badb00 100644 --- a/tests/integration/ha_tests/test_self_healing.py +++ b/tests/integration/ha_tests/test_self_healing.py @@ -9,7 +9,20 @@ from pytest_operator.plugin import OpsTest from tenacity import Retrying, stop_after_delay, wait_fixed -from tests.integration.ha_tests.helpers import ( +from .. import markers +from ..helpers import ( + APPLICATION_NAME, + CHARM_SERIES, + METADATA, + app_name, + build_and_deploy, + db_connect, + get_password, + get_unit_address, + run_command_on_unit, + scale_application, +) +from .helpers import ( are_all_db_processes_down, are_writes_increasing, change_patroni_setting, @@ -29,18 +42,6 @@ send_signal_to_process, start_continuous_writes, ) -from tests.integration.helpers import ( - APPLICATION_NAME, - CHARM_SERIES, - METADATA, - app_name, - build_and_deploy, - db_connect, - get_password, - get_unit_address, - run_command_on_unit, - scale_application, -) logger = logging.getLogger(__name__) @@ -51,6 +52,7 @@ MEDIAN_ELECTION_TIME = 10 +@pytest.mark.group(1) @pytest.mark.abort_on_fail async def test_build_and_deploy(ops_test: OpsTest) -> None: """Build and deploy three unit of PostgreSQL.""" @@ -76,7 +78,8 @@ async def test_build_and_deploy(ops_test: OpsTest) -> None: await ops_test.model.wait_for_idle(status="active", timeout=1000) -@pytest.mark.juju2 +@pytest.mark.group(1) +@markers.juju2 @pytest.mark.parametrize("process", DB_PROCESSES) async def test_kill_db_process( ops_test: OpsTest, process: str, continuous_writes, primary_start_timeout @@ -107,6 +110,7 @@ async def test_kill_db_process( await is_cluster_updated(ops_test, primary_name) +@pytest.mark.group(1) @pytest.mark.parametrize("process", DB_PROCESSES) async def test_freeze_db_process( ops_test: OpsTest, process: str, continuous_writes, primary_start_timeout @@ -148,6 +152,7 @@ async def test_freeze_db_process( await is_cluster_updated(ops_test, primary_name) +@pytest.mark.group(1) @pytest.mark.parametrize("process", DB_PROCESSES) async def test_restart_db_process( ops_test: OpsTest, process: str, continuous_writes, primary_start_timeout @@ -178,6 +183,7 @@ async def test_restart_db_process( await is_cluster_updated(ops_test, primary_name) +@pytest.mark.group(1) @pytest.mark.unstable @pytest.mark.parametrize("process", DB_PROCESSES) @pytest.mark.parametrize("signal", ["SIGTERM", "SIGKILL"]) @@ -244,7 +250,7 @@ async def test_full_cluster_restart( await check_writes(ops_test) -@pytest.mark.ha_self_healing_tests +@pytest.mark.group(1) async def test_forceful_restart_without_data_and_transaction_logs( ops_test: OpsTest, continuous_writes, @@ -327,6 +333,7 @@ async def test_forceful_restart_without_data_and_transaction_logs( await is_cluster_updated(ops_test, primary_name) +@pytest.mark.group(1) async def test_network_cut( ops_test: OpsTest, continuous_writes, primary_start_timeout, chaos_mesh ) -> None: @@ -390,6 +397,7 @@ async def test_network_cut( await is_cluster_updated(ops_test, primary_name) +@pytest.mark.group(1) async def test_scaling_to_zero(ops_test: OpsTest, continuous_writes) -> None: """Scale the database to zero units and scale up again.""" # Locate primary unit. diff --git a/tests/integration/ha_tests/test_upgrade.py b/tests/integration/ha_tests/test_upgrade.py index 52ca535d7a..58e3123c7a 100644 --- a/tests/integration/ha_tests/test_upgrade.py +++ b/tests/integration/ha_tests/test_upgrade.py @@ -15,12 +15,7 @@ from pytest_operator.plugin import OpsTest from tenacity import Retrying, stop_after_attempt, wait_fixed -from tests.integration.ha_tests.helpers import ( - are_writes_increasing, - check_writes, - start_continuous_writes, -) -from tests.integration.helpers import ( +from ..helpers import ( APPLICATION_NAME, DATABASE_APP_NAME, METADATA, @@ -29,13 +24,19 @@ get_primary, get_unit_by_index, ) -from tests.integration.new_relations.helpers import get_application_relation_data +from ..new_relations.helpers import get_application_relation_data +from .helpers import ( + are_writes_increasing, + check_writes, + start_continuous_writes, +) logger = logging.getLogger(__name__) TIMEOUT = 600 +@pytest.mark.group(1) @pytest.mark.abort_on_fail async def test_deploy_latest(ops_test: OpsTest) -> None: """Simple test to ensure that the PostgreSQL and application charms get deployed.""" @@ -61,6 +62,7 @@ async def test_deploy_latest(ops_test: OpsTest) -> None: assert len(ops_test.model.applications[DATABASE_APP_NAME].units) == 3 +@pytest.mark.group(1) @pytest.mark.abort_on_fail async def test_pre_upgrade_check(ops_test: OpsTest) -> None: """Test that the pre-upgrade-check action runs successfully.""" @@ -87,6 +89,7 @@ async def test_pre_upgrade_check(ops_test: OpsTest) -> None: assert stateful_set.spec.updateStrategy.rollingUpdate.partition == 2, "Partition not set to 2" +@pytest.mark.group(1) @pytest.mark.abort_on_fail async def test_upgrade_from_edge(ops_test: OpsTest, continuous_writes) -> None: # Start an application that continuously writes data to the database. @@ -148,6 +151,7 @@ async def test_upgrade_from_edge(ops_test: OpsTest, continuous_writes) -> None: ) <= 2, "Number of switchovers is greater than 2" +@pytest.mark.group(1) @pytest.mark.abort_on_fail async def test_fail_and_rollback(ops_test, continuous_writes) -> None: # Start an application that continuously writes data to the database. @@ -239,6 +243,7 @@ async def test_fail_and_rollback(ops_test, continuous_writes) -> None: fault_charm.unlink() +@pytest.mark.group(1) async def inject_dependency_fault( ops_test: OpsTest, application_name: str, charm_file: Union[str, Path] ) -> None: diff --git a/tests/integration/ha_tests/test_upgrade_from_stable.py b/tests/integration/ha_tests/test_upgrade_from_stable.py index 7693ba4dd1..0b65eb3d7d 100644 --- a/tests/integration/ha_tests/test_upgrade_from_stable.py +++ b/tests/integration/ha_tests/test_upgrade_from_stable.py @@ -10,12 +10,7 @@ from pytest_operator.plugin import OpsTest from tenacity import Retrying, stop_after_attempt, wait_fixed -from tests.integration.ha_tests.helpers import ( - are_writes_increasing, - check_writes, - start_continuous_writes, -) -from tests.integration.helpers import ( +from ..helpers import ( APPLICATION_NAME, DATABASE_APP_NAME, METADATA, @@ -24,12 +19,18 @@ get_primary, get_unit_by_index, ) +from .helpers import ( + are_writes_increasing, + check_writes, + start_continuous_writes, +) logger = logging.getLogger(__name__) TIMEOUT = 5 * 60 +@pytest.mark.group(1) @pytest.mark.abort_on_fail async def test_deploy_stable(ops_test: OpsTest) -> None: """Simple test to ensure that the PostgreSQL and application charms get deployed.""" @@ -54,6 +55,7 @@ async def test_deploy_stable(ops_test: OpsTest) -> None: assert len(ops_test.model.applications[DATABASE_APP_NAME].units) == 3 +@pytest.mark.group(1) @pytest.mark.abort_on_fail async def test_pre_upgrade_check(ops_test: OpsTest) -> None: """Test that the pre-upgrade-check action runs successfully.""" @@ -85,6 +87,7 @@ async def test_pre_upgrade_check(ops_test: OpsTest) -> None: assert stateful_set.spec.updateStrategy.rollingUpdate.partition == 2, "Partition not set to 2" +@pytest.mark.group(1) @pytest.mark.abort_on_fail async def test_upgrade_from_stable(ops_test: OpsTest, continuous_writes): """Test updating from stable channel.""" diff --git a/tests/integration/helpers.py b/tests/integration/helpers.py index 02314e4c27..bfb81ba55f 100644 --- a/tests/integration/helpers.py +++ b/tests/integration/helpers.py @@ -33,6 +33,7 @@ METADATA = yaml.safe_load(Path("./metadata.yaml").read_text()) DATABASE_APP_NAME = METADATA["name"] APPLICATION_NAME = "postgresql-test-app" +STORAGE_PATH = METADATA["storage"]["pgdata"]["location"] charm = None diff --git a/tests/integration/juju_.py b/tests/integration/juju_.py new file mode 100644 index 0000000000..a2d250e4fc --- /dev/null +++ b/tests/integration/juju_.py @@ -0,0 +1,10 @@ +# Copyright 2023 Canonical Ltd. +# See LICENSE file for licensing details. + +import importlib.metadata + +# libjuju version != juju agent version, but the major version should be identical—which is good +# enough to check for secrets +_libjuju_version = importlib.metadata.version("juju") + +juju_major_version = int(_libjuju_version.split(".")[0]) diff --git a/tests/integration/markers.py b/tests/integration/markers.py new file mode 100644 index 0000000000..6dc2d6949f --- /dev/null +++ b/tests/integration/markers.py @@ -0,0 +1,11 @@ +# Copyright 2023 Canonical Ltd. +# See LICENSE file for licensing details. + + +import pytest + +from .juju_ import juju_major_version + +juju2 = pytest.mark.skipif(juju_major_version != 2, reason="Requires juju 2") +juju3 = pytest.mark.skipif(juju_major_version != 3, reason="Requires juju 3") +juju_secrets = pytest.mark.skipif(juju_major_version < 3, reason="Requires juju secrets") diff --git a/tests/integration/new_relations/__init__.py b/tests/integration/new_relations/__init__.py new file mode 100644 index 0000000000..db3bfe1a65 --- /dev/null +++ b/tests/integration/new_relations/__init__.py @@ -0,0 +1,2 @@ +# Copyright 2023 Canonical Ltd. +# See LICENSE file for licensing details. diff --git a/tests/integration/new_relations/test_new_relations.py b/tests/integration/new_relations/test_new_relations.py index 05b93e7567..fc7886d674 100644 --- a/tests/integration/new_relations/test_new_relations.py +++ b/tests/integration/new_relations/test_new_relations.py @@ -14,12 +14,12 @@ from pytest_operator.plugin import OpsTest from tenacity import Retrying, stop_after_attempt, wait_fixed -from tests.integration.helpers import ( +from ..helpers import ( CHARM_SERIES, check_database_users_existence, scale_application, ) -from tests.integration.new_relations.helpers import ( +from .helpers import ( build_connection_string, check_relation_data_existence, get_application_relation_data, @@ -43,6 +43,7 @@ INVALID_EXTRA_USER_ROLE_BLOCKING_MESSAGE = "invalid role(s) for extra user roles" +@pytest.mark.group(1) @pytest.mark.abort_on_fail async def test_database_relation_with_charm_libraries(ops_test: OpsTest, database_charm): """Test basic functionality of database relation interface.""" @@ -157,6 +158,7 @@ async def test_database_relation_with_charm_libraries(ops_test: OpsTest, databas cursor.execute("DROP TABLE test;") +@pytest.mark.group(1) async def test_user_with_extra_roles(ops_test: OpsTest): """Test superuser actions and the request for more permissions.""" # Get the connection string to connect to the database. @@ -177,6 +179,7 @@ async def test_user_with_extra_roles(ops_test: OpsTest): connection.close() +@pytest.mark.group(1) async def test_two_applications_doesnt_share_the_same_relation_data(ops_test: OpsTest): """Test that two different application connect to the database with different credentials.""" # Set some variables to use in this test. @@ -230,6 +233,7 @@ async def test_two_applications_doesnt_share_the_same_relation_data(ops_test: Op psycopg2.connect(connection_string) +@pytest.mark.group(1) async def test_an_application_can_connect_to_multiple_database_clusters( ops_test: OpsTest, database_charm ): @@ -262,6 +266,7 @@ async def test_an_application_can_connect_to_multiple_database_clusters( assert application_connection_string != another_application_connection_string +@pytest.mark.group(1) async def test_an_application_can_connect_to_multiple_aliased_database_clusters( ops_test: OpsTest, database_charm ): @@ -299,6 +304,7 @@ async def test_an_application_can_connect_to_multiple_aliased_database_clusters( assert application_connection_string != another_application_connection_string +@pytest.mark.group(1) @pytest.mark.abort_on_fail async def test_an_application_can_request_multiple_databases(ops_test: OpsTest): """Test that an application can request additional databases using the same interface.""" @@ -322,6 +328,7 @@ async def test_an_application_can_request_multiple_databases(ops_test: OpsTest): assert first_database_connection_string != second_database_connection_string +@pytest.mark.group(1) async def test_no_read_only_endpoint_in_standalone_cluster(ops_test: OpsTest): """Test that there is no read-only endpoint in a standalone cluster.""" async with ops_test.fast_forward(): @@ -339,6 +346,7 @@ async def test_no_read_only_endpoint_in_standalone_cluster(ops_test: OpsTest): ) +@pytest.mark.group(1) async def test_read_only_endpoint_in_scaled_up_cluster(ops_test: OpsTest): """Test that there is read-only endpoint in a scaled up cluster.""" async with ops_test.fast_forward(): @@ -356,6 +364,7 @@ async def test_read_only_endpoint_in_scaled_up_cluster(ops_test: OpsTest): ) +@pytest.mark.group(1) async def test_relation_broken(ops_test: OpsTest): """Test that the user is removed when the relation is broken.""" async with ops_test.fast_forward(): @@ -376,6 +385,7 @@ async def test_relation_broken(ops_test: OpsTest): ) +@pytest.mark.group(1) async def test_restablish_relation(ops_test: OpsTest): """Test that a previously broken relation would be functional if restored.""" # Relate the charms and wait for them exchanging some connection data. @@ -413,6 +423,7 @@ async def test_restablish_relation(ops_test: OpsTest): assert data[0] == "other data" +@pytest.mark.group(1) @pytest.mark.abort_on_fail async def test_relation_with_no_database_name(ops_test: OpsTest): """Test that a relation with no database name doesn't block the charm.""" @@ -430,6 +441,7 @@ async def test_relation_with_no_database_name(ops_test: OpsTest): await ops_test.model.wait_for_idle(apps=APP_NAMES, status="active", raise_on_blocked=True) +@pytest.mark.group(1) @pytest.mark.abort_on_fail async def test_admin_role(ops_test: OpsTest): """Test that the admin role gives access to all the databases.""" @@ -515,6 +527,7 @@ async def test_admin_role(ops_test: OpsTest): connection.close() +@pytest.mark.group(1) async def test_invalid_extra_user_roles(ops_test: OpsTest): async with ops_test.fast_forward(): # Remove the relation between the database and the first data integrator. @@ -576,6 +589,7 @@ async def test_invalid_extra_user_roles(ops_test: OpsTest): ) +@pytest.mark.group(1) async def test_discourse(ops_test: OpsTest): # Deploy Discourse and Redis. await gather( @@ -651,6 +665,7 @@ async def test_discourse(ops_test: OpsTest): await ops_test.model.wait_for_idle(apps=[other_discourse_app_name], status="active") +@pytest.mark.group(1) async def test_indico_datatabase(ops_test: OpsTest) -> None: """Tests deploying and relating to the Indico charm.""" async with ops_test.fast_forward(fast_interval="30s"): diff --git a/tests/integration/test_backups.py b/tests/integration/test_backups.py index 73d224c667..d22e5a4ad0 100644 --- a/tests/integration/test_backups.py +++ b/tests/integration/test_backups.py @@ -10,7 +10,7 @@ from pytest_operator.plugin import OpsTest from tenacity import Retrying, stop_after_attempt, wait_exponential -from tests.integration.helpers import ( +from .helpers import ( DATABASE_APP_NAME, build_and_deploy, construct_endpoint, @@ -80,11 +80,13 @@ async def cloud_configs(ops_test: OpsTest, github_secrets) -> None: bucket_object.delete() +@pytest.mark.group(1) async def test_none() -> None: """Empty test so that the suite will not fail if all tests are skippedi.""" pass +@pytest.mark.group(1) @pytest.mark.abort_on_fail async def test_backup_and_restore(ops_test: OpsTest, cloud_configs: Tuple[Dict, Dict]) -> None: """Build and deploy two units of PostgreSQL and then test the backup and restore actions.""" @@ -208,6 +210,7 @@ async def test_backup_and_restore(ops_test: OpsTest, cloud_configs: Tuple[Dict, await ops_test.model.remove_application(TLS_CERTIFICATES_APP_NAME, block_until_done=True) +@pytest.mark.group(1) async def test_restore_on_new_cluster(ops_test: OpsTest, github_secrets) -> None: """Test that is possible to restore a backup to another PostgreSQL cluster.""" database_app_name = f"new-{DATABASE_APP_NAME}" @@ -280,6 +283,7 @@ async def test_restore_on_new_cluster(ops_test: OpsTest, github_secrets) -> None connection.close() +@pytest.mark.group(1) async def test_invalid_config_and_recovery_after_fixing_it( ops_test: OpsTest, cloud_configs: Tuple[Dict, Dict] ) -> None: diff --git a/tests/integration/test_charm.py b/tests/integration/test_charm.py index 6fd2872ad4..a5035a066f 100644 --- a/tests/integration/test_charm.py +++ b/tests/integration/test_charm.py @@ -13,9 +13,10 @@ from pytest_operator.plugin import OpsTest from tenacity import Retrying, stop_after_delay, wait_fixed -from tests.helpers import METADATA, STORAGE_PATH -from tests.integration.helpers import ( +from .helpers import ( CHARM_SERIES, + METADATA, + STORAGE_PATH, build_and_deploy, convert_records_to_dict, db_connect, @@ -35,6 +36,7 @@ UNIT_IDS = [0, 1, 2] +@pytest.mark.group(1) @pytest.mark.abort_on_fail @pytest.mark.skip_if_deployed async def test_build_and_deploy(ops_test: OpsTest): @@ -48,6 +50,7 @@ async def test_build_and_deploy(ops_test: OpsTest): assert ops_test.model.applications[APP_NAME].units[unit_id].workload_status == "active" +@pytest.mark.group(1) async def test_application_created_required_resources(ops_test: OpsTest) -> None: # Compare the k8s resources that the charm and Patroni should create with # the currently created k8s resources. @@ -57,6 +60,7 @@ async def test_application_created_required_resources(ops_test: OpsTest) -> None assert set(existing_resources) == set(expected_resources) +@pytest.mark.group(1) @pytest.mark.parametrize("unit_id", UNIT_IDS) async def test_labels_consistency_across_pods(ops_test: OpsTest, unit_id: int) -> None: model = ops_test.model.info @@ -68,6 +72,7 @@ async def test_labels_consistency_across_pods(ops_test: OpsTest, unit_id: int) - assert pod.metadata.labels["cluster-name"] == f"patroni-{APP_NAME}" +@pytest.mark.group(1) @pytest.mark.parametrize("unit_id", UNIT_IDS) async def test_database_is_up(ops_test: OpsTest, unit_id: int): # Query Patroni REST API and check the status that indicates @@ -77,6 +82,7 @@ async def test_database_is_up(ops_test: OpsTest, unit_id: int): assert result.status_code == 200 +@pytest.mark.group(1) @pytest.mark.parametrize("unit_id", UNIT_IDS) async def test_exporter_is_up(ops_test: OpsTest, unit_id: int): # Query exporter metrics endpoint and check the status that indicates @@ -89,6 +95,7 @@ async def test_exporter_is_up(ops_test: OpsTest, unit_id: int): ), "Scrape error in postgresql_prometheus_exporter" +@pytest.mark.group(1) @pytest.mark.parametrize("unit_id", UNIT_IDS) async def test_settings_are_correct(ops_test: OpsTest, unit_id: int): password = await get_password(ops_test) @@ -162,6 +169,7 @@ async def test_settings_are_correct(ops_test: OpsTest, unit_id: int): assert settings["postgresql"]["remove_data_directory_on_diverged_timelines"] is True +@pytest.mark.group(1) async def test_postgresql_parameters_change(ops_test: OpsTest) -> None: """Test that's possible to change PostgreSQL parameters.""" await ops_test.model.applications[APP_NAME].set_config( @@ -198,6 +206,7 @@ async def test_postgresql_parameters_change(ops_test: OpsTest) -> None: assert settings["lc_monetary"] == "en_GB.utf8" +@pytest.mark.group(1) async def test_cluster_is_stable_after_leader_deletion(ops_test: OpsTest) -> None: """Tests that the cluster maintains a primary after the primary is deleted.""" # Find the current primary unit. @@ -220,6 +229,7 @@ async def test_cluster_is_stable_after_leader_deletion(ops_test: OpsTest) -> Non assert await get_primary(ops_test, down_unit=primary) != "None" +@pytest.mark.group(1) @pytest.mark.unstable async def test_scale_down_and_up(ops_test: OpsTest): """Test data is replicated to new units after a scale up.""" @@ -246,6 +256,7 @@ async def test_scale_down_and_up(ops_test: OpsTest): await scale_application(ops_test, APP_NAME, initial_scale) +@pytest.mark.group(1) async def test_persist_data_through_graceful_restart(ops_test: OpsTest): """Test data persists through a graceful restart.""" primary = await get_primary(ops_test) @@ -274,6 +285,7 @@ async def test_persist_data_through_graceful_restart(ops_test: OpsTest): connection.cursor().execute("SELECT * FROM gracetest;") +@pytest.mark.group(1) async def test_persist_data_through_failure(ops_test: OpsTest): """Test data persists through a failure.""" primary = await get_primary(ops_test) @@ -314,6 +326,7 @@ async def test_persist_data_through_failure(ops_test: OpsTest): connection.cursor().execute("SELECT * FROM failtest;") +@pytest.mark.group(1) async def test_automatic_failover_after_leader_issue(ops_test: OpsTest) -> None: """Tests that an automatic failover is triggered after an issue happens in the leader.""" # Find the current primary unit. @@ -331,6 +344,7 @@ async def test_automatic_failover_after_leader_issue(ops_test: OpsTest) -> None: assert await get_primary(ops_test) != "None" +@pytest.mark.group(1) async def test_application_removal(ops_test: OpsTest) -> None: # Remove the application to trigger some hooks (like peer relation departed). await ops_test.model.applications[APP_NAME].remove() @@ -356,6 +370,7 @@ async def test_application_removal(ops_test: OpsTest) -> None: assert APP_NAME not in ops_test.model.applications +@pytest.mark.group(1) async def test_redeploy_charm_same_model(ops_test: OpsTest): """Redeploy the charm in the same model to test that it works.""" charm = await ops_test.build_charm(".") @@ -378,6 +393,7 @@ async def test_redeploy_charm_same_model(ops_test: OpsTest): ) +@pytest.mark.group(1) async def test_redeploy_charm_same_model_after_forcing_removal(ops_test: OpsTest) -> None: """Redeploy the charm in the same model to test that it works after a forceful removal.""" return_code, _, stderr = await ops_test.juju( @@ -421,6 +437,7 @@ async def test_redeploy_charm_same_model_after_forcing_removal(ops_test: OpsTest ) +@pytest.mark.group(1) async def test_storage_with_more_restrictive_permissions(ops_test: OpsTest): """Test that the charm can be deployed with a storage with more restrictive permissions.""" app_name = f"test-storage-{APP_NAME}" diff --git a/tests/integration/test_db.py b/tests/integration/test_db.py index fa312de3e8..428b4c66f5 100644 --- a/tests/integration/test_db.py +++ b/tests/integration/test_db.py @@ -4,9 +4,10 @@ import logging from asyncio import gather +import pytest from pytest_operator.plugin import OpsTest -from tests.integration.helpers import ( +from .helpers import ( APPLICATION_NAME, CHARM_SERIES, DATABASE_APP_NAME, @@ -27,6 +28,7 @@ logger = logging.getLogger(__name__) +@pytest.mark.group(1) async def test_finos_waltz_db(ops_test: OpsTest) -> None: """Deploy Finos Waltz to test the 'db' relation. @@ -85,6 +87,7 @@ async def test_finos_waltz_db(ops_test: OpsTest) -> None: await ops_test.model.remove_application(FINOS_WALTZ_APP_NAME, block_until_done=True) +@pytest.mark.group(1) async def test_extensions_blocking(ops_test: OpsTest) -> None: await ops_test.model.deploy( APPLICATION_NAME, diff --git a/tests/integration/test_db_admin.py b/tests/integration/test_db_admin.py index f257c1cf67..4e1e928059 100644 --- a/tests/integration/test_db_admin.py +++ b/tests/integration/test_db_admin.py @@ -6,7 +6,7 @@ import pytest as pytest from pytest_operator.plugin import OpsTest -from tests.integration.helpers import ( +from .helpers import ( DATABASE_APP_NAME, build_and_deploy, check_database_creation, @@ -20,6 +20,7 @@ DATABASE_UNITS = 3 +@pytest.mark.group(1) @pytest.mark.abort_on_fail async def test_discourse_from_discourse_charmers(ops_test: OpsTest): # Build and deploy charm from local source folder (and also redis from Charmhub). diff --git a/tests/integration/test_password_rotation.py b/tests/integration/test_password_rotation.py index 867d608e82..57dc8206f2 100644 --- a/tests/integration/test_password_rotation.py +++ b/tests/integration/test_password_rotation.py @@ -7,9 +7,10 @@ import pytest from pytest_operator.plugin import OpsTest -from tests.helpers import METADATA -from tests.integration.helpers import ( +from . import markers +from .helpers import ( CHARM_SERIES, + METADATA, check_patroni, get_leader_unit, get_password, @@ -20,6 +21,7 @@ APP_NAME = METADATA["name"] +@pytest.mark.group(1) @pytest.mark.abort_on_fail @pytest.mark.skip_if_deployed async def test_deploy_active(ops_test: OpsTest): @@ -40,6 +42,7 @@ async def test_deploy_active(ops_test: OpsTest): await ops_test.model.wait_for_idle(apps=[APP_NAME], status="active", timeout=1000) +@pytest.mark.group(1) async def test_password_rotation(ops_test: OpsTest): """Test password rotation action.""" # Get the initial passwords set for the system users. @@ -78,7 +81,8 @@ async def test_password_rotation(ops_test: OpsTest): assert await check_patroni(ops_test, unit.name, restart_time) -@pytest.mark.juju3 +@pytest.mark.group(1) +@markers.juju_secrets async def test_password_from_secret_same_as_cli(ops_test: OpsTest): """Checking if password is same as returned by CLI. @@ -102,6 +106,7 @@ async def test_password_from_secret_same_as_cli(ops_test: OpsTest): assert data[secret_id]["content"]["Data"]["replication-password"] == password +@pytest.mark.group(1) async def test_empty_password(ops_test: OpsTest) -> None: """Test that the password can't be set to an empty string.""" leader_unit = await get_leader_unit(ops_test, APP_NAME) @@ -114,6 +119,7 @@ async def test_empty_password(ops_test: OpsTest) -> None: assert password == "None" +@pytest.mark.group(1) async def test_no_password_change_on_invalid_password(ops_test: OpsTest) -> None: """Test that in general, there is no change when password validation fails.""" leader_unit = await get_leader_unit(ops_test, APP_NAME) diff --git a/tests/integration/test_plugins.py b/tests/integration/test_plugins.py index d538b853d5..9ee8ec657d 100644 --- a/tests/integration/test_plugins.py +++ b/tests/integration/test_plugins.py @@ -7,7 +7,7 @@ import pytest as pytest from pytest_operator.plugin import OpsTest -from tests.integration.helpers import ( +from .helpers import ( DATABASE_APP_NAME, build_and_deploy, db_connect, @@ -59,6 +59,7 @@ MODDATETIME_EXTENSION_STATEMENT = "CREATE TABLE mdt (moddate timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL);CREATE TRIGGER mdt_moddatetime BEFORE UPDATE ON mdt FOR EACH ROW EXECUTE PROCEDURE moddatetime (moddate);" +@pytest.mark.group(1) @pytest.mark.abort_on_fail async def test_plugins(ops_test: OpsTest) -> None: """Build and deploy one unit of PostgreSQL and then test the available plugins.""" diff --git a/tests/integration/test_tls.py b/tests/integration/test_tls.py index 1a3b90263d..9f2c3894c2 100644 --- a/tests/integration/test_tls.py +++ b/tests/integration/test_tls.py @@ -8,7 +8,7 @@ from pytest_operator.plugin import OpsTest from tenacity import Retrying, stop_after_delay, wait_fixed -from tests.integration.helpers import ( +from .helpers import ( DATABASE_APP_NAME, build_and_deploy, check_database_creation, @@ -32,12 +32,14 @@ DATABASE_UNITS = 3 +@pytest.mark.group(1) @pytest.mark.abort_on_fail async def test_build_and_deploy(ops_test: OpsTest) -> None: """Build and deploy three units of PostgreSQL.""" await build_and_deploy(ops_test, DATABASE_UNITS, wait_for_idle=False) +@pytest.mark.group(1) async def check_tls_rewind(ops_test: OpsTest) -> None: """Checks if TLS was used by rewind.""" for unit in ops_test.model.applications[DATABASE_APP_NAME].units: @@ -57,6 +59,7 @@ async def check_tls_rewind(ops_test: OpsTest) -> None: ), "TLS is not being used on pg_rewind connections" +@pytest.mark.group(1) async def test_mattermost_db(ops_test: OpsTest) -> None: """Deploy Mattermost to test the 'db' relation. diff --git a/tox.ini b/tox.ini index 8712d4f8f2..ca559fffbc 100644 --- a/tox.ini +++ b/tox.ini @@ -67,235 +67,6 @@ commands = -m pytest -v --tb native -s {posargs} {[vars]tests_path}/unit poetry run coverage report -[testenv:backup-integration] -description = Run backup integration tests -set_env = - {[testenv]set_env} - # Workaround for https://github.com/python-poetry/poetry/issues/6958 - POETRY_INSTALLER_PARALLEL = false -pass_env = - CI - GITHUB_OUTPUT - SECRETS_FROM_GITHUB -allowlist_externals = - {[testenv:pack-wrapper]allowlist_externals} -commands_pre = - poetry install --only integration --no-root - {[testenv:pack-wrapper]commands_pre} -commands = - poetry run pytest -v --tb native --log-cli-level=INFO -s {posargs} {[vars]tests_path}/integration/test_backups.py -commands_post = - {[testenv:pack-wrapper]commands_post} - -[testenv:charm-integration] -description = Run charm integration tests -set_env = - {[testenv]set_env} - # Workaround for https://github.com/python-poetry/poetry/issues/6958 - POETRY_INSTALLER_PARALLEL = false -pass_env = - CI - GITHUB_OUTPUT -allowlist_externals = - {[testenv:pack-wrapper]allowlist_externals} -commands_pre = - poetry install --only integration --no-root - {[testenv:pack-wrapper]commands_pre} -commands = - poetry run pytest -v --tb native --log-cli-level=INFO -s {posargs} {[vars]tests_path}/integration/test_charm.py -commands_post = - {[testenv:pack-wrapper]commands_post} - -[testenv:database-relation-integration] -description = Run database relation integration tests -set_env = - {[testenv]set_env} - # Workaround for https://github.com/python-poetry/poetry/issues/6958 - POETRY_INSTALLER_PARALLEL = false -pass_env = - CI - GITHUB_OUTPUT -allowlist_externals = - {[testenv:pack-wrapper]allowlist_externals} -commands_pre = - poetry install --only integration --no-root - {[testenv:pack-wrapper]commands_pre} -commands = - poetry run pytest -v --tb native --log-cli-level=INFO -s {posargs} {[vars]tests_path}/integration/new_relations/test_new_relations.py -commands_post = - {[testenv:pack-wrapper]commands_post} - -[testenv:db-relation-integration] -description = Run db relation integration tests -set_env = - {[testenv]set_env} - # Workaround for https://github.com/python-poetry/poetry/issues/6958 - POETRY_INSTALLER_PARALLEL = false -pass_env = - CI - GITHUB_OUTPUT -allowlist_externals = - {[testenv:pack-wrapper]allowlist_externals} -commands_pre = - poetry install --only integration --no-root - {[testenv:pack-wrapper]commands_pre} -commands = - poetry run pytest -v --tb native --log-cli-level=INFO -s {posargs} {[vars]tests_path}/integration/test_db.py -commands_post = - {[testenv:pack-wrapper]commands_post} - -[testenv:db-admin-relation-integration] -description = Run db-admin relation integration tests -set_env = - {[testenv]set_env} - # Workaround for https://github.com/python-poetry/poetry/issues/6958 - POETRY_INSTALLER_PARALLEL = false -pass_env = - CI - GITHUB_OUTPUT -allowlist_externals = - {[testenv:pack-wrapper]allowlist_externals} -commands_pre = - poetry install --only integration --no-root - {[testenv:pack-wrapper]commands_pre} -commands = - poetry run pytest -v --tb native --log-cli-level=INFO -s {posargs} {[vars]tests_path}/integration/test_db_admin.py -commands_post = - {[testenv:pack-wrapper]commands_post} - -[testenv:ha-replication-integration] -description = Run high availability replication integration tests -set_env = - {[testenv]set_env} - # Workaround for https://github.com/python-poetry/poetry/issues/6958 - POETRY_INSTALLER_PARALLEL = false -pass_env = - CI - GITHUB_OUTPUT -allowlist_externals = - {[testenv:pack-wrapper]allowlist_externals} -commands_pre = - poetry install --only integration --no-root - {[testenv:pack-wrapper]commands_pre} -commands = - poetry run pytest -v --tb native --log-cli-level=INFO -s {posargs} {[vars]tests_path}/integration/ha_tests/test_replication.py -commands_post = - {[testenv:pack-wrapper]commands_post} - -[testenv:ha-self-healing-integration] -description = Run high availability self healing integration tests -set_env = - {[testenv]set_env} - # Workaround for https://github.com/python-poetry/poetry/issues/6958 - POETRY_INSTALLER_PARALLEL = false -pass_env = - CI - GITHUB_OUTPUT -allowlist_externals = - {[testenv:pack-wrapper]allowlist_externals} -commands_pre = - poetry install --only integration --no-root - {[testenv:pack-wrapper]commands_pre} -commands = - poetry run pytest -v --tb native --log-cli-level=INFO -s {posargs} {[vars]tests_path}/integration/ha_tests/test_self_healing.py -commands_post = - {[testenv:pack-wrapper]commands_post} - -[testenv:password-rotation-integration] -description = Run password rotation integration tests -set_env = - {[testenv]set_env} - # Workaround for https://github.com/python-poetry/poetry/issues/6958 - POETRY_INSTALLER_PARALLEL = false -pass_env = - CI - GITHUB_OUTPUT -allowlist_externals = - {[testenv:pack-wrapper]allowlist_externals} -commands_pre = - poetry install --only integration --no-root - {[testenv:pack-wrapper]commands_pre} -commands = - poetry run pytest -v --tb native --log-cli-level=INFO -s {posargs} {[vars]tests_path}/integration/test_password_rotation.py -commands_post = - {[testenv:pack-wrapper]commands_post} - -[testenv:plugins-integration] -description = Run plugins integration tests -set_env = - {[testenv]set_env} - # Workaround for https://github.com/python-poetry/poetry/issues/6958 - POETRY_INSTALLER_PARALLEL = false -pass_env = - CI - GITHUB_OUTPUT -allowlist_externals = - {[testenv:pack-wrapper]allowlist_externals} -commands_pre = - poetry install --only integration --no-root - {[testenv:pack-wrapper]commands_pre} -commands = - poetry run pytest -v --tb native --log-cli-level=INFO -s {posargs} {[vars]tests_path}/integration/test_plugins.py -commands_post = - {[testenv:pack-wrapper]commands_post} - -[testenv:tls-integration] -description = Run TLS integration tests -set_env = - {[testenv]set_env} - # Workaround for https://github.com/python-poetry/poetry/issues/6958 - POETRY_INSTALLER_PARALLEL = false -pass_env = - CI - GITHUB_OUTPUT -allowlist_externals = - {[testenv:pack-wrapper]allowlist_externals} -commands_pre = - poetry install --only integration --no-root - {[testenv:pack-wrapper]commands_pre} -commands = - poetry run pytest -v --tb native --log-cli-level=INFO -s {posargs} {[vars]tests_path}/integration/test_tls.py -commands_post = - {[testenv:pack-wrapper]commands_post} - -[testenv:upgrade-integration] -description = Run upgrade integration tests -set_env = - {[testenv]set_env} - # Workaround for https://github.com/python-poetry/poetry/issues/6958 - POETRY_INSTALLER_PARALLEL = false -pass_env = - CI - GITHUB_OUTPUT -allowlist_externals = - {[testenv:pack-wrapper]allowlist_externals} -commands_pre = - poetry install --only integration --no-root - {[testenv:pack-wrapper]commands_pre} -commands = - poetry run pytest -v --tb native --log-cli-level=INFO -s {posargs} {[vars]tests_path}/integration/ha_tests/test_upgrade.py -commands_post = - {[testenv:pack-wrapper]commands_post} - -[testenv:upgrade-from-stable-integration] -description = Run upgrade from stable integration tests -set_env = - {[testenv]set_env} - # Workaround for https://github.com/python-poetry/poetry/issues/6958 - POETRY_INSTALLER_PARALLEL = false -pass_env = - CI - GITHUB_OUTPUT -allowlist_externals = - {[testenv:pack-wrapper]allowlist_externals} -commands_pre = - poetry install --only integration --no-root - {[testenv:pack-wrapper]commands_pre} -commands = - poetry run pytest -v --tb native --log-cli-level=INFO -s {posargs} {[vars]tests_path}/integration/ha_tests/test_upgrade_from_stable.py -commands_post = - {[testenv:pack-wrapper]commands_post} - [testenv:integration] description = Run integration tests set_env =