From d6673a2d6effcc63bdcb63d459a84962f2152dc0 Mon Sep 17 00:00:00 2001 From: Iman Enami Date: Thu, 19 Mar 2026 13:11:41 +0400 Subject: [PATCH 1/5] refactor: use jubilant for int. tests --- tests/v0/integration/conftest.py | 96 +- tests/v0/integration/helpers.py | 136 ++- tests/v0/integration/test_charm.py | 837 +++++++++--------- tests/v0/integration/test_kafka_charm.py | 211 ++--- .../integration/test_kafka_connect_charm.py | 68 +- tests/v0/integration/test_opensearch_charm.py | 121 ++- tests/v0/integration/test_rolling_upgrade.py | 361 ++++---- ...t_rolling_upgrade_from_specific_version.py | 265 +++--- tests/v0/integration/test_s3_charm.py | 67 +- tests/v0/integration/test_secrets.py | 58 +- tests/v0/integration/test_status.py | 103 ++- tox.ini | 3 +- 12 files changed, 1135 insertions(+), 1191 deletions(-) diff --git a/tests/v0/integration/conftest.py b/tests/v0/integration/conftest.py index 32674fb7..eaebff4f 100644 --- a/tests/v0/integration/conftest.py +++ b/tests/v0/integration/conftest.py @@ -6,14 +6,15 @@ import os import shutil from datetime import datetime -from pathlib import Path from subprocess import check_call, check_output import pytest -from pytest_operator.plugin import OpsTest +from jubilant_adapters import JujuFixture, temp_model_fixture logger = logging.getLogger(__name__) +USE_CACHED_BUILD = bool(os.environ.get("CI", False)) + @pytest.fixture(scope="session") def dp_libs_ubuntu_series(pytestconfig) -> str: @@ -21,31 +22,40 @@ def dp_libs_ubuntu_series(pytestconfig) -> str: return pytestconfig.option.os_series -@pytest.fixture(scope="module") -def ops_test(ops_test: OpsTest, pytestconfig) -> OpsTest: - """Re-defining OpsTest.build_charm in a way that it takes CI caching and build parameters into account. - - Build parameters (for charms available for multiple OS versions) are considered both when building the - charm, or when fetching pre-built, CI cached version of it. - """ - _build_charm = ops_test.build_charm +def pytest_addoption(parser): + """Defines pytest parsers.""" + parser.addoption( + "--model", + action="store", + help="Juju model to use; if not provided, a new model " + "will be created for each test which requires one", + ) + parser.addoption( + "--keep-models", + action="store_true", + help="Keep models handled by opstest, can be overridden in track_model", + ) - # Add bases_index option (indicating which OS version to use) - # when building the charm within the scope of the test run - async def build_charm(charm_path, bases_index: int = None) -> Path: - if not bases_index and pytestconfig.option.build_bases_index is not None: - bases_index = pytestconfig.option.build_bases_index - logger.info(f"Building charm {charm_path} with base index {bases_index}") +@pytest.fixture(scope="module") +def juju(request: pytest.FixtureRequest): + """Pytest fixture that wraps :meth:`jubilant.with_model`. - return await _build_charm(charm_path, bases_index=bases_index) + This adds command line parameter ``--keep-models`` (see help for details). + """ + model = request.config.getoption("--model") + keep_models = bool(request.config.getoption("--keep-models")) - ops_test.build_charm = build_charm - return ops_test + if model: + juju = JujuFixture(model=model) + yield juju + else: + with temp_model_fixture(keep=keep_models) as juju: + yield juju @pytest.fixture(scope="module", autouse=True) -def copy_data_interfaces_library_into_charm(ops_test: OpsTest): +def copy_data_interfaces_library_into_charm(juju: JujuFixture): """Copy the data_interfaces library to the different charm folder.""" library_path = "lib/charms/data_platform_libs/v0/data_interfaces.py" install_path = "tests/v0/integration/database-charm/" + library_path @@ -65,7 +75,7 @@ def copy_data_interfaces_library_into_charm(ops_test: OpsTest): @pytest.fixture(scope="module", autouse=True) -def copy_s3_library_into_charm(ops_test: OpsTest): +def copy_s3_library_into_charm(juju: JujuFixture): """Copy the s3 library to the applications charm folder.""" library_path = "lib/charms/data_platform_libs/v0/s3.py" install_path_provider = "tests/v0/integration/s3-charm/" + library_path @@ -75,71 +85,71 @@ def copy_s3_library_into_charm(ops_test: OpsTest): @pytest.fixture(scope="module") -async def application_charm(ops_test: OpsTest): +def application_charm(juju: JujuFixture): """Build the application charm.""" charm_path = "tests/v0/integration/application-charm" - charm = await ops_test.build_charm(charm_path) + charm = juju.ext.build_charm(charm_path, use_cache=USE_CACHED_BUILD) return charm @pytest.fixture(scope="module") -async def application_charm_etcd_client(ops_test: OpsTest): +def application_charm_etcd_client(juju: JujuFixture): """Build the application charm.""" charm_path = "tests/v0/integration/application-charm-etcd-client" - charm = await ops_test.build_charm(charm_path) + charm = juju.ext.build_charm(charm_path, use_cache=USE_CACHED_BUILD) return charm @pytest.fixture(scope="module") -async def database_charm(ops_test: OpsTest): +def database_charm(juju: JujuFixture): """Build the database charm.""" charm_path = "tests/v0/integration/database-charm" - charm = await ops_test.build_charm(charm_path) + charm = juju.ext.build_charm(charm_path, use_cache=USE_CACHED_BUILD) return charm @pytest.fixture(scope="module") -async def dummy_database_charm(ops_test: OpsTest): +def dummy_database_charm(juju: JujuFixture): """Build the database charm.""" charm_path = "tests/v0/integration/dummy-database-charm" - charm = await ops_test.build_charm(charm_path) + charm = juju.ext.build_charm(charm_path, use_cache=USE_CACHED_BUILD) return charm @pytest.fixture(scope="module") -async def application_s3_charm(ops_test: OpsTest): +def application_s3_charm(juju: JujuFixture): """Build the application-s3 charm.""" charm_path = "tests/v0/integration/application-s3-charm" - charm = await ops_test.build_charm(charm_path) + charm = juju.ext.build_charm(charm_path, use_cache=USE_CACHED_BUILD) return charm @pytest.fixture(scope="module") -async def s3_charm(ops_test: OpsTest): +def s3_charm(juju: JujuFixture): """Build the S3 charm.""" charm_path = "tests/v0/integration/s3-charm" - charm = await ops_test.build_charm(charm_path) + charm = juju.ext.build_charm(charm_path, use_cache=USE_CACHED_BUILD) return charm @pytest.fixture(scope="module") -async def kafka_charm(ops_test: OpsTest): +def kafka_charm(juju: JujuFixture): """Build the Kafka charm.""" charm_path = "tests/v0/integration/kafka-charm" - charm = await ops_test.build_charm(charm_path) + charm = juju.ext.build_charm(charm_path, use_cache=USE_CACHED_BUILD) return charm @pytest.fixture(scope="module") -async def kafka_connect_charm(ops_test: OpsTest): +def kafka_connect_charm(juju: JujuFixture): """Build the Kafka Connect dummy charm.""" charm_path = "tests/v0/integration/kafka-connect-charm" - charm = await ops_test.build_charm(charm_path) + charm = juju.ext.build_charm(charm_path, use_cache=USE_CACHED_BUILD) return charm @pytest.fixture(scope="module") -async def opensearch_charm(ops_test: OpsTest): +def opensearch_charm(juju: JujuFixture): """Build the OpenSearch charm. TODO we could simplify a lot of these charm builds by having a single test charm that includes @@ -147,20 +157,20 @@ async def opensearch_charm(ops_test: OpsTest): data-integrator charm repo. """ charm_path = "tests/v0/integration/opensearch-charm" - charm = await ops_test.build_charm(charm_path) + charm = juju.ext.build_charm(charm_path, use_cache=USE_CACHED_BUILD) return charm @pytest.fixture(scope="module") -async def secrets_charm(ops_test: OpsTest): +def secrets_charm(juju: JujuFixture): """Build the secrets charm.""" charm_path = "tests/v0/integration/secrets-charm" - charm = await ops_test.build_charm(charm_path) + charm = juju.ext.build_charm(charm_path, use_cache=USE_CACHED_BUILD) return charm @pytest.fixture(autouse=True) -async def without_errors(ops_test: OpsTest, request): +def without_errors(juju: JujuFixture, request): """This fixture is to list all those errors that mustn't occur during execution.""" # To be executed after the tests now = datetime.now().strftime("%H:%M:%S.%f")[:-3] @@ -177,7 +187,7 @@ async def without_errors(ops_test: OpsTest, request): if not whitelist: return - _, dbg_log, _ = await ops_test.juju("debug-log", "--ms", "--replay") + _, dbg_log, _ = juju.juju("debug-log", "--ms", "--replay") lines = dbg_log.split("\n") for index, line in enumerate(lines): logitems = line.split(" ") diff --git a/tests/v0/integration/helpers.py b/tests/v0/integration/helpers.py index cc88ee2f..78d1796c 100644 --- a/tests/v0/integration/helpers.py +++ b/tests/v0/integration/helpers.py @@ -6,46 +6,46 @@ from typing import Dict, List, Optional import yaml +from jubilant_adapters import JujuFixture from ops import JujuVersion -from pytest_operator.plugin import OpsTest PROV_SECRET_PREFIX = "secret-" -async def get_juju_secret(ops_test: OpsTest, secret_uri: str) -> Dict[str, str]: +def get_juju_secret(juju: JujuFixture, secret_uri: str) -> Dict[str, str]: """Retrieve juju secret.""" secret_unique_id = secret_uri.split("/")[-1] complete_command = f"show-secret {secret_uri} --reveal --format=json" - _, stdout, _ = await ops_test.juju(*complete_command.split()) + _, stdout, _ = juju.juju(*complete_command.split()) return json.loads(stdout)[secret_unique_id]["content"]["Data"] -async def list_juju_secrets(ops_test: OpsTest) -> List[str]: +def list_juju_secrets(juju: JujuFixture) -> List[str]: """Check if a juju secret does not exist.""" - _, stdout, _ = await ops_test.juju("list-secrets") + _, stdout, _ = juju.juju("list-secrets") data = stdout.split("\n") data = data[1:] return [line.split()[0] for line in data if line] -async def get_leader_id(ops_test: OpsTest, app_name: str) -> int: +def get_leader_id(juju: JujuFixture, app_name: str) -> int: """Returns the unit number of the juju leader unit.""" - for unit in ops_test.model.applications[app_name].units: - if await unit.is_leader_from_status(): + for unit in juju.ext.model.applications[app_name].units: + if unit.is_leader_from_status(): return int(unit.name.split("/")[1]) return -1 -async def get_non_leader_id(ops_test: OpsTest, app_name: str) -> int: +def get_non_leader_id(juju: JujuFixture, app_name: str) -> int: """Returns the unit number of the juju leader unit.""" - for unit in ops_test.model.applications[app_name].units: - if not await unit.is_leader_from_status(): + for unit in juju.ext.model.applications[app_name].units: + if not unit.is_leader_from_status(): return int(unit.name.split("/")[1]) return -1 -async def build_connection_string( - ops_test: OpsTest, +def build_connection_string( + juju: JujuFixture, application_name: str, relation_name: str, *, @@ -55,7 +55,7 @@ async def build_connection_string( """Build a PostgreSQL connection string. Args: - ops_test: The ops test framework instance + juju: The ops test framework instance application_name: The name of the application relation_name: name of the relation to get connection data from relation_id: id of the relation to get connection data from @@ -69,27 +69,27 @@ async def build_connection_string( database = f'{application_name.replace("-", "_")}_{relation_name.replace("-", "_")}' if JujuVersion.from_environ().has_secrets: - secret_uri = await get_application_relation_data( - ops_test, + secret_uri = get_application_relation_data( + juju, application_name, relation_name, f"{PROV_SECRET_PREFIX}user", relation_id, relation_alias, ) - secret_data = await get_juju_secret(ops_test, secret_uri) + secret_data = get_juju_secret(juju, secret_uri) username = secret_data["username"] password = secret_data["password"] else: - username = await get_application_relation_data( - ops_test, application_name, relation_name, "username", relation_id, relation_alias + username = get_application_relation_data( + juju, application_name, relation_name, "username", relation_id, relation_alias ) - password = await get_application_relation_data( - ops_test, application_name, relation_name, "password", relation_id, relation_alias + password = get_application_relation_data( + juju, application_name, relation_name, "password", relation_id, relation_alias ) - endpoints = await get_application_relation_data( - ops_test, application_name, relation_name, "endpoints", relation_id, relation_alias + endpoints = get_application_relation_data( + juju, application_name, relation_name, "endpoints", relation_id, relation_alias ) host = endpoints.split(",")[0].split(":")[0] @@ -97,8 +97,8 @@ async def build_connection_string( return f"dbname='{database}' user='{username}' host='{host}' password='{password}' connect_timeout=10" -async def get_connection_info( - ops_test: OpsTest, +def get_connection_info( + juju: JujuFixture, application_name: str, relation_name: str, relation_id: str = None, @@ -106,7 +106,7 @@ async def get_connection_info( """Build a dictionary that contains the connection info. Args: - ops_test: The ops test framework instance + juju: The ops test framework instance application_name: The name of the application relation_name: name of the relation to get connection data from relation_id: id of the relation to get connection data from @@ -116,38 +116,38 @@ async def get_connection_info( """ # Get the connection data exposed to the application through the relation. - access_key = await get_application_relation_data( - ops_test, application_name, relation_name, "access-key", relation_id + access_key = get_application_relation_data( + juju, application_name, relation_name, "access-key", relation_id ) - secret_key = await get_application_relation_data( - ops_test, application_name, relation_name, "secret-key", relation_id + secret_key = get_application_relation_data( + juju, application_name, relation_name, "secret-key", relation_id ) - endpoint = await get_application_relation_data( - ops_test, application_name, relation_name, "endpoint", relation_id + endpoint = get_application_relation_data( + juju, application_name, relation_name, "endpoint", relation_id ) - bucket = await get_application_relation_data( - ops_test, application_name, relation_name, "bucket", relation_id + bucket = get_application_relation_data( + juju, application_name, relation_name, "bucket", relation_id ) - path = await get_application_relation_data( - ops_test, application_name, relation_name, "path", relation_id + path = get_application_relation_data( + juju, application_name, relation_name, "path", relation_id ) - region = await get_application_relation_data( - ops_test, application_name, relation_name, "region", relation_id + region = get_application_relation_data( + juju, application_name, relation_name, "region", relation_id ) - s3_uri_style = await get_application_relation_data( - ops_test, application_name, relation_name, "s3-uri-style", relation_id + s3_uri_style = get_application_relation_data( + juju, application_name, relation_name, "s3-uri-style", relation_id ) - storage_class = await get_application_relation_data( - ops_test, application_name, relation_name, "storage-class", relation_id + storage_class = get_application_relation_data( + juju, application_name, relation_name, "storage-class", relation_id ) - tls_ca_chain = await get_application_relation_data( - ops_test, application_name, relation_name, "tls-ca-chain", relation_id + tls_ca_chain = get_application_relation_data( + juju, application_name, relation_name, "tls-ca-chain", relation_id ) - s3_api_version = await get_application_relation_data( - ops_test, application_name, relation_name, "s3-api-version", relation_id + s3_api_version = get_application_relation_data( + juju, application_name, relation_name, "s3-api-version", relation_id ) - attributes = await get_application_relation_data( - ops_test, application_name, relation_name, "attributes", relation_id + attributes = get_application_relation_data( + juju, application_name, relation_name, "attributes", relation_id ) connection_info = { @@ -167,13 +167,13 @@ async def get_connection_info( return connection_info -async def get_alias_from_relation_data( - ops_test: OpsTest, unit_name: str, related_unit_name: str +def get_alias_from_relation_data( + juju: JujuFixture, unit_name: str, related_unit_name: str ) -> Optional[str]: """Get the alias that the unit assigned to the related unit application/cluster. Args: - ops_test: The ops test framework instance + juju: The ops test framework instance unit_name: The name of the unit related_unit_name: name of the related unit @@ -185,7 +185,7 @@ async def get_alias_from_relation_data( ValueError if it's not possible to get unit data or if there is no alias on that. """ - raw_data = (await ops_test.juju("show-unit", related_unit_name))[1] + raw_data = (juju.juju("show-unit", related_unit_name))[1] if not raw_data: raise ValueError(f"no unit info could be grabbed for {related_unit_name}") data = yaml.safe_load(raw_data) @@ -208,8 +208,8 @@ async def get_alias_from_relation_data( return relation_data["alias"] -async def get_application_relation_data( - ops_test: OpsTest, +def get_application_relation_data( + juju: JujuFixture, application_name: str, relation_name: str, key: str, @@ -221,7 +221,7 @@ async def get_application_relation_data( """Get relation data for an application. Args: - ops_test: The ops test framework instance + juju: The ops test framework instance application_name: The name of the application relation_name: name of the relation to get connection data from key: key of data to be retrieved @@ -241,7 +241,7 @@ async def get_application_relation_data( and/or alias. """ unit_name = f"{application_name}/0" - raw_data = (await ops_test.juju("show-unit", unit_name))[1] + raw_data = (juju.juju("show-unit", unit_name))[1] if not raw_data: raise ValueError(f"no unit info could be grabbed for {unit_name}") data = yaml.safe_load(raw_data) @@ -256,9 +256,7 @@ async def get_application_relation_data( relation_data = [ v for v in relation_data - if await get_alias_from_relation_data( - ops_test, unit_name, next(iter(v["related-units"])) - ) + if get_alias_from_relation_data(juju, unit_name, next(iter(v["related-units"]))) == relation_alias ] if related_endpoint: @@ -277,27 +275,25 @@ async def get_application_relation_data( return relation_data[0]["local-unit"].get("data", {}).get(key) -async def check_logs(ops_test: OpsTest, strings: str, limit: int = 10) -> bool: +def check_logs(juju: JujuFixture, strings: str, limit: int = 10) -> bool: """Check if any of strings may appear in juju debug-log.""" # juju debug-log may not be flushed yet, thus the "tenacity simulation" for tries in range(5): sleep(3) - _, dbg_log, _ = await ops_test.juju("debug-log", "--no-tail", "--replay") + _, dbg_log, _ = juju.juju("debug-log", "--no-tail", "--replay") if any(text in dbg_log for text in strings): return True return False -async def get_secret_by_label(ops_test, label: str, owner: str = "") -> Dict[str, str]: - secrets_raw = await ops_test.juju("list-secrets") +def get_secret_by_label(juju, label: str, owner: str = "") -> Dict[str, str]: + secrets_raw = juju.juju("list-secrets") secret_ids = [ secret_line.split()[0] for secret_line in secrets_raw[1].split("\n")[1:] if secret_line ] for secret_id in secret_ids: - secret_data_raw = await ops_test.juju( - "show-secret", "--format", "json", "--reveal", secret_id - ) + secret_data_raw = juju.juju("show-secret", "--format", "json", "--reveal", secret_id) secret_data = json.loads(secret_data_raw[1]) if label == secret_data[secret_id].get("label"): @@ -305,16 +301,14 @@ async def get_secret_by_label(ops_test, label: str, owner: str = "") -> Dict[str return secret_data[secret_id]["content"]["Data"] -async def get_secret_revision_by_label(ops_test, label: str, owner: str = "") -> int: - secrets_raw = await ops_test.juju("list-secrets") +def get_secret_revision_by_label(juju, label: str, owner: str = "") -> int: + secrets_raw = juju.juju("list-secrets") secret_ids = [ secret_line.split()[0] for secret_line in secrets_raw[1].split("\n")[1:] if secret_line ] for secret_id in secret_ids: - secret_data_raw = await ops_test.juju( - "show-secret", "--format", "json", "--reveal", secret_id - ) + secret_data_raw = juju.juju("show-secret", "--format", "json", "--reveal", secret_id) secret_data = json.loads(secret_data_raw[1]) if label == secret_data[secret_id].get("label"): diff --git a/tests/v0/integration/test_charm.py b/tests/v0/integration/test_charm.py index bd276d67..b52192ae 100644 --- a/tests/v0/integration/test_charm.py +++ b/tests/v0/integration/test_charm.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 # Copyright 2022 Canonical Ltd. # See LICENSE file for licensing details. -import asyncio import json import logging from pathlib import Path @@ -10,7 +9,7 @@ import psycopg2 import pytest import yaml -from pytest_operator.plugin import OpsTest +from jubilant_adapters import JujuFixture, gather from .helpers import ( build_connection_string, @@ -56,8 +55,8 @@ @pytest.mark.abort_on_fail -async def test_deploy_charms( - ops_test: OpsTest, +def test_deploy_charms( + juju: JujuFixture, application_charm, database_charm, dummy_database_charm, @@ -66,14 +65,14 @@ async def test_deploy_charms( """Deploy both charms (application and database) to use in the tests.""" # Deploy both charms (2 units for each application to test that later they correctly # set data in the relation application databag using only the leader unit). - await asyncio.gather( - ops_test.model.deploy( + gather( + juju.ext.model.deploy( application_charm, application_name=APPLICATION_APP_NAME, num_units=NUM_APP, series=dp_libs_ubuntu_series, ), - ops_test.model.deploy( + juju.ext.model.deploy( database_charm, resources={ "database-image": DATABASE_APP_METADATA["resources"]["database-image"][ @@ -84,7 +83,7 @@ async def test_deploy_charms( num_units=NUM_DB, series=dp_libs_ubuntu_series, ), - ops_test.model.deploy( + juju.ext.model.deploy( dummy_database_charm, resources={ "database-image": DATABASE_DUMMY_APP_METADATA["resources"]["database-image"][ @@ -95,7 +94,7 @@ async def test_deploy_charms( num_units=NUM_DUMMY_DB, series=dp_libs_ubuntu_series, ), - ops_test.model.deploy( + juju.ext.model.deploy( database_charm, resources={ "database-image": DATABASE_APP_METADATA["resources"]["database-image"][ @@ -107,17 +106,17 @@ async def test_deploy_charms( ), ) - await asyncio.gather( - ops_test.model.wait_for_idle( + gather( + juju.ext.model.wait_for_idle( apps=[APPLICATION_APP_NAME], status="active", wait_for_exact_units=NUM_APP ), - ops_test.model.wait_for_idle( + juju.ext.model.wait_for_idle( apps=[DATABASE_APP_NAME], status="active", wait_for_exact_units=NUM_DB ), - ops_test.model.wait_for_idle( + juju.ext.model.wait_for_idle( apps=[DATABASE_DUMMY_APP_NAME], status="active", wait_for_exact_units=NUM_DUMMY_DB ), - ops_test.model.wait_for_idle( + juju.ext.model.wait_for_idle( apps=[ANOTHER_DATABASE_APP_NAME], status="active", wait_for_exact_units=NUM_OTHER_DB ), ) @@ -126,221 +125,219 @@ async def test_deploy_charms( @pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_without_juju_secrets") @pytest.mark.parametrize("component", ["app", "unit"]) -async def test_peer_relation(component, ops_test: OpsTest): +def test_peer_relation(component, juju: JujuFixture): """Testing peer relation using the DataPeer class.""" # Setting and verifying two secret fields - leader_id = await get_leader_id(ops_test, DATABASE_APP_NAME) + leader_id = get_leader_id(juju, DATABASE_APP_NAME) unit_name = f"{DATABASE_APP_NAME}/{leader_id}" - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "set-peer-relation-field", **{"component": component, "field": "monitor-password", "value": "blablabla"}, ) - await action.wait() + action.wait() - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "set-peer-relation-field", **{"component": component, "field": "secret-field", "value": "blablabla2"}, ) - await action.wait() + action.wait() - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "get-peer-relation-field", **{"component": component, "field": "monitor-password"} ) - await action.wait() + action.wait() assert action.results.get("value") == "blablabla" - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "get-peer-relation-field", **{"component": component, "field": "secret-field"} ) - await action.wait() + action.wait() assert action.results.get("value") == "blablabla2" # Setting and verifying a non-secret field - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "set-peer-relation-field", **{"component": component, "field": "not-a-secret", "value": "plain text"}, ) - await action.wait() + action.wait() - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "get-peer-relation-field", **{"component": component, "field": "not-a-secret"} ) - await action.wait() + action.wait() assert action.results.get("value") == "plain text" # Deleting all fields - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "delete-peer-relation-field", **{"component": component, "field": "monitor-password"} ) - await action.wait() + action.wait() - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "get-peer-relation-field", **{"component": component, "field": "monitor-password"} ) - await action.wait() + action.wait() assert not action.results.get("value") - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "delete-peer-relation-field", **{"component": component, "field": "not-a-secret"} ) - await action.wait() + action.wait() - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "get-peer-relation-field", **{"component": component, "field": "not-a-secret"} ) - await action.wait() + action.wait() assert not action.results.get("value") assert not ( - await get_application_relation_data( - ops_test, DATABASE_APP_NAME, "database-peers", "not-a-secret" - ) + get_application_relation_data(juju, DATABASE_APP_NAME, "database-peers", "not-a-secret") ) @pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_with_juju_secrets") @pytest.mark.parametrize("component", ["app", "unit"]) -async def test_peer_relation_secrets(component, ops_test: OpsTest): +def test_peer_relation_secrets(component, juju: JujuFixture): """Testing peer relation using the DataPeer class.""" # Setting and verifying two secret fields - leader_id = await get_leader_id(ops_test, DATABASE_APP_NAME) + leader_id = get_leader_id(juju, DATABASE_APP_NAME) unit_name = f"{DATABASE_APP_NAME}/{leader_id}" # Generally we shouldn't have test decision based on pytest.mark.parametrize # but I think this is a valid exception owner = "database" if component == "app" else unit_name - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "set-peer-relation-field", **{"component": component, "field": "monitor-password", "value": "blablabla"}, ) - await action.wait() + action.wait() - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "set-peer-relation-field", **{"component": component, "field": "secret-field", "value": "blablabla2"}, ) - await action.wait() + action.wait() - secret = await get_secret_by_label(ops_test, f"database-peers.database.{component}", owner) + secret = get_secret_by_label(juju, f"database-peers.database.{component}", owner) assert secret.get("monitor-password") == "blablabla" assert secret.get("secret-field") == "blablabla2" - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "get-peer-relation-field", **{"component": component, "field": "monitor-password"} ) - await action.wait() + action.wait() assert action.results.get("value") == "blablabla" - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "get-peer-relation-field", **{"component": component, "field": "secret-field"} ) - await action.wait() + action.wait() assert action.results.get("value") == "blablabla2" # Setting and verifying a non-secret field - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "set-peer-relation-field", **{"component": component, "field": "not-a-secret", "value": "plain text"}, ) - await action.wait() + action.wait() - secret = await get_secret_by_label(ops_test, f"database-peers.database.{component}", owner) + secret = get_secret_by_label(juju, f"database-peers.database.{component}", owner) assert not secret.get("not-a-secret") - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "get-peer-relation-field", **{"component": component, "field": "not-a-secret"} ) - await action.wait() + action.wait() assert action.results.get("value") == "plain text" # Deleting all fields - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "delete-peer-relation-field", **{"component": component, "field": "monitor-password"} ) - await action.wait() + action.wait() - secret = await get_secret_by_label(ops_test, f"database-peers.database.{component}", owner) + secret = get_secret_by_label(juju, f"database-peers.database.{component}", owner) assert secret.get("secret-field") == "blablabla2" assert secret.get("monitor-password") == "#DELETED#" - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "get-peer-relation-field", **{"component": component, "field": "monitor-password"} ) - await action.wait() + action.wait() assert not action.results.get("value") - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "delete-peer-relation-field", **{"component": component, "field": "not-a-secret"} ) - await action.wait() + action.wait() - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "get-peer-relation-field", **{"component": component, "field": "not-a-secret"} ) - await action.wait() + action.wait() assert not action.results.get("value") assert not ( - await get_application_relation_data( - ops_test, DATABASE_APP_NAME, "database-peers", "not-a-secret", app_or_unit=component + get_application_relation_data( + juju, DATABASE_APP_NAME, "database-peers", "not-a-secret", app_or_unit=component ) ) # Internal secret URI is not saved on the databag assert not ( - await get_application_relation_data( - ops_test, DATABASE_APP_NAME, "database-peers", "internal-secret", app_or_unit=component + get_application_relation_data( + juju, DATABASE_APP_NAME, "database-peers", "internal-secret", app_or_unit=component ) ) # Cleanup - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "delete-peer-secret", **{"component": component} ) - await action.wait() + action.wait() @pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_with_juju_secrets") @pytest.mark.parametrize("component", ["app", "unit"]) -async def test_peer_relation_secret_revisions(component, ops_test: OpsTest): +def test_peer_relation_secret_revisions(component, juju: JujuFixture): """Check that only a content change triggers the emission of a new revision.""" # Given - leader_id = await get_leader_id(ops_test, DATABASE_APP_NAME) + leader_id = get_leader_id(juju, DATABASE_APP_NAME) unit_name = f"{DATABASE_APP_NAME}/{leader_id}" owner = "database" if component == "app" else unit_name # When - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "set-peer-relation-field", **{"component": component, "field": "secret-field", "value": "blablabla"}, ) - await action.wait() + action.wait() - original_secret_revision = await get_secret_revision_by_label( - ops_test, f"database-peers.database.{component}", owner + original_secret_revision = get_secret_revision_by_label( + juju, f"database-peers.database.{component}", owner ) - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "set-peer-relation-field", **{"component": component, "field": "secret-field", "value": "blablabla2"}, ) - await action.wait() + action.wait() - changed_secret_revision = await get_secret_revision_by_label( - ops_test, f"database-peers.database.{component}", owner + changed_secret_revision = get_secret_revision_by_label( + juju, f"database-peers.database.{component}", owner ) - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "set-peer-relation-field", **{"component": component, "field": "secret-field", "value": "blablabla2"}, ) - await action.wait() + action.wait() - unchanged_secret_revision = await get_secret_revision_by_label( - ops_test, f"database-peers.database.{component}", owner + unchanged_secret_revision = get_secret_revision_by_label( + juju, f"database-peers.database.{component}", owner ) # Then @@ -351,10 +348,10 @@ async def test_peer_relation_secret_revisions(component, ops_test: OpsTest): @pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_with_juju_secrets") @pytest.mark.parametrize("component", ["app", "unit"]) -async def test_peer_relation_set_secret(component, ops_test: OpsTest): +def test_peer_relation_set_secret(component, juju: JujuFixture): """Testing peer relation using the DataPeer class.""" # Setting and verifying two secret fields - leader_id = await get_leader_id(ops_test, DATABASE_DUMMY_APP_NAME) + leader_id = get_leader_id(juju, DATABASE_DUMMY_APP_NAME) unit_name = f"{DATABASE_DUMMY_APP_NAME}/{leader_id}" # Generally we shouldn't have test decision based on pytest.mark.parametrize @@ -362,19 +359,17 @@ async def test_peer_relation_set_secret(component, ops_test: OpsTest): owner = "dummy-database" if component == "app" else unit_name # Setting a new secret field dynamically - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "set-peer-secret", **{"component": component, "field": "new-field", "value": "blablabla"}, ) - await action.wait() + action.wait() - secret = await get_secret_by_label( - ops_test, f"database-peers.dummy-database.{component}", owner - ) + secret = get_secret_by_label(juju, f"database-peers.dummy-database.{component}", owner) assert secret.get("new-field") == "blablabla" # Setting a new secret field dynamically in a new, dedicated secret - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "set-peer-secret", **{ "component": component, @@ -383,8 +378,8 @@ async def test_peer_relation_set_secret(component, ops_test: OpsTest): "group": "mygroup", }, ) - await action.wait() - action = await ops_test.model.units.get(unit_name).run_action( + action.wait() + action = juju.ext.model.units.get(unit_name).run_action( "set-peer-secret", **{ "component": component, @@ -393,210 +388,208 @@ async def test_peer_relation_set_secret(component, ops_test: OpsTest): "group": "mygroup", }, ) - await action.wait() + action.wait() - secret = await get_secret_by_label( - ops_test, f"database-peers.dummy-database.{component}.mygroup", owner - ) + secret = get_secret_by_label(juju, f"database-peers.dummy-database.{component}.mygroup", owner) assert secret.get("mygroup-field1") == "blablabla3" assert secret.get("mygroup-field2") == "blablabla4" # Getting the secret - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "get-peer-relation-field", **{"component": component, "field": "new-field"} ) - await action.wait() + action.wait() assert action.results.get("value") == "blablabla" - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "get-peer-relation-field", **{"component": component, "field": "mygroup-field1@mygroup"} ) - await action.wait() + action.wait() assert action.results.get("value") == "blablabla3" - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "get-peer-relation-field", **{"component": component, "field": "mygroup-field2@mygroup"} ) - await action.wait() + action.wait() assert action.results.get("value") == "blablabla4" # Cleanup - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "delete-peer-secret", **{"component": component} ) - await action.wait() + action.wait() - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "delete-peer-secret", **{"component": component, "group": "mygroup"} ) - await action.wait() + action.wait() @pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_with_juju_secrets") -async def test_peer_relation_non_leader_unit_secrets(ops_test: OpsTest): +def test_peer_relation_non_leader_unit_secrets(juju: JujuFixture): """Testing peer relation using the DataPeer class.""" # Setting and verifying two secret fields - non_leader_unit_id = await get_non_leader_id(ops_test, DATABASE_APP_NAME) + non_leader_unit_id = get_non_leader_id(juju, DATABASE_APP_NAME) unit_name = f"{DATABASE_APP_NAME}/{non_leader_unit_id}" - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "set-peer-relation-field", **{"component": "unit", "field": "monitor-password", "value": "blablabla"}, ) - await action.wait() + action.wait() - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "set-peer-relation-field", **{"component": "unit", "field": "secret-field", "value": "blablabla2"}, ) - await action.wait() + action.wait() - secret = await get_secret_by_label(ops_test, "database-peers.database.unit", unit_name) + secret = get_secret_by_label(juju, "database-peers.database.unit", unit_name) assert secret.get("monitor-password") == "blablabla" assert secret.get("secret-field") == "blablabla2" - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "get-peer-relation-field", **{"component": "unit", "field": "monitor-password"} ) - await action.wait() + action.wait() assert action.results.get("value") == "blablabla" - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "get-peer-relation-field", **{"component": "unit", "field": "secret-field"} ) - await action.wait() + action.wait() assert action.results.get("value") == "blablabla2" # Setting and verifying a non-secret field - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "set-peer-relation-field", **{"component": "unit", "field": "not-a-secret", "value": "plain text"}, ) - await action.wait() + action.wait() - secret = await get_secret_by_label(ops_test, "database-peers.database.unit", unit_name) + secret = get_secret_by_label(juju, "database-peers.database.unit", unit_name) assert not secret.get("not-a-secret") - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "get-peer-relation-field", **{"component": "unit", "field": "not-a-secret"} ) - await action.wait() + action.wait() assert action.results.get("value") == "plain text" # Deleting all fields - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "delete-peer-relation-field", **{"component": "unit", "field": "monitor-password"} ) - await action.wait() + action.wait() - secret = await get_secret_by_label(ops_test, "database-peers.database.unit", unit_name) + secret = get_secret_by_label(juju, "database-peers.database.unit", unit_name) assert secret.get("secret-field") == "blablabla2" assert secret.get("monitor-password") == "#DELETED#" - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "get-peer-relation-field", **{"component": "unit", "field": "monitor-password"} ) - await action.wait() + action.wait() assert not action.results.get("value") - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "delete-peer-relation-field", **{"component": "unit", "field": "not-a-secret"} ) - await action.wait() + action.wait() - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "get-peer-relation-field", **{"component": "unit", "field": "not-a-secret"} ) - await action.wait() + action.wait() assert not action.results.get("value") assert not ( - await get_application_relation_data( - ops_test, DATABASE_APP_NAME, "database-peers", "not-a-secret", app_or_unit="unit" + get_application_relation_data( + juju, DATABASE_APP_NAME, "database-peers", "not-a-secret", app_or_unit="unit" ) ) # Internal secret URI is not saved on the databag assert not ( - await get_application_relation_data( - ops_test, DATABASE_APP_NAME, "database-peers", "internal-secret", app_or_unit="unit" + get_application_relation_data( + juju, DATABASE_APP_NAME, "database-peers", "internal-secret", app_or_unit="unit" ) ) # Cleanup - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "delete-peer-secret", **{"component": "unit"} ) - await action.wait() + action.wait() @pytest.mark.abort_on_fail -async def test_peer_relation_non_leader_can_read_app_data(ops_test: OpsTest): +def test_peer_relation_non_leader_can_read_app_data(juju: JujuFixture): """Testing peer relation using the DataPeer class.""" # Setting and verifying two secret fields - leader_id = await get_leader_id(ops_test, DATABASE_APP_NAME) + leader_id = get_leader_id(juju, DATABASE_APP_NAME) unit_name = f"{DATABASE_APP_NAME}/{leader_id}" - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "set-peer-relation-field", **{"component": "app", "field": "monitor-password", "value": "blablabla"}, ) - await action.wait() + action.wait() - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "set-peer-relation-field", **{"component": "app", "field": "not-a-secret", "value": "plain text"}, ) - await action.wait() + action.wait() # Checking that non-leader unit can fetch any (i.e. also app) secret - non_leader_unit_id = await get_non_leader_id(ops_test, DATABASE_APP_NAME) + non_leader_unit_id = get_non_leader_id(juju, DATABASE_APP_NAME) non_leader_unit_name = f"{DATABASE_APP_NAME}/{non_leader_unit_id}" - action = await ops_test.model.units.get(non_leader_unit_name).run_action( + action = juju.ext.model.units.get(non_leader_unit_name).run_action( "get-peer-relation-field", **{"component": "app", "field": "monitor-password"} ) - await action.wait() + action.wait() assert action.results.get("value") == "blablabla" - action = await ops_test.model.units.get(non_leader_unit_name).run_action( + action = juju.ext.model.units.get(non_leader_unit_name).run_action( "get-peer-relation-field", **{"component": "app", "field": "not-a-secret"} ) - await action.wait() + action.wait() assert action.results.get("value") == "plain text" @pytest.mark.abort_on_fail -async def test_other_peer_relation(ops_test: OpsTest): +def test_other_peer_relation(juju: JujuFixture): """Testing peer relation using the DataPeer class.""" # Setting and verifying two secret fields component = "unit" - units = ops_test.model.applications[DATABASE_APP_NAME].units + units = juju.ext.model.applications[DATABASE_APP_NAME].units for unit in units: - action = await unit.run_action( + action = unit.run_action( "set-peer-relation-field", **{"component": component, "field": "monitor-password", "value": "blablabla"}, ) - await action.wait() + action.wait() - action = await unit.run_action( + action = unit.run_action( "set-peer-relation-field", **{"component": component, "field": "non-secret-field", "value": "blablabla2"}, ) - await action.wait() + action.wait() for main_unit in units: - action = await main_unit.run_action( + action = main_unit.run_action( "get-other-peer-relation-field", **{"field": "monitor-password"} ) - await action.wait() + action.wait() for unit in units: if unit != main_unit: assert action.results.get(unit.name.replace("/", "-")) == "blablabla" - action = await main_unit.run_action( + action = main_unit.run_action( "get-other-peer-relation-field", **{"field": "non-secret-field"} ) - await action.wait() + action.wait() for unit in units: if unit != main_unit: @@ -604,72 +597,68 @@ async def test_other_peer_relation(ops_test: OpsTest): @pytest.mark.abort_on_fail -async def test_other_peer_relation_scale(ops_test: OpsTest): +def test_other_peer_relation_scale(juju: JujuFixture): """The scaling test is the 'continuation' of the previous (test_other_peer_relation()) test. We assume data set up there. """ - await ops_test.model.applications[DATABASE_APP_NAME].scale(scale_change=-1) - await ops_test.model.wait_for_idle( - apps=[DATABASE_APP_NAME], status="active", wait_for_exact_units=2 - ) - units = ops_test.model.applications[DATABASE_APP_NAME].units + juju.ext.model.applications[DATABASE_APP_NAME].scale(scale_change=-1) + juju.ext.model.wait_for_idle(apps=[DATABASE_APP_NAME], status="active", wait_for_exact_units=2) + units = juju.ext.model.applications[DATABASE_APP_NAME].units for main_unit in units: - action = await main_unit.run_action( + action = main_unit.run_action( "get-other-peer-relation-field", **{"field": "monitor-password"} ) - await action.wait() + action.wait() for unit in units: if unit != main_unit: assert action.results.get(unit.name.replace("/", "-")) == "blablabla" - action = await main_unit.run_action( + action = main_unit.run_action( "get-other-peer-relation-field", **{"field": "non-secret-field"} ) - await action.wait() + action.wait() for unit in units: if unit != main_unit: assert action.results.get(unit.name.replace("/", "-")) == "blablabla2" - await ops_test.model.applications[DATABASE_APP_NAME].add_units(count=1) - await ops_test.model.wait_for_idle( - apps=[DATABASE_APP_NAME], status="active", wait_for_exact_units=3 - ) - new_units = ops_test.model.applications[DATABASE_APP_NAME].units + juju.ext.model.applications[DATABASE_APP_NAME].add_units(count=1) + juju.ext.model.wait_for_idle(apps=[DATABASE_APP_NAME], status="active", wait_for_exact_units=3) + new_units = juju.ext.model.applications[DATABASE_APP_NAME].units unit = list(set(new_units) - set(units))[0] for main_unit in units: - action = await main_unit.run_action( + action = main_unit.run_action( "get-other-peer-relation-field", **{"field": "monitor-password"} ) - await action.wait() + action.wait() assert action.results.get(unit) is None - action = await main_unit.run_action( + action = main_unit.run_action( "get-other-peer-relation-field", **{"field": "non-secret-field"} ) - await action.wait() + action.wait() assert action.results.get(unit) is None @pytest.mark.abort_on_fail -async def test_database_relation_with_charm_libraries(ops_test: OpsTest): +def test_database_relation_with_charm_libraries(juju: JujuFixture): """Test basic functionality of database relation interface.""" # Relate the charms and wait for them exchanging some connection data. - pytest.first_database_relation = await ops_test.model.add_relation( + pytest.first_database_relation = juju.ext.model.add_relation( f"{APPLICATION_APP_NAME}:{DB_FIRST_DATABASE_RELATION_NAME}", DATABASE_APP_NAME ) - await ops_test.model.wait_for_idle(apps=APP_NAMES, status="active") + juju.ext.model.wait_for_idle(apps=APP_NAMES, status="active") # Get the connection string to connect to the database. - connection_string = await build_connection_string( - ops_test, APPLICATION_APP_NAME, DB_FIRST_DATABASE_RELATION_NAME + connection_string = build_connection_string( + juju, APPLICATION_APP_NAME, DB_FIRST_DATABASE_RELATION_NAME ) # Connect to the database. @@ -690,17 +679,17 @@ async def test_database_relation_with_charm_libraries(ops_test: OpsTest): # Get the version of the database and compare with the information that # was retrieved directly from the database. - version = await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, DB_FIRST_DATABASE_RELATION_NAME, "version" + version = get_application_relation_data( + juju, APPLICATION_APP_NAME, DB_FIRST_DATABASE_RELATION_NAME, "version" ) assert version == data[0] -async def test_user_with_extra_roles(ops_test: OpsTest): +def test_user_with_extra_roles(juju: JujuFixture): """Test superuser actions and the request for more permissions.""" # Get the connection string to connect to the database. - connection_string = await build_connection_string( - ops_test, APPLICATION_APP_NAME, DB_FIRST_DATABASE_RELATION_NAME + connection_string = build_connection_string( + juju, APPLICATION_APP_NAME, DB_FIRST_DATABASE_RELATION_NAME ) # Connect to the database. @@ -717,19 +706,19 @@ async def test_user_with_extra_roles(ops_test: OpsTest): @pytest.mark.abort_on_fail -async def test_postgresql_plugin(ops_test: OpsTest): +def test_postgresql_plugin(juju: JujuFixture): """Test that the application charm can check whether a plugin is enabled.""" # Check that the plugin is disabled. unit_name = f"{APPLICATION_APP_NAME}/0" - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "get-plugin-status", **{"plugin": "citext"} ) - await action.wait() + action.wait() assert action.results.get("plugin-status") == "disabled" # Connect to the database and enable the plugin (PostgreSQL extension). - connection_string = await build_connection_string( - ops_test, APPLICATION_APP_NAME, DB_FIRST_DATABASE_RELATION_NAME + connection_string = build_connection_string( + juju, APPLICATION_APP_NAME, DB_FIRST_DATABASE_RELATION_NAME ) with psycopg2.connect(connection_string) as connection, connection.cursor() as cursor: connection.autocommit = True @@ -737,16 +726,14 @@ async def test_postgresql_plugin(ops_test: OpsTest): connection.close() # Check that the plugin is enabled. - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "get-plugin-status", **{"plugin": "citext"} ) - await action.wait() + action.wait() assert action.results.get("plugin-status") == "enabled" -async def test_two_applications_dont_share_the_same_relation_data( - ops_test: OpsTest, application_charm -): +def test_two_applications_dont_share_the_same_relation_data(juju: JujuFixture, application_charm): """Test that two different application connect to the database with different credentials.""" # Set some variables to use in this test. another_application_app_name = "another-application" @@ -754,66 +741,66 @@ async def test_two_applications_dont_share_the_same_relation_data( all_app_names.extend(APP_NAMES) # Deploy another application. - await ops_test.model.deploy( + juju.ext.model.deploy( application_charm, application_name=another_application_app_name, series="jammy" ) - await ops_test.model.wait_for_idle(apps=all_app_names, status="active") + juju.ext.model.wait_for_idle(apps=all_app_names, status="active") # Relate the new application with the database # and wait for them exchanging some connection data. - await ops_test.model.add_relation( + juju.ext.model.add_relation( f"{another_application_app_name}:{DB_FIRST_DATABASE_RELATION_NAME}", DATABASE_APP_NAME ) - await ops_test.model.wait_for_idle(apps=all_app_names, status="active") + juju.ext.model.wait_for_idle(apps=all_app_names, status="active") # Assert the two application have different relation (connection) data. - application_connection_string = await build_connection_string( - ops_test, APPLICATION_APP_NAME, DB_FIRST_DATABASE_RELATION_NAME + application_connection_string = build_connection_string( + juju, APPLICATION_APP_NAME, DB_FIRST_DATABASE_RELATION_NAME ) - another_application_connection_string = await build_connection_string( - ops_test, another_application_app_name, DB_FIRST_DATABASE_RELATION_NAME + another_application_connection_string = build_connection_string( + juju, another_application_app_name, DB_FIRST_DATABASE_RELATION_NAME ) assert application_connection_string != another_application_connection_string @pytest.mark.usefixtures("only_without_juju_secrets") -async def test_databag_usage_correct(ops_test: OpsTest, application_charm): +def test_databag_usage_correct(juju: JujuFixture, application_charm): for field in ["username", "password"]: - assert await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, DB_FIRST_DATABASE_RELATION_NAME, field + assert get_application_relation_data( + juju, APPLICATION_APP_NAME, DB_FIRST_DATABASE_RELATION_NAME, field ) @pytest.mark.usefixtures("only_with_juju_secrets") -async def test_secrets_usage_correct_secrets(ops_test: OpsTest, application_charm): +def test_secrets_usage_correct_secrets(juju: JujuFixture, application_charm): for field in ["username", "password", "uris"]: assert ( - await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, DB_FIRST_DATABASE_RELATION_NAME, field + get_application_relation_data( + juju, APPLICATION_APP_NAME, DB_FIRST_DATABASE_RELATION_NAME, field ) is None ) - assert await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, DB_FIRST_DATABASE_RELATION_NAME, "secret-user" + assert get_application_relation_data( + juju, APPLICATION_APP_NAME, DB_FIRST_DATABASE_RELATION_NAME, "secret-user" ) @pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_without_juju_secrets") -async def test_database_roles_relation_with_charm_libraries(ops_test: OpsTest): +def test_database_roles_relation_with_charm_libraries(juju: JujuFixture): """Test basic functionality of database-roles relation interface.""" # Relate the charms and wait for them exchanging some connection data. - pytest.first_database_relation = await ops_test.model.add_relation( + pytest.first_database_relation = juju.ext.model.add_relation( f"{APPLICATION_APP_NAME}:{ROLES_FIRST_DATABASE_RELATION_NAME}", DATABASE_APP_NAME ) - await ops_test.model.wait_for_idle(apps=APP_NAMES, status="active") + juju.ext.model.wait_for_idle(apps=APP_NAMES, status="active") - entity_name = await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, ROLES_FIRST_DATABASE_RELATION_NAME, "entity-name" + entity_name = get_application_relation_data( + juju, APPLICATION_APP_NAME, ROLES_FIRST_DATABASE_RELATION_NAME, "entity-name" ) - entity_pass = await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, ROLES_FIRST_DATABASE_RELATION_NAME, "entity-password" + entity_pass = get_application_relation_data( + juju, APPLICATION_APP_NAME, ROLES_FIRST_DATABASE_RELATION_NAME, "entity-password" ) assert entity_name is not None @@ -822,23 +809,23 @@ async def test_database_roles_relation_with_charm_libraries(ops_test: OpsTest): @pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_with_juju_secrets") -async def test_database_roles_relation_with_charm_libraries_secrets(ops_test: OpsTest): +def test_database_roles_relation_with_charm_libraries_secrets(juju: JujuFixture): """Test basic functionality of database-roles relation interface.""" # Relate the charms and wait for them exchanging some connection data. - pytest.first_database_relation = await ops_test.model.add_relation( + pytest.first_database_relation = juju.ext.model.add_relation( f"{APPLICATION_APP_NAME}:{ROLES_FIRST_DATABASE_RELATION_NAME}", DATABASE_APP_NAME ) - await ops_test.model.wait_for_idle(apps=APP_NAMES, status="active") + juju.ext.model.wait_for_idle(apps=APP_NAMES, status="active") - secret_uri = await get_application_relation_data( - ops_test, + secret_uri = get_application_relation_data( + juju, APPLICATION_APP_NAME, ROLES_FIRST_DATABASE_RELATION_NAME, f"{SECRET_REF_PREFIX}entity", ) - secret_content = await get_juju_secret(ops_test, secret_uri) + secret_content = get_juju_secret(juju, secret_uri) entity_name = secret_content["entity-name"] entity_pass = secret_content["entity-password"] @@ -848,23 +835,23 @@ async def test_database_roles_relation_with_charm_libraries_secrets(ops_test: Op @pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_with_juju_secrets") -async def test_database_username(ops_test: OpsTest): +def test_database_username(juju: JujuFixture): """Test basic functionality of database-roles relation interface.""" # Relate the charms and wait for them exchanging some connection data. - pytest.first_database_relation = await ops_test.model.add_relation( + pytest.first_database_relation = juju.ext.model.add_relation( f"{APPLICATION_APP_NAME}:{USERNAME_FIRST_DATABASE_RELATION_NAME}", DATABASE_APP_NAME ) - await ops_test.model.wait_for_idle(apps=APP_NAMES, status="active") + juju.ext.model.wait_for_idle(apps=APP_NAMES, status="active") - secret_uri = await get_application_relation_data( - ops_test, + secret_uri = get_application_relation_data( + juju, APPLICATION_APP_NAME, USERNAME_FIRST_DATABASE_RELATION_NAME, "secret-user", ) - secret_content = await get_juju_secret(ops_test, secret_uri) + secret_content = get_juju_secret(juju, secret_uri) name = secret_content["username"] password = secret_content["password"] @@ -873,18 +860,18 @@ async def test_database_username(ops_test: OpsTest): @pytest.mark.abort_on_fail -async def test_database_prefix(ops_test: OpsTest): +def test_database_prefix(juju: JujuFixture): """Test basic functionality of database-roles relation interface.""" # Relate the charms and wait for them exchanging some connection data. - pytest.first_database_relation = await ops_test.model.add_relation( + pytest.first_database_relation = juju.ext.model.add_relation( f"{APPLICATION_APP_NAME}:{PREFIX_DATABASE_RELATION_NAME}", DATABASE_APP_NAME ) - await ops_test.model.wait_for_idle(apps=APP_NAMES, status="active") + juju.ext.model.wait_for_idle(apps=APP_NAMES, status="active") assert ( - await get_application_relation_data( - ops_test, + get_application_relation_data( + juju, APPLICATION_APP_NAME, PREFIX_DATABASE_RELATION_NAME, "prefix-databases", @@ -893,8 +880,8 @@ async def test_database_prefix(ops_test: OpsTest): ) data = json.loads( - await get_application_relation_data( - ops_test, + get_application_relation_data( + juju, APPLICATION_APP_NAME, PREFIX_DATABASE_RELATION_NAME, "data", @@ -903,34 +890,34 @@ async def test_database_prefix(ops_test: OpsTest): assert data["prefix-matching"] == "all" -async def test_an_application_can_connect_to_multiple_database_clusters( - ops_test: OpsTest, database_charm +def test_an_application_can_connect_to_multiple_database_clusters( + juju: JujuFixture, database_charm ): """Test that an application can connect to different clusters of the same database.""" # Relate the application with both database clusters # and wait for them exchanging some connection data. - first_cluster_relation = await ops_test.model.add_relation( + first_cluster_relation = juju.ext.model.add_relation( f"{APPLICATION_APP_NAME}:{MULTIPLE_DATABASE_CLUSTERS_RELATION_NAME}", DATABASE_APP_NAME ) # This call enables the unit to be available in the relation changed event. - await ops_test.model.wait_for_idle(apps=APP_NAMES, status="active") + juju.ext.model.wait_for_idle(apps=APP_NAMES, status="active") - second_cluster_relation = await ops_test.model.add_relation( + second_cluster_relation = juju.ext.model.add_relation( f"{APPLICATION_APP_NAME}:{MULTIPLE_DATABASE_CLUSTERS_RELATION_NAME}", ANOTHER_DATABASE_APP_NAME, ) - await ops_test.model.wait_for_idle(apps=APP_NAMES, status="active") + juju.ext.model.wait_for_idle(apps=APP_NAMES, status="active") # Retrieve the connection string to both database clusters using the relation aliases # and assert they are different. - application_connection_string = await build_connection_string( - ops_test, + application_connection_string = build_connection_string( + juju, APPLICATION_APP_NAME, MULTIPLE_DATABASE_CLUSTERS_RELATION_NAME, relation_id=first_cluster_relation.id, ) - another_application_connection_string = await build_connection_string( - ops_test, + another_application_connection_string = build_connection_string( + juju, APPLICATION_APP_NAME, MULTIPLE_DATABASE_CLUSTERS_RELATION_NAME, relation_id=second_cluster_relation.id, @@ -938,35 +925,35 @@ async def test_an_application_can_connect_to_multiple_database_clusters( assert application_connection_string != another_application_connection_string -async def test_an_application_can_connect_to_multiple_aliased_database_clusters( - ops_test: OpsTest, database_charm +def test_an_application_can_connect_to_multiple_aliased_database_clusters( + juju: JujuFixture, database_charm ): """Test that an application can connect to different clusters of the same database.""" # Relate the application with both database clusters # and wait for them exchanging some connection data. - await ops_test.model.add_relation( + juju.ext.model.add_relation( f"{APPLICATION_APP_NAME}:{ALIASED_MULTIPLE_DATABASE_CLUSTERS_RELATION_NAME}", DATABASE_APP_NAME, ) # This call enables the unit to be available in the relation changed event. - await ops_test.model.wait_for_idle(apps=APP_NAMES, status="active") + juju.ext.model.wait_for_idle(apps=APP_NAMES, status="active") - await ops_test.model.add_relation( + juju.ext.model.add_relation( f"{APPLICATION_APP_NAME}:{ALIASED_MULTIPLE_DATABASE_CLUSTERS_RELATION_NAME}", ANOTHER_DATABASE_APP_NAME, ) - await ops_test.model.wait_for_idle(apps=APP_NAMES, status="active") + juju.ext.model.wait_for_idle(apps=APP_NAMES, status="active") # Retrieve the connection string to both database clusters using the relation aliases # and assert they are different. - application_connection_string = await build_connection_string( - ops_test, + application_connection_string = build_connection_string( + juju, APPLICATION_APP_NAME, ALIASED_MULTIPLE_DATABASE_CLUSTERS_RELATION_NAME, relation_alias="cluster1", ) - another_application_connection_string = await build_connection_string( - ops_test, + another_application_connection_string = build_connection_string( + juju, APPLICATION_APP_NAME, ALIASED_MULTIPLE_DATABASE_CLUSTERS_RELATION_NAME, relation_alias="cluster2", @@ -974,32 +961,32 @@ async def test_an_application_can_connect_to_multiple_aliased_database_clusters( assert application_connection_string != another_application_connection_string -async def test_an_application_can_request_multiple_databases(ops_test: OpsTest, application_charm): +def test_an_application_can_request_multiple_databases(juju: JujuFixture, application_charm): """Test that an application can request additional databases using the same interface.""" # Relate the charms using another relation and wait for them exchanging some connection data. sleep(5) - pytest.second_database_relation = await ops_test.model.add_relation( + pytest.second_database_relation = juju.ext.model.add_relation( f"{APPLICATION_APP_NAME}:{DB_SECOND_DATABASE_RELATION_NAME}", DATABASE_APP_NAME ) - await ops_test.model.wait_for_idle(apps=APP_NAMES, status="active") + juju.ext.model.wait_for_idle(apps=APP_NAMES, status="active") # Get the connection strings to connect to both databases. - first_database_connection_string = await build_connection_string( - ops_test, APPLICATION_APP_NAME, DB_FIRST_DATABASE_RELATION_NAME + first_database_connection_string = build_connection_string( + juju, APPLICATION_APP_NAME, DB_FIRST_DATABASE_RELATION_NAME ) - second_database_connection_string = await build_connection_string( - ops_test, APPLICATION_APP_NAME, DB_SECOND_DATABASE_RELATION_NAME + second_database_connection_string = build_connection_string( + juju, APPLICATION_APP_NAME, DB_SECOND_DATABASE_RELATION_NAME ) # Assert the two application have different relation (connection) data. assert first_database_connection_string != second_database_connection_string -async def test_external_node_connectivity_field(ops_test: OpsTest, application_charm): +def test_external_node_connectivity_field(juju: JujuFixture, application_charm): # Check that the flag is missing if not requested assert ( - await get_application_relation_data( - ops_test, + get_application_relation_data( + juju, DATABASE_APP_NAME, "database", "external-node-connectivity", @@ -1009,8 +996,8 @@ async def test_external_node_connectivity_field(ops_test: OpsTest, application_c # Check that the second relation raises the flag assert ( - await get_application_relation_data( - ops_test, + get_application_relation_data( + juju, DATABASE_APP_NAME, "database", "external-node-connectivity", @@ -1020,10 +1007,10 @@ async def test_external_node_connectivity_field(ops_test: OpsTest, application_c @pytest.mark.usefixtures("only_with_juju_secrets") -async def test_provider_with_additional_secrets(ops_test: OpsTest, database_charm): +def test_provider_with_additional_secrets(juju: JujuFixture, database_charm): # Let's make sure that there was enough time for the relation initialization to communicate secrets - secret_fields = await get_application_relation_data( - ops_test, + secret_fields = get_application_relation_data( + juju, DATABASE_APP_NAME, DATABASE_APP_NAME, "requested-secrets", @@ -1032,39 +1019,39 @@ async def test_provider_with_additional_secrets(ops_test: OpsTest, database_char assert {"topsecret", "donttellanyone"} <= set(json.loads(secret_fields)) # Set secret - leader_id = await get_leader_id(ops_test, DATABASE_APP_NAME) + leader_id = get_leader_id(juju, DATABASE_APP_NAME) leader_name = f"{DATABASE_APP_NAME}/{leader_id}" - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "set-secret", **{"relation_id": pytest.second_database_relation.id, "field": "topsecret"} ) - await action.wait() + action.wait() # Get secret original value - secret_uri = await get_application_relation_data( - ops_test, + secret_uri = get_application_relation_data( + juju, APPLICATION_APP_NAME, DB_SECOND_DATABASE_RELATION_NAME, f"{SECRET_REF_PREFIX}extra", ) - secret_content = await get_juju_secret(ops_test, secret_uri) + secret_content = get_juju_secret(juju, secret_uri) topsecret1 = secret_content["topsecret"] # Re-set secret - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "set-secret", **{"relation_id": pytest.second_database_relation.id, "field": "topsecret"} ) - await action.wait() + action.wait() # Get secret after change - secret_uri = await get_application_relation_data( - ops_test, + secret_uri = get_application_relation_data( + juju, APPLICATION_APP_NAME, DB_SECOND_DATABASE_RELATION_NAME, f"{SECRET_REF_PREFIX}extra", ) - secret_content = await get_juju_secret(ops_test, secret_uri) + secret_content = get_juju_secret(juju, secret_uri) topsecret2 = secret_content["topsecret"] assert topsecret1 != topsecret2 @@ -1072,26 +1059,26 @@ async def test_provider_with_additional_secrets(ops_test: OpsTest, database_char @pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_with_juju_secrets") -async def test_relation_secret_revisions(ops_test: OpsTest): +def test_relation_secret_revisions(juju: JujuFixture): """Check that only a content change triggers the emission of a new revision.""" # Given - leader_id = await get_leader_id(ops_test, DATABASE_APP_NAME) + leader_id = get_leader_id(juju, DATABASE_APP_NAME) leader_name = f"{DATABASE_APP_NAME}/{leader_id}" owner = "database" rel_id = pytest.second_database_relation.id group_mapping = "extra" # When - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "set-secret", **{"relation_id": rel_id, "field": "topsecret", "value": "initialvalue"} ) - await action.wait() + action.wait() - original_secret_revision = await get_secret_revision_by_label( - ops_test, f"{DATABASE_APP_NAME}.{rel_id}.{group_mapping}.secret", owner + original_secret_revision = get_secret_revision_by_label( + juju, f"{DATABASE_APP_NAME}.{rel_id}.{group_mapping}.secret", owner ) - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "set-relation-field", **{ "relation_id": pytest.second_database_relation.id, @@ -1099,13 +1086,13 @@ async def test_relation_secret_revisions(ops_test: OpsTest): "value": "changedvalue", }, ) - await action.wait() + action.wait() - changed_secret_revision = await get_secret_revision_by_label( - ops_test, f"{DATABASE_APP_NAME}.{rel_id}.{group_mapping}.secret", owner + changed_secret_revision = get_secret_revision_by_label( + juju, f"{DATABASE_APP_NAME}.{rel_id}.{group_mapping}.secret", owner ) - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "set-relation-field", **{ "relation_id": pytest.second_database_relation.id, @@ -1113,10 +1100,10 @@ async def test_relation_secret_revisions(ops_test: OpsTest): "value": "changedvalue", }, ) - await action.wait() + action.wait() - unchanged_secret_revision = await get_secret_revision_by_label( - ops_test, f"{DATABASE_APP_NAME}.{rel_id}.{group_mapping}.secret", owner + unchanged_secret_revision = get_secret_revision_by_label( + juju, f"{DATABASE_APP_NAME}.{rel_id}.{group_mapping}.secret", owner ) # Then @@ -1126,12 +1113,12 @@ async def test_relation_secret_revisions(ops_test: OpsTest): @pytest.mark.parametrize("field,value", [("new_field", "blah"), ("tls", "True")]) @pytest.mark.usefixtures("only_without_juju_secrets") -async def test_provider_get_set_delete_fields(field, value, ops_test: OpsTest): +def test_provider_get_set_delete_fields(field, value, juju: JujuFixture): # Add normal field - leader_id = await get_leader_id(ops_test, DATABASE_APP_NAME) + leader_id = get_leader_id(juju, DATABASE_APP_NAME) leader_name = f"{DATABASE_APP_NAME}/{leader_id}" - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "set-relation-field", **{ "relation_id": pytest.second_database_relation.id, @@ -1139,29 +1126,29 @@ async def test_provider_get_set_delete_fields(field, value, ops_test: OpsTest): "value": value, }, ) - await action.wait() + action.wait() assert ( - await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, DB_SECOND_DATABASE_RELATION_NAME, field + get_application_relation_data( + juju, APPLICATION_APP_NAME, DB_SECOND_DATABASE_RELATION_NAME, field ) == value ) # Check all application units can read remote relation data - for unit in ops_test.model.applications[APPLICATION_APP_NAME].units: - action = await unit.run_action( + for unit in juju.ext.model.applications[APPLICATION_APP_NAME].units: + action = unit.run_action( "get-relation-field", **{ "relation_id": pytest.second_database_relation.id, "field": field, }, ) - await action.wait() + action.wait() assert action.results.get("value") == value # Check if database can retrieve self-side relation data - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "get-relation-self-side-field", **{ "relation_id": pytest.second_database_relation.id, @@ -1169,33 +1156,33 @@ async def test_provider_get_set_delete_fields(field, value, ops_test: OpsTest): "value": value, }, ) - await action.wait() + action.wait() assert action.results.get("value") == value # Delete normal field - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "delete-relation-field", **{"relation_id": pytest.second_database_relation.id, "field": field}, ) - await action.wait() + action.wait() assert ( - await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, DB_SECOND_DATABASE_RELATION_NAME, field + get_application_relation_data( + juju, APPLICATION_APP_NAME, DB_SECOND_DATABASE_RELATION_NAME, field ) is None ) # Delete non-existent field - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "delete-relation-field", **{"relation_id": pytest.second_database_relation.id, "field": "doesnt_exist"}, ) - await action.wait() + action.wait() # Juju2 syntax assert int(action.results["Code"]) == 0 - assert await check_logs( - ops_test, + assert check_logs( + juju, strings=["Non-existing field 'doesnt_exist' was attempted to be removed from the databag"], ) @@ -1211,13 +1198,11 @@ async def test_provider_get_set_delete_fields(field, value, ops_test: OpsTest): ], ) @pytest.mark.usefixtures("only_with_juju_secrets") -async def test_provider_get_set_delete_fields_secrets( - field, value, relation_field, ops_test: OpsTest -): +def test_provider_get_set_delete_fields_secrets(field, value, relation_field, juju: JujuFixture): # Add field - leader_id = await get_leader_id(ops_test, DATABASE_APP_NAME) + leader_id = get_leader_id(juju, DATABASE_APP_NAME) leader_name = f"{DATABASE_APP_NAME}/{leader_id}" - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "set-relation-field", **{ "relation_id": pytest.second_database_relation.id, @@ -1225,26 +1210,26 @@ async def test_provider_get_set_delete_fields_secrets( "value": value, }, ) - await action.wait() + action.wait() - assert await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, DB_SECOND_DATABASE_RELATION_NAME, relation_field + assert get_application_relation_data( + juju, APPLICATION_APP_NAME, DB_SECOND_DATABASE_RELATION_NAME, relation_field ) # Check all application units can read remote relation data - for unit in ops_test.model.applications[APPLICATION_APP_NAME].units: - action = await unit.run_action( + for unit in juju.ext.model.applications[APPLICATION_APP_NAME].units: + action = unit.run_action( "get-relation-field", **{ "relation_id": pytest.second_database_relation.id, "field": field, }, ) - await action.wait() + action.wait() assert action.results.get("value") == value # Check if database can retrieve self-side relation data - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "get-relation-self-side-field", **{ "relation_id": pytest.second_database_relation.id, @@ -1252,54 +1237,54 @@ async def test_provider_get_set_delete_fields_secrets( "value": value, }, ) - await action.wait() + action.wait() assert action.results.get("value") == value # Delete field - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "delete-relation-field", **{"relation_id": pytest.second_database_relation.id, "field": field}, ) - await action.wait() + action.wait() assert ( - await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, DB_SECOND_DATABASE_RELATION_NAME, relation_field + get_application_relation_data( + juju, APPLICATION_APP_NAME, DB_SECOND_DATABASE_RELATION_NAME, relation_field ) is None ) # Check that the field is deleted - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "get-relation-self-side-field", **{ "relation_id": pytest.second_database_relation.id, "field": field, }, ) - await action.wait() + action.wait() assert not action.results.get("value") # Delete non-existent notmal and secret field - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "delete-relation-field", **{"relation_id": pytest.second_database_relation.id, "field": "doesnt_exist"}, ) - await action.wait() + action.wait() assert action.results["return-code"] == 0 @pytest.mark.abort_on_fail @pytest.mark.log_errors_allowed("Can't delete secret for relation") @pytest.mark.usefixtures("only_with_juju_secrets") -async def test_provider_deleted_secret_is_removed(ops_test: OpsTest): +def test_provider_deleted_secret_is_removed(juju: JujuFixture): """The 'tls' field, that was removed in the previous test has it's secret removed.""" # Add field field = "tls" value = "True" - leader_id = await get_leader_id(ops_test, DATABASE_APP_NAME) + leader_id = get_leader_id(juju, DATABASE_APP_NAME) leader_name = f"{DATABASE_APP_NAME}/{leader_id}" - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "set-relation-field", **{ "relation_id": pytest.second_database_relation.id, @@ -1307,43 +1292,43 @@ async def test_provider_deleted_secret_is_removed(ops_test: OpsTest): "value": value, }, ) - await action.wait() + action.wait() # Get TLS secret pointer - secret_uri = await get_application_relation_data( - ops_test, + secret_uri = get_application_relation_data( + juju, APPLICATION_APP_NAME, DB_SECOND_DATABASE_RELATION_NAME, f"{SECRET_REF_PREFIX}{field}", ) # Delete field - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "delete-relation-field", **{"relation_id": pytest.second_database_relation.id, "field": field}, ) - await action.wait() + action.wait() assert not ( - await check_logs( - ops_test, + check_logs( + juju, strings=["Non-existing field 'tls' was attempted to be removed from the databag"], ) ) - assert not (await check_logs(ops_test, strings=["Can't delete secret for relation"])) + assert not (check_logs(juju, strings=["Can't delete secret for relation"])) - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "delete-relation-field", **{"relation_id": pytest.second_database_relation.id, "field": field}, ) - await action.wait() - assert await check_logs( - ops_test, strings=["Non-existing field 'tls' was attempted to be removed from the databag"] + action.wait() + assert check_logs( + juju, strings=["Non-existing field 'tls' was attempted to be removed from the databag"] ) - assert await check_logs(ops_test, strings=["Can't delete secret for relation"]) + assert check_logs(juju, strings=["Can't delete secret for relation"]) assert ( - await get_application_relation_data( - ops_test, + get_application_relation_data( + juju, APPLICATION_APP_NAME, DB_SECOND_DATABASE_RELATION_NAME, f"{SECRET_REF_PREFIX}{field}", @@ -1351,17 +1336,17 @@ async def test_provider_deleted_secret_is_removed(ops_test: OpsTest): is None ) - secrets = await list_juju_secrets(ops_test) + secrets = list_juju_secrets(juju) secret_xid = secret_uri.split("/")[-1] assert secret_xid not in secrets -async def test_requires_get_set_delete_fields(ops_test: OpsTest): +def test_requires_get_set_delete_fields(juju: JujuFixture): # Add normal field - leader_id = await get_leader_id(ops_test, APPLICATION_APP_NAME) + leader_id = get_leader_id(juju, APPLICATION_APP_NAME) leader_name = f"{APPLICATION_APP_NAME}/{leader_id}" - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "set-relation-field", **{ "relation_id": pytest.second_database_relation.id, @@ -1369,11 +1354,11 @@ async def test_requires_get_set_delete_fields(ops_test: OpsTest): "value": "blah", }, ) - await action.wait() + action.wait() assert ( - await get_application_relation_data( - ops_test, + get_application_relation_data( + juju, DATABASE_APP_NAME, DB_SECOND_DATABASE_RELATION_NAME, "new_field", @@ -1383,19 +1368,19 @@ async def test_requires_get_set_delete_fields(ops_test: OpsTest): ) # Check all application units can read remote relation data - for unit in ops_test.model.applications[DATABASE_APP_NAME].units: - action = await unit.run_action( + for unit in juju.ext.model.applications[DATABASE_APP_NAME].units: + action = unit.run_action( "get-relation-field", **{ "relation_id": pytest.second_database_relation.id, "field": "new_field", }, ) - await action.wait() + action.wait() assert action.results.get("value") == "blah" # Check if database can retrieve self-side relation data - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "get-relation-self-side-field", **{ "relation_id": pytest.second_database_relation.id, @@ -1403,19 +1388,19 @@ async def test_requires_get_set_delete_fields(ops_test: OpsTest): "value": "blah", }, ) - await action.wait() + action.wait() assert action.results.get("value") == "blah" # Delete normal field - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "delete-relation-field", **{"relation_id": pytest.second_database_relation.id, "field": "new_field"}, ) - await action.wait() + action.wait() assert ( - await get_application_relation_data( - ops_test, + get_application_relation_data( + juju, DATABASE_APP_NAME, DB_SECOND_DATABASE_RELATION_NAME, "new_field", @@ -1431,10 +1416,10 @@ async def test_requires_get_set_delete_fields(ops_test: OpsTest): @pytest.mark.log_errors_allowed( "This operation (delete_relation_data()) can only be performed by the leader unit" ) -async def test_provider_set_delete_fields_leader_only(ops_test: OpsTest): - leader_id = await get_leader_id(ops_test, DATABASE_APP_NAME) +def test_provider_set_delete_fields_leader_only(juju: JujuFixture): + leader_id = get_leader_id(juju, DATABASE_APP_NAME) leader_name = f"{DATABASE_APP_NAME}/{leader_id}" - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "set-relation-field", **{ "relation_id": pytest.second_database_relation.id, @@ -1442,11 +1427,11 @@ async def test_provider_set_delete_fields_leader_only(ops_test: OpsTest): "value": "blah", }, ) - await action.wait() + action.wait() - unit_id = await get_non_leader_id(ops_test, DATABASE_APP_NAME) + unit_id = get_non_leader_id(juju, DATABASE_APP_NAME) unit_name = f"{DATABASE_APP_NAME}/{unit_id}" - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "set-relation-field", **{ "relation_id": pytest.second_database_relation.id, @@ -1454,46 +1439,46 @@ async def test_provider_set_delete_fields_leader_only(ops_test: OpsTest): "value": "blah2", }, ) - await action.wait() - assert await check_logs( - ops_test, + action.wait() + assert check_logs( + juju, strings=[ "This operation (update_relation_data()) can only be performed by the leader unit" ], ) assert ( - await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, DB_SECOND_DATABASE_RELATION_NAME, "new_field2" + get_application_relation_data( + juju, APPLICATION_APP_NAME, DB_SECOND_DATABASE_RELATION_NAME, "new_field2" ) is None ) - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "delete-relation-field", **{"relation_id": pytest.second_database_relation.id, "field": "new_field"}, ) - await action.wait() - assert await check_logs( - ops_test, + action.wait() + assert check_logs( + juju, strings=[ "This operation (delete_relation_data()) can only be performed by the leader unit" ], ) assert ( - await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, DB_SECOND_DATABASE_RELATION_NAME, "new_field" + get_application_relation_data( + juju, APPLICATION_APP_NAME, DB_SECOND_DATABASE_RELATION_NAME, "new_field" ) == "blah" ) -async def test_requires_set_delete_fields(ops_test: OpsTest): +def test_requires_set_delete_fields(juju: JujuFixture): # Add field - leader_id = await get_leader_id(ops_test, APPLICATION_APP_NAME) + leader_id = get_leader_id(juju, APPLICATION_APP_NAME) leader_name = f"{APPLICATION_APP_NAME}/{leader_id}" - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "set-relation-field", **{ "relation_id": pytest.second_database_relation.id, @@ -1501,11 +1486,11 @@ async def test_requires_set_delete_fields(ops_test: OpsTest): "value": "blah-req", }, ) - await action.wait() + action.wait() assert ( - await get_application_relation_data( - ops_test, + get_application_relation_data( + juju, DATABASE_APP_NAME, DATABASE_APP_NAME, "new_field_req", @@ -1515,15 +1500,15 @@ async def test_requires_set_delete_fields(ops_test: OpsTest): ) # Delete field - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "delete-relation-field", **{"relation_id": pytest.second_database_relation.id, "field": "new_field_req"}, ) - await action.wait() + action.wait() assert ( - await get_application_relation_data( - ops_test, + get_application_relation_data( + juju, DATABASE_APP_NAME, DATABASE_APP_NAME, "new_field_req", @@ -1539,10 +1524,10 @@ async def test_requires_set_delete_fields(ops_test: OpsTest): @pytest.mark.log_errors_allowed( "This operation (delete_relation_data()) can only be performed by the leader unit" ) -async def test_requires_set_delete_fields_leader_only(ops_test: OpsTest): - leader_id = await get_leader_id(ops_test, APPLICATION_APP_NAME) +def test_requires_set_delete_fields_leader_only(juju: JujuFixture): + leader_id = get_leader_id(juju, APPLICATION_APP_NAME) leader_name = f"{APPLICATION_APP_NAME}/{leader_id}" - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "set-relation-field", **{ "relation_id": pytest.second_database_relation.id, @@ -1550,11 +1535,11 @@ async def test_requires_set_delete_fields_leader_only(ops_test: OpsTest): "value": "blah-req", }, ) - await action.wait() + action.wait() - unit_id = await get_non_leader_id(ops_test, APPLICATION_APP_NAME) + unit_id = get_non_leader_id(juju, APPLICATION_APP_NAME) unit_name = f"{APPLICATION_APP_NAME}/{unit_id}" - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "set-relation-field", **{ "relation_id": pytest.second_database_relation.id, @@ -1562,17 +1547,17 @@ async def test_requires_set_delete_fields_leader_only(ops_test: OpsTest): "value": "blah2-req", }, ) - await action.wait() - assert await check_logs( - ops_test, + action.wait() + assert check_logs( + juju, strings=[ "This operation (update_relation_data()) can only be performed by the leader unit" ], ) assert ( - await get_application_relation_data( - ops_test, + get_application_relation_data( + juju, DATABASE_APP_NAME, DATABASE_APP_NAME, "new_field2-req", @@ -1581,21 +1566,21 @@ async def test_requires_set_delete_fields_leader_only(ops_test: OpsTest): is None ) - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "delete-relation-field", **{"relation_id": pytest.second_database_relation.id, "field": "new_field-req"}, ) - await action.wait() - assert await check_logs( - ops_test, + action.wait() + assert check_logs( + juju, strings=[ "This operation (delete_relation_data()) can only be performed by the leader unit" ], ) assert ( - await get_application_relation_data( - ops_test, + get_application_relation_data( + juju, DATABASE_APP_NAME, DATABASE_APP_NAME, "new_field-req", @@ -1605,34 +1590,34 @@ async def test_requires_set_delete_fields_leader_only(ops_test: OpsTest): ) -async def test_scaling_requires_can_access_shared_secret(ops_test): +def test_scaling_requires_can_access_shared_secret(juju): """When scaling up the application, new units should have access to relation secrets.""" - await ops_test.model.applications[APPLICATION_APP_NAME].scale(3) + juju.ext.model.applications[APPLICATION_APP_NAME].scale(3) - await ops_test.model.wait_for_idle( + juju.ext.model.wait_for_idle( apps=[APPLICATION_APP_NAME], status="active", timeout=(15 * 60), wait_for_exact_units=3 ) old_unit_name = f"{APPLICATION_APP_NAME}/1" new_unit_name = f"{APPLICATION_APP_NAME}/2" - action = await ops_test.model.units.get(old_unit_name).run_action( + action = juju.ext.model.units.get(old_unit_name).run_action( "get-relation-field", **{ "relation_id": pytest.second_database_relation.id, "field": "password", }, ) - await action.wait() + action.wait() orig_password = action.results.get("value") - action = await ops_test.model.units.get(new_unit_name).run_action( + action = juju.ext.model.units.get(new_unit_name).run_action( "get-relation-field", **{ "relation_id": pytest.second_database_relation.id, "field": "password", }, ) - await action.wait() + action.wait() new_password = action.results.get("value") assert new_password == orig_password diff --git a/tests/v0/integration/test_kafka_charm.py b/tests/v0/integration/test_kafka_charm.py index 6f85461a..636e9898 100644 --- a/tests/v0/integration/test_kafka_charm.py +++ b/tests/v0/integration/test_kafka_charm.py @@ -1,12 +1,11 @@ #!/usr/bin/env python3 # Copyright 2022 Canonical Ltd. # See LICENSE file for licensing details. -import asyncio import logging import subprocess import pytest -from pytest_operator.plugin import OpsTest +from jubilant_adapters import JujuFixture, gather from .helpers import get_application_relation_data, get_juju_secret @@ -26,70 +25,64 @@ @pytest.mark.abort_on_fail @pytest.mark.log_errors_allowed @pytest.mark.skip_if_deployed -async def test_deploy_charms(ops_test: OpsTest, application_charm, kafka_charm): +def test_deploy_charms(juju: JujuFixture, application_charm, kafka_charm): """Deploy both charms (application and the testing kafka app) to use in the tests.""" # Deploy both charms (1 unit for each application to test that later they correctly # set data in the relation application databag using only the leader unit). - await asyncio.gather( - ops_test.model.deploy( + gather( + juju.ext.model.deploy( application_charm, application_name=APPLICATION_APP_NAME, num_units=1, series="jammy" ), - ops_test.model.deploy( + juju.ext.model.deploy( application_charm, application_name=APPLICATION_APP_NAME_SPLIT, num_units=1, series="jammy", ), - ops_test.model.deploy( + juju.ext.model.deploy( kafka_charm, application_name=KAFKA_APP_NAME, num_units=1, series="jammy" ), ) - await ops_test.model.wait_for_idle( - apps=[KAFKA_APP_NAME], status="active", wait_for_exact_units=1 - ) - await ops_test.model.wait_for_idle( + juju.ext.model.wait_for_idle(apps=[KAFKA_APP_NAME], status="active", wait_for_exact_units=1) + juju.ext.model.wait_for_idle( apps=[APPLICATION_APP_NAME], status="active", wait_for_exact_units=1 ) - await ops_test.model.wait_for_idle( + juju.ext.model.wait_for_idle( apps=[APPLICATION_APP_NAME_SPLIT], status="active", wait_for_exact_units=1 ) @pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_without_juju_secrets") -async def test_kafka_relation_with_charm_libraries(ops_test: OpsTest): +def test_kafka_relation_with_charm_libraries(juju: JujuFixture): """Test basic functionality of kafka relation interface.""" # Relate the charms and wait for them exchanging some connection data. - await ops_test.model.add_relation( - KAFKA_APP_NAME, f"{APPLICATION_APP_NAME}:{TOPIC_RELATION_NAME}" - ) - await ops_test.model.wait_for_idle(apps=APP_NAMES, status="active") + juju.ext.model.add_relation(KAFKA_APP_NAME, f"{APPLICATION_APP_NAME}:{TOPIC_RELATION_NAME}") + juju.ext.model.wait_for_idle(apps=APP_NAMES, status="active") # check unit message to check if the topic_created_event is triggered - for unit in ops_test.model.applications[APPLICATION_APP_NAME].units: + for unit in juju.ext.model.applications[APPLICATION_APP_NAME].units: assert unit.workload_status_message == "kafka_topic_created" # check if the topic was granted - for unit in ops_test.model.applications[KAFKA_APP_NAME].units: + for unit in juju.ext.model.applications[KAFKA_APP_NAME].units: assert "granted" in unit.workload_status_message - username = await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, TOPIC_RELATION_NAME, "username" + username = get_application_relation_data( + juju, APPLICATION_APP_NAME, TOPIC_RELATION_NAME, "username" ) - password = await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, TOPIC_RELATION_NAME, "password" + password = get_application_relation_data( + juju, APPLICATION_APP_NAME, TOPIC_RELATION_NAME, "password" ) - bootstrap_server = await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, TOPIC_RELATION_NAME, "endpoints" + bootstrap_server = get_application_relation_data( + juju, APPLICATION_APP_NAME, TOPIC_RELATION_NAME, "endpoints" ) - consumer_group_prefix = await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, TOPIC_RELATION_NAME, "consumer-group-prefix" + consumer_group_prefix = get_application_relation_data( + juju, APPLICATION_APP_NAME, TOPIC_RELATION_NAME, "consumer-group-prefix" ) - topic = await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, TOPIC_RELATION_NAME, "topic" - ) + topic = get_application_relation_data(juju, APPLICATION_APP_NAME, TOPIC_RELATION_NAME, "topic") assert username == "admin" assert password == "password" @@ -100,41 +93,41 @@ async def test_kafka_relation_with_charm_libraries(ops_test: OpsTest): @pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_without_juju_secrets") -async def test_kafka_relation_with_charm_libraries_split_pattern(ops_test: OpsTest): +def test_kafka_relation_with_charm_libraries_split_pattern(juju: JujuFixture): """Test basic functionality of kafka relation interface.""" # Relate the charms and wait for them exchanging some connection data. - await ops_test.model.add_relation( + juju.ext.model.add_relation( KAFKA_APP_NAME, f"{APPLICATION_APP_NAME_SPLIT}:{TOPIC_RELATION_NAME_SPLIT_PATTERN}" ) - await ops_test.model.wait_for_idle(apps=APP_NAMES, status="active") + juju.ext.model.wait_for_idle(apps=APP_NAMES, status="active") # check unit message to check if the topic_created_event is triggered - for unit in ops_test.model.applications[APPLICATION_APP_NAME_SPLIT].units: + for unit in juju.ext.model.applications[APPLICATION_APP_NAME_SPLIT].units: assert unit.workload_status_message == "kafka_topic_created" # check if the topic was granted - for unit in ops_test.model.applications[KAFKA_APP_NAME].units: + for unit in juju.ext.model.applications[KAFKA_APP_NAME].units: assert "granted" in unit.workload_status_message - username = await get_application_relation_data( - ops_test, APPLICATION_APP_NAME_SPLIT, TOPIC_RELATION_NAME_SPLIT_PATTERN, "username" + username = get_application_relation_data( + juju, APPLICATION_APP_NAME_SPLIT, TOPIC_RELATION_NAME_SPLIT_PATTERN, "username" ) - password = await get_application_relation_data( - ops_test, APPLICATION_APP_NAME_SPLIT, TOPIC_RELATION_NAME_SPLIT_PATTERN, "password" + password = get_application_relation_data( + juju, APPLICATION_APP_NAME_SPLIT, TOPIC_RELATION_NAME_SPLIT_PATTERN, "password" ) - bootstrap_server = await get_application_relation_data( - ops_test, APPLICATION_APP_NAME_SPLIT, TOPIC_RELATION_NAME_SPLIT_PATTERN, "endpoints" + bootstrap_server = get_application_relation_data( + juju, APPLICATION_APP_NAME_SPLIT, TOPIC_RELATION_NAME_SPLIT_PATTERN, "endpoints" ) - consumer_group_prefix = await get_application_relation_data( - ops_test, + consumer_group_prefix = get_application_relation_data( + juju, APPLICATION_APP_NAME_SPLIT, TOPIC_RELATION_NAME_SPLIT_PATTERN, "consumer-group-prefix", ) - topic = await get_application_relation_data( - ops_test, APPLICATION_APP_NAME_SPLIT, TOPIC_RELATION_NAME_SPLIT_PATTERN, "topic" + topic = get_application_relation_data( + juju, APPLICATION_APP_NAME_SPLIT, TOPIC_RELATION_NAME_SPLIT_PATTERN, "topic" ) assert username == "admin" @@ -146,40 +139,36 @@ async def test_kafka_relation_with_charm_libraries_split_pattern(ops_test: OpsTe @pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_with_juju_secrets") -async def test_kafka_relation_with_charm_libraries_secrets(ops_test: OpsTest): +def test_kafka_relation_with_charm_libraries_secrets(juju: JujuFixture): """Test basic functionality of kafka relation interface.""" # Relate the charms and wait for them exchanging some connection data. - await ops_test.model.add_relation( - KAFKA_APP_NAME, f"{APPLICATION_APP_NAME}:{TOPIC_RELATION_NAME}" - ) - await ops_test.model.wait_for_idle(apps=APP_NAMES, status="active") + juju.ext.model.add_relation(KAFKA_APP_NAME, f"{APPLICATION_APP_NAME}:{TOPIC_RELATION_NAME}") + juju.ext.model.wait_for_idle(apps=APP_NAMES, status="active") # check unit message to check if the topic_created_event is triggered - for unit in ops_test.model.applications[APPLICATION_APP_NAME].units: + for unit in juju.ext.model.applications[APPLICATION_APP_NAME].units: assert unit.workload_status_message == "kafka_topic_created" # check if the topic was granted - for unit in ops_test.model.applications[KAFKA_APP_NAME].units: + for unit in juju.ext.model.applications[KAFKA_APP_NAME].units: assert "granted" in unit.workload_status_message - secret_uri = await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, TOPIC_RELATION_NAME, f"{PROV_SECRET_PREFIX}user" + secret_uri = get_application_relation_data( + juju, APPLICATION_APP_NAME, TOPIC_RELATION_NAME, f"{PROV_SECRET_PREFIX}user" ) - secret_content = await get_juju_secret(ops_test, secret_uri) + secret_content = get_juju_secret(juju, secret_uri) username = secret_content["username"] password = secret_content["password"] - bootstrap_server = await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, TOPIC_RELATION_NAME, "endpoints" + bootstrap_server = get_application_relation_data( + juju, APPLICATION_APP_NAME, TOPIC_RELATION_NAME, "endpoints" ) - consumer_group_prefix = await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, TOPIC_RELATION_NAME, "consumer-group-prefix" + consumer_group_prefix = get_application_relation_data( + juju, APPLICATION_APP_NAME, TOPIC_RELATION_NAME, "consumer-group-prefix" ) - topic = await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, TOPIC_RELATION_NAME, "topic" - ) + topic = get_application_relation_data(juju, APPLICATION_APP_NAME, TOPIC_RELATION_NAME, "topic") assert username == "admin" assert password == "password" @@ -188,73 +177,73 @@ async def test_kafka_relation_with_charm_libraries_secrets(ops_test: OpsTest): assert topic == "test-topic" -async def test_kafka_bootstrap_server_changed(ops_test: OpsTest): +def test_kafka_bootstrap_server_changed(juju: JujuFixture): """Test that the bootstrap server changed event is correctly triggered.""" - app_unit = ops_test.model.applications[APPLICATION_APP_NAME].units[0] - kafka_unit = ops_test.model.applications[KAFKA_APP_NAME].units[0] + app_unit = juju.ext.model.applications[APPLICATION_APP_NAME].units[0] + kafka_unit = juju.ext.model.applications[KAFKA_APP_NAME].units[0] # set new bootstrap parameters = {"bootstrap-server": "host1:port,host2:port,host3:port"} - action = await kafka_unit.run_action(action_name="sync-bootstrap-server", **parameters) - result = await action.wait() - await ops_test.model.wait_for_idle(apps=APP_NAMES, status="active") + action = kafka_unit.run_action(action_name="sync-bootstrap-server", **parameters) + result = action.wait() + juju.ext.model.wait_for_idle(apps=APP_NAMES, status="active") assert result.results["bootstrap-server"] == "host1:port,host2:port,host3:port" # check that the new bootstrap-server is in the databag - bootstrap_server = await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, TOPIC_RELATION_NAME, "endpoints" + bootstrap_server = get_application_relation_data( + juju, APPLICATION_APP_NAME, TOPIC_RELATION_NAME, "endpoints" ) assert bootstrap_server == "host1:port,host2:port,host3:port" # check that the bootstrap_server_changed event is triggered - for unit in ops_test.model.applications[APPLICATION_APP_NAME].units: + for unit in juju.ext.model.applications[APPLICATION_APP_NAME].units: assert unit.workload_status_message == "kafka_bootstrap_server_changed" # reset unit message - action = await app_unit.run_action(action_name="reset-unit-status") - result = await action.wait() - await ops_test.model.wait_for_idle(apps=APP_NAMES, status="active") + action = app_unit.run_action(action_name="reset-unit-status") + result = action.wait() + juju.ext.model.wait_for_idle(apps=APP_NAMES, status="active") # check if the message is empty - for unit in ops_test.model.applications[APPLICATION_APP_NAME].units: + for unit in juju.ext.model.applications[APPLICATION_APP_NAME].units: assert unit.workload_status_message == "" # configure the same bootstrap-server - action = await kafka_unit.run_action(action_name="sync-bootstrap-server", **parameters) - result = await action.wait() - await ops_test.model.wait_for_idle(apps=APP_NAMES, status="active") + action = kafka_unit.run_action(action_name="sync-bootstrap-server", **parameters) + result = action.wait() + juju.ext.model.wait_for_idle(apps=APP_NAMES, status="active") assert result.results["bootstrap-server"] == "host1:port,host2:port,host3:port" - bootstrap_server = await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, TOPIC_RELATION_NAME, "endpoints" + bootstrap_server = get_application_relation_data( + juju, APPLICATION_APP_NAME, TOPIC_RELATION_NAME, "endpoints" ) assert bootstrap_server == "host1:port,host2:port,host3:port" # check the bootstrap_server_changed event is NOT triggered - for unit in ops_test.model.applications[APPLICATION_APP_NAME].units: + for unit in juju.ext.model.applications[APPLICATION_APP_NAME].units: assert unit.workload_status_message == "" @pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_with_juju_secrets") -async def test_kafka_mtls(ops_test: OpsTest): +def test_kafka_mtls(juju: JujuFixture): """Tests mtls-cert is set as a secret from the requirer side and proper event triggered on provider side.""" # Relate the charms and wait for them exchanging some connection data. - await ops_test.model.add_relation( + juju.ext.model.add_relation( KAFKA_APP_NAME, f"{APPLICATION_APP_NAME_SPLIT}:{TOPIC_RELATION_NAME_SPLIT_PATTERN}" ) - await ops_test.model.wait_for_idle(apps=APP_NAMES, status="active") + juju.ext.model.wait_for_idle(apps=APP_NAMES, status="active") - app_unit = ops_test.model.applications[APPLICATION_APP_NAME_SPLIT].units[0] - action = await app_unit.run_action(action_name="set-mtls-cert") - _ = await action.wait() - await ops_test.model.wait_for_idle(apps=APP_NAMES, status="active") + app_unit = juju.ext.model.applications[APPLICATION_APP_NAME_SPLIT].units[0] + action = app_unit.run_action(action_name="set-mtls-cert") + _ = action.wait() + juju.ext.model.wait_for_idle(apps=APP_NAMES, status="active") - secret_uri = await get_application_relation_data( - ops_test, + secret_uri = get_application_relation_data( + juju, KAFKA_APP_NAME, TOPIC_RELATION_NAME, f"{PROV_SECRET_PREFIX}mtls", related_endpoint=TOPIC_RELATION_NAME_SPLIT_PATTERN, ) - secret_content = await get_juju_secret(ops_test, secret_uri) + secret_content = get_juju_secret(juju, secret_uri) mtls_cert = secret_content["mtls-cert"] - kafka_unit = ops_test.model.applications[KAFKA_APP_NAME].units[0] + kafka_unit = juju.ext.model.applications[KAFKA_APP_NAME].units[0] provider_cert_path = kafka_unit.workload_status_message unit_cert = subprocess.check_output( f"juju ssh {kafka_unit.name} cat {provider_cert_path}", shell=True, universal_newlines=True @@ -265,26 +254,24 @@ async def test_kafka_mtls(ops_test: OpsTest): @pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_without_juju_secrets") -async def test_kafka_roles_relation_with_charm_libraries(ops_test: OpsTest): +def test_kafka_roles_relation_with_charm_libraries(juju: JujuFixture): """Test basic functionality of kafka-roles relation interface.""" # Relate the charms and wait for them exchanging some connection data. - await ops_test.model.add_relation( - KAFKA_APP_NAME, f"{APPLICATION_APP_NAME}:{ROLES_RELATION_NAME}" - ) - await ops_test.model.wait_for_idle(apps=APP_NAMES, status="active") + juju.ext.model.add_relation(KAFKA_APP_NAME, f"{APPLICATION_APP_NAME}:{ROLES_RELATION_NAME}") + juju.ext.model.wait_for_idle(apps=APP_NAMES, status="active") # check unit message to check if the topic_created_event is triggered - for unit in ops_test.model.applications[APPLICATION_APP_NAME].units: + for unit in juju.ext.model.applications[APPLICATION_APP_NAME].units: assert unit.workload_status_message == "kafka_entity_created" # check if the topic role was granted - for unit in ops_test.model.applications[KAFKA_APP_NAME].units: + for unit in juju.ext.model.applications[KAFKA_APP_NAME].units: assert "created" in unit.workload_status_message - entity_name = await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, ROLES_RELATION_NAME, "entity-name" + entity_name = get_application_relation_data( + juju, APPLICATION_APP_NAME, ROLES_RELATION_NAME, "entity-name" ) - entity_pass = await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, ROLES_RELATION_NAME, "entity-password" + entity_pass = get_application_relation_data( + juju, APPLICATION_APP_NAME, ROLES_RELATION_NAME, "entity-password" ) assert entity_name == "admin" @@ -293,26 +280,24 @@ async def test_kafka_roles_relation_with_charm_libraries(ops_test: OpsTest): @pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_with_juju_secrets") -async def test_kafka_roles_relation_with_charm_libraries_secrets(ops_test: OpsTest): +def test_kafka_roles_relation_with_charm_libraries_secrets(juju: JujuFixture): """Test basic functionality of kafka-roles relation interface.""" # Relate the charms and wait for them exchanging some connection data. - await ops_test.model.add_relation( - KAFKA_APP_NAME, f"{APPLICATION_APP_NAME}:{ROLES_RELATION_NAME}" - ) - await ops_test.model.wait_for_idle(apps=APP_NAMES, status="active") + juju.ext.model.add_relation(KAFKA_APP_NAME, f"{APPLICATION_APP_NAME}:{ROLES_RELATION_NAME}") + juju.ext.model.wait_for_idle(apps=APP_NAMES, status="active") # check unit message to check if the topic_created_event is triggered - for unit in ops_test.model.applications[APPLICATION_APP_NAME].units: + for unit in juju.ext.model.applications[APPLICATION_APP_NAME].units: assert unit.workload_status_message == "kafka_entity_created" # check if the topic was granted - for unit in ops_test.model.applications[KAFKA_APP_NAME].units: + for unit in juju.ext.model.applications[KAFKA_APP_NAME].units: assert "created" in unit.workload_status_message - secret_uri = await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, ROLES_RELATION_NAME, f"{PROV_SECRET_PREFIX}entity" + secret_uri = get_application_relation_data( + juju, APPLICATION_APP_NAME, ROLES_RELATION_NAME, f"{PROV_SECRET_PREFIX}entity" ) - secret_content = await get_juju_secret(ops_test, secret_uri) + secret_content = get_juju_secret(juju, secret_uri) entity_name = secret_content["entity-name"] entity_pass = secret_content["entity-password"] diff --git a/tests/v0/integration/test_kafka_connect_charm.py b/tests/v0/integration/test_kafka_connect_charm.py index b57c564c..036abd78 100644 --- a/tests/v0/integration/test_kafka_connect_charm.py +++ b/tests/v0/integration/test_kafka_connect_charm.py @@ -1,12 +1,10 @@ #!/usr/bin/env python3 # Copyright 2022 Canonical Ltd. # See LICENSE file for licensing details. -import asyncio import logging -from pathlib import Path import pytest -from pytest_operator.plugin import OpsTest +from jubilant_adapters import JujuFixture, gather from .helpers import get_application_relation_data, get_juju_secret @@ -25,61 +23,57 @@ @pytest.mark.log_errors_allowed( 'ERROR juju.worker.meterstatus error running "meter-status-changed": charm missing from disk' ) -async def test_deploy_charms( - ops_test: OpsTest, application_charm: Path, kafka_connect_charm: Path -): +def test_deploy_charms(juju: JujuFixture, application_charm: str, kafka_connect_charm: str): """Test deployment of Kafka Connect provider and requirer toy charms.""" - await asyncio.gather( - ops_test.model.deploy( + gather( + juju.ext.model.deploy( application_charm, application_name=REQUIRER_APP_NAME, num_units=1, series="jammy" ), - ops_test.model.deploy( + juju.ext.model.deploy( kafka_connect_charm, application_name=PROVIDER_APP_NAME, num_units=1, series="jammy" ), ) - await ops_test.model.wait_for_idle( + juju.ext.model.wait_for_idle( apps=APP_NAMES, idle_period=30, timeout=1800, status="active", ) - assert ops_test.model.applications[REQUIRER_APP_NAME].status == "active" - assert ops_test.model.applications[PROVIDER_APP_NAME].status == "active" + assert juju.ext.model.applications[REQUIRER_APP_NAME].status == "active" + assert juju.ext.model.applications[PROVIDER_APP_NAME].status == "active" @pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_with_juju_secrets") -async def test_connect_client_relation_with_charm_libraries( - ops_test: OpsTest, request: pytest.FixtureRequest +def test_connect_client_relation_with_charm_libraries( + juju: JujuFixture, request: pytest.FixtureRequest ): """Test basic functionality of Kafka Connect client relation interface.""" # Relate the charms and wait for them exchanging some connection data. - await ops_test.model.add_relation(PROVIDER_APP_NAME, f"{REQUIRER_APP_NAME}:{SOURCE_REL}") - await ops_test.model.wait_for_idle(apps=APP_NAMES, status="active") + juju.ext.model.add_relation(PROVIDER_APP_NAME, f"{REQUIRER_APP_NAME}:{SOURCE_REL}") + juju.ext.model.wait_for_idle(apps=APP_NAMES, status="active") # check unit messagge on requirer side - for unit in ops_test.model.applications[REQUIRER_APP_NAME].units: + for unit in juju.ext.model.applications[REQUIRER_APP_NAME].units: assert unit.workload_status_message == "connect_integration_created" # check unit message on provider side - for unit in ops_test.model.applications[PROVIDER_APP_NAME].units: + for unit in juju.ext.model.applications[PROVIDER_APP_NAME].units: assert "successful" in unit.workload_status_message secret_uri = ( - await get_application_relation_data( - ops_test, REQUIRER_APP_NAME, SOURCE_REL, f"{PROV_SECRET_PREFIX}user" + get_application_relation_data( + juju, REQUIRER_APP_NAME, SOURCE_REL, f"{PROV_SECRET_PREFIX}user" ) or "" ) - secret_content = await get_juju_secret(ops_test, secret_uri) + secret_content = get_juju_secret(juju, secret_uri) username = secret_content["username"] password = secret_content["password"] - endpoints = await get_application_relation_data( - ops_test, REQUIRER_APP_NAME, SOURCE_REL, "endpoints" - ) + endpoints = get_application_relation_data(juju, REQUIRER_APP_NAME, SOURCE_REL, "endpoints") request.config.cache.set("initial_password", password) request.config.cache.set("initial_endpoints", endpoints) @@ -91,7 +85,7 @@ async def test_connect_client_relation_with_charm_libraries( @pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_with_juju_secrets") -async def test_kafka_connect_credentials_change(ops_test: OpsTest, request: pytest.FixtureRequest): +def test_kafka_connect_credentials_change(juju: JujuFixture, request: pytest.FixtureRequest): """Test Kafka Connect credentials change functionality.""" # Get current password password = request.config.cache.get("initial_password", "") @@ -99,13 +93,13 @@ async def test_kafka_connect_credentials_change(ops_test: OpsTest, request: pyte # Change connect password action = ( - await ops_test.model.applications[PROVIDER_APP_NAME] + juju.ext.model.applications[PROVIDER_APP_NAME] .units[0] .run_action("sync", key="password", value="newpass") ) - await action.wait() + action.wait() - await ops_test.model.wait_for_idle( + juju.ext.model.wait_for_idle( apps=APP_NAMES, idle_period=20, timeout=600, @@ -113,13 +107,13 @@ async def test_kafka_connect_credentials_change(ops_test: OpsTest, request: pyte ) secret_uri = ( - await get_application_relation_data( - ops_test, REQUIRER_APP_NAME, SOURCE_REL, f"{PROV_SECRET_PREFIX}user" + get_application_relation_data( + juju, REQUIRER_APP_NAME, SOURCE_REL, f"{PROV_SECRET_PREFIX}user" ) or "" ) - secret_content = await get_juju_secret(ops_test, secret_uri) + secret_content = get_juju_secret(juju, secret_uri) new_password = secret_content["password"] assert password != new_password @@ -128,7 +122,7 @@ async def test_kafka_connect_credentials_change(ops_test: OpsTest, request: pyte @pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_with_juju_secrets") -async def test_kafka_connect_endpoints_change(ops_test: OpsTest, request: pytest.FixtureRequest): +def test_kafka_connect_endpoints_change(juju: JujuFixture, request: pytest.FixtureRequest): """Test Kafka Connect endpoints change functionality.""" # Get current password endpoints = request.config.cache.get("initial_endpoints", "") @@ -136,22 +130,20 @@ async def test_kafka_connect_endpoints_change(ops_test: OpsTest, request: pytest # Change connect endpoints action = ( - await ops_test.model.applications[PROVIDER_APP_NAME] + juju.ext.model.applications[PROVIDER_APP_NAME] .units[0] .run_action("sync", key="endpoints", value="http://worker1:8083") ) - await action.wait() + action.wait() - await ops_test.model.wait_for_idle( + juju.ext.model.wait_for_idle( apps=APP_NAMES, idle_period=20, timeout=600, status="active", ) - new_endpoints = await get_application_relation_data( - ops_test, REQUIRER_APP_NAME, SOURCE_REL, "endpoints" - ) + new_endpoints = get_application_relation_data(juju, REQUIRER_APP_NAME, SOURCE_REL, "endpoints") assert endpoints != new_endpoints assert new_endpoints == "http://worker1:8083" diff --git a/tests/v0/integration/test_opensearch_charm.py b/tests/v0/integration/test_opensearch_charm.py index dd1f4efe..2ed81625 100644 --- a/tests/v0/integration/test_opensearch_charm.py +++ b/tests/v0/integration/test_opensearch_charm.py @@ -1,11 +1,10 @@ #!/usr/bin/env python3 # Copyright 2022 Canonical Ltd. # See LICENSE file for licensing details. -import asyncio import logging import pytest -from pytest_operator.plugin import OpsTest +from jubilant_adapters import JujuFixture, gather from .helpers import get_application_relation_data, get_juju_secret @@ -21,23 +20,23 @@ @pytest.mark.abort_on_fail -async def test_deploy_charms(ops_test: OpsTest, application_charm, opensearch_charm): +def test_deploy_charms(juju: JujuFixture, application_charm, opensearch_charm): """Deploy both charms (application and the testing opensearch app) to use in the tests.""" # Deploy both charms (1 unit for each application to test that later they correctly # set data in the relation application databag using only the leader unit). - await asyncio.gather( - ops_test.model.deploy( + gather( + juju.ext.model.deploy( application_charm, application_name=APPLICATION_APP_NAME, num_units=1, series="jammy" ), - ops_test.model.deploy( + juju.ext.model.deploy( opensearch_charm, application_name=OPENSEARCH_APP_NAME, num_units=1, series="jammy" ), ) - await asyncio.gather( - ops_test.model.wait_for_idle( + gather( + juju.ext.model.wait_for_idle( apps=[OPENSEARCH_APP_NAME], status="active", wait_for_exact_units=1 ), - ops_test.model.wait_for_idle( + juju.ext.model.wait_for_idle( apps=[APPLICATION_APP_NAME], status="active", wait_for_exact_units=1 ), ) @@ -45,33 +44,31 @@ async def test_deploy_charms(ops_test: OpsTest, application_charm, opensearch_ch @pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_without_juju_secrets") -async def test_opensearch_relation_with_charm_libraries(ops_test: OpsTest): +def test_opensearch_relation_with_charm_libraries(juju: JujuFixture): """Test basic functionality of opensearch relation interface.""" # Relate the charms and wait for them exchanging some connection data. - await ops_test.model.add_relation( + juju.ext.model.add_relation( OPENSEARCH_APP_NAME, f"{APPLICATION_APP_NAME}:{INDEX_RELATION_NAME}" ) - await ops_test.model.wait_for_idle(apps=APP_NAMES, status="active") + juju.ext.model.wait_for_idle(apps=APP_NAMES, status="active") # check unit message to check if the index_created_event is triggered - for unit in ops_test.model.applications[APPLICATION_APP_NAME].units: + for unit in juju.ext.model.applications[APPLICATION_APP_NAME].units: assert unit.workload_status_message == "opensearch_index_created" # check if index access is granted - for unit in ops_test.model.applications[OPENSEARCH_APP_NAME].units: + for unit in juju.ext.model.applications[OPENSEARCH_APP_NAME].units: assert "granted" in unit.workload_status_message - username = await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, INDEX_RELATION_NAME, "username" + username = get_application_relation_data( + juju, APPLICATION_APP_NAME, INDEX_RELATION_NAME, "username" ) - password = await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, INDEX_RELATION_NAME, "password" + password = get_application_relation_data( + juju, APPLICATION_APP_NAME, INDEX_RELATION_NAME, "password" ) - endpoints = await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, INDEX_RELATION_NAME, "endpoints" - ) - index = await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, INDEX_RELATION_NAME, "index" + endpoints = get_application_relation_data( + juju, APPLICATION_APP_NAME, INDEX_RELATION_NAME, "endpoints" ) + index = get_application_relation_data(juju, APPLICATION_APP_NAME, INDEX_RELATION_NAME, "index") assert username == "admin" assert password == "password" @@ -81,35 +78,33 @@ async def test_opensearch_relation_with_charm_libraries(ops_test: OpsTest): @pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_with_juju_secrets") -async def test_opensearch_relation_with_charm_libraries_secrets(ops_test: OpsTest): +def test_opensearch_relation_with_charm_libraries_secrets(juju: JujuFixture): """Test basic functionality of opensearch relation interface.""" # Relate the charms and wait for them exchanging some connection data. - await ops_test.model.add_relation( + juju.ext.model.add_relation( OPENSEARCH_APP_NAME, f"{APPLICATION_APP_NAME}:{INDEX_RELATION_NAME}" ) - await ops_test.model.wait_for_idle(apps=APP_NAMES, status="active") + juju.ext.model.wait_for_idle(apps=APP_NAMES, status="active") # check unit message to check if the index_created_event is triggered - for unit in ops_test.model.applications[APPLICATION_APP_NAME].units: + for unit in juju.ext.model.applications[APPLICATION_APP_NAME].units: assert unit.workload_status_message == "opensearch_index_created" # check if index access is granted - for unit in ops_test.model.applications[OPENSEARCH_APP_NAME].units: + for unit in juju.ext.model.applications[OPENSEARCH_APP_NAME].units: assert "granted" in unit.workload_status_message - secret_uri = await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, INDEX_RELATION_NAME, f"{PROV_SECRET_PREFIX}user" + secret_uri = get_application_relation_data( + juju, APPLICATION_APP_NAME, INDEX_RELATION_NAME, f"{PROV_SECRET_PREFIX}user" ) - secret_content = await get_juju_secret(ops_test, secret_uri) + secret_content = get_juju_secret(juju, secret_uri) username = secret_content["username"] password = secret_content["password"] - endpoints = await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, INDEX_RELATION_NAME, "endpoints" - ) - index = await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, INDEX_RELATION_NAME, "index" + endpoints = get_application_relation_data( + juju, APPLICATION_APP_NAME, INDEX_RELATION_NAME, "endpoints" ) + index = get_application_relation_data(juju, APPLICATION_APP_NAME, INDEX_RELATION_NAME, "index") assert username == "admin" assert password == "password" @@ -119,55 +114,55 @@ async def test_opensearch_relation_with_charm_libraries_secrets(ops_test: OpsTes @pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_with_juju_secrets") -async def test_opensearch_relation_secret_changed(ops_test: OpsTest): +def test_opensearch_relation_secret_changed(juju: JujuFixture): """Test basic functionality of opensearch relation interface.""" # Get current password - secret_uri = await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, INDEX_RELATION_NAME, f"{PROV_SECRET_PREFIX}user" + secret_uri = get_application_relation_data( + juju, APPLICATION_APP_NAME, INDEX_RELATION_NAME, f"{PROV_SECRET_PREFIX}user" ) - secret_content = await get_juju_secret(ops_test, secret_uri) + secret_content = get_juju_secret(juju, secret_uri) password = secret_content["password"] # Change admin password unit_name = f"{OPENSEARCH_APP_NAME}/0" - action = await ops_test.model.units.get(unit_name).run_action("change-admin-password") - await action.wait() + action = juju.ext.model.units.get(unit_name).run_action("change-admin-password") + action.wait() - secret_uri = await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, INDEX_RELATION_NAME, f"{PROV_SECRET_PREFIX}user" + secret_uri = get_application_relation_data( + juju, APPLICATION_APP_NAME, INDEX_RELATION_NAME, f"{PROV_SECRET_PREFIX}user" ) - secret_content = await get_juju_secret(ops_test, secret_uri) + secret_content = get_juju_secret(juju, secret_uri) new_password = secret_content["password"] assert password != new_password # check unit message to check if the index_created_event is triggered - for unit in ops_test.model.applications[APPLICATION_APP_NAME].units: + for unit in juju.ext.model.applications[APPLICATION_APP_NAME].units: assert unit.workload_status_message == "opensearch_authentication_updated" @pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_without_juju_secrets") -async def test_opensearch_roles_relation_with_charm_libraries(ops_test: OpsTest): +def test_opensearch_roles_relation_with_charm_libraries(juju: JujuFixture): """Test basic functionality of opensearch-roles relation interface.""" # Relate the charms and wait for them exchanging some connection data. - await ops_test.model.add_relation( + juju.ext.model.add_relation( OPENSEARCH_APP_NAME, f"{APPLICATION_APP_NAME}:{ROLES_RELATION_NAME}" ) - await ops_test.model.wait_for_idle(apps=APP_NAMES, status="active") + juju.ext.model.wait_for_idle(apps=APP_NAMES, status="active") # check unit message to check if the index_entity_created_event is triggered - for unit in ops_test.model.applications[APPLICATION_APP_NAME].units: + for unit in juju.ext.model.applications[APPLICATION_APP_NAME].units: assert unit.workload_status_message == "opensearch_entity_created" # check if index role is created - for unit in ops_test.model.applications[OPENSEARCH_APP_NAME].units: + for unit in juju.ext.model.applications[OPENSEARCH_APP_NAME].units: assert "created" in unit.workload_status_message - entity_name = await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, ROLES_RELATION_NAME, "entity-name" + entity_name = get_application_relation_data( + juju, APPLICATION_APP_NAME, ROLES_RELATION_NAME, "entity-name" ) - entity_pass = await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, ROLES_RELATION_NAME, "entity-password" + entity_pass = get_application_relation_data( + juju, APPLICATION_APP_NAME, ROLES_RELATION_NAME, "entity-password" ) assert entity_name == "admin" @@ -176,26 +171,26 @@ async def test_opensearch_roles_relation_with_charm_libraries(ops_test: OpsTest) @pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_with_juju_secrets") -async def test_opensearch_roles_relation_with_charm_libraries_secrets(ops_test: OpsTest): +def test_opensearch_roles_relation_with_charm_libraries_secrets(juju: JujuFixture): """Test basic functionality of opensearch relation interface.""" # Relate the charms and wait for them exchanging some connection data. - await ops_test.model.add_relation( + juju.ext.model.add_relation( OPENSEARCH_APP_NAME, f"{APPLICATION_APP_NAME}:{ROLES_RELATION_NAME}" ) - await ops_test.model.wait_for_idle(apps=APP_NAMES, status="active") + juju.ext.model.wait_for_idle(apps=APP_NAMES, status="active") # check unit message to check if the index_entity_created_event is triggered - for unit in ops_test.model.applications[APPLICATION_APP_NAME].units: + for unit in juju.ext.model.applications[APPLICATION_APP_NAME].units: assert unit.workload_status_message == "opensearch_entity_created" # check if index role is created - for unit in ops_test.model.applications[OPENSEARCH_APP_NAME].units: + for unit in juju.ext.model.applications[OPENSEARCH_APP_NAME].units: assert "created" in unit.workload_status_message - secret_uri = await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, ROLES_RELATION_NAME, f"{PROV_SECRET_PREFIX}entity" + secret_uri = get_application_relation_data( + juju, APPLICATION_APP_NAME, ROLES_RELATION_NAME, f"{PROV_SECRET_PREFIX}entity" ) - secret_content = await get_juju_secret(ops_test, secret_uri) + secret_content = get_juju_secret(juju, secret_uri) entity_name = secret_content["entity-name"] entity_pass = secret_content["entity-password"] diff --git a/tests/v0/integration/test_rolling_upgrade.py b/tests/v0/integration/test_rolling_upgrade.py index eea24d20..bea259e7 100644 --- a/tests/v0/integration/test_rolling_upgrade.py +++ b/tests/v0/integration/test_rolling_upgrade.py @@ -1,13 +1,12 @@ #!/usr/bin/env python3 # Copyright 2023 Canonical Ltd. # See LICENSE file for licensing details. -import asyncio import logging from pathlib import Path import pytest import yaml -from pytest_operator.plugin import OpsTest +from jubilant_adapters import JujuFixture, gather from .helpers import ( get_application_relation_data, @@ -29,47 +28,47 @@ SECRET_REF_PREFIX = "secret-" -async def downgrade_to_databag(ops_test, app_name): +def downgrade_to_databag(juju, app_name): """Helper function simulating a "rolling downgrade". The data_interfaces module is replaced "on-the-fly" by an older version, where Juju Secrets aren't enabled yet. """ - for unit in ops_test.model.applications[app_name].units: + for unit in juju.ext.model.applications[app_name].units: unit_name_with_dash = unit.name.replace("/", "-") complete_command = ( "scp tests/v0/integration/data/data_interfaces.py " f"{unit.name}:/var/lib/juju/agents/unit-{unit_name_with_dash}/charm/lib/charms/data_platform_libs/v0/" ) - x, stdout, y = await ops_test.juju(*complete_command.split()) + x, stdout, y = juju.juju(*complete_command.split()) -async def upgrade_to_secrets(ops_test, app_name): +def upgrade_to_secrets(juju, app_name): """Helper function simulating a "rolling upgrade". The data_interfaces module is replaced "on-the-fly" by the latest version, using Juju secrets. """ - for unit in ops_test.model.applications[app_name].units: + for unit in juju.ext.model.applications[app_name].units: unit_name_with_dash = unit.name.replace("/", "-") complete_command = ( "scp lib/charms/data_platform_libs/v0/data_interfaces.py " f"{unit.name}:/var/lib/juju/agents/unit-{unit_name_with_dash}/charm/lib/charms/data_platform_libs/v0/" ) - x, stdout, y = await ops_test.juju(*complete_command.split()) + x, stdout, y = juju.juju(*complete_command.split()) @pytest.mark.abort_on_fail -async def test_deploy_charms( - ops_test: OpsTest, application_charm, database_charm, dp_libs_ubuntu_series +def test_deploy_charms( + juju: JujuFixture, application_charm, database_charm, dp_libs_ubuntu_series ): """Deploy both charms (application and database) to use in the tests.""" - await asyncio.gather( - ops_test.model.deploy( + gather( + juju.ext.model.deploy( application_charm, application_name=APPLICATION_APP_NAME, num_units=1, series=dp_libs_ubuntu_series, ), - ops_test.model.deploy( + juju.ext.model.deploy( database_charm, resources={ "database-image": DATABASE_APP_METADATA["resources"]["database-image"][ @@ -81,7 +80,7 @@ async def test_deploy_charms( series=dp_libs_ubuntu_series, ), ) - await ops_test.model.wait_for_idle( + juju.ext.model.wait_for_idle( apps=[APPLICATION_APP_NAME, DATABASE_APP_NAME], status="active", wait_for_exact_units=1, @@ -95,60 +94,60 @@ async def test_deploy_charms( @pytest.mark.abort_on_fail @pytest.mark.parametrize("component", ["app", "unit"]) -async def test_peer_relation(component, ops_test: OpsTest): +def test_peer_relation(component, juju: JujuFixture): """Relating Requires (databag only) with Provides (that could use secrets). This should fall back to databag usage. """ - await downgrade_to_databag(ops_test, DATABASE_APP_NAME) + downgrade_to_databag(juju, DATABASE_APP_NAME) # Setting and verifying two fields (one that should be a secret, one plain text) - leader_id = await get_leader_id(ops_test, DATABASE_APP_NAME) + leader_id = get_leader_id(juju, DATABASE_APP_NAME) unit_name = f"{DATABASE_APP_NAME}/{leader_id}" - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "set-peer-relation-field", **{"component": component, "field": "monitor-password", "value": "blablabla"}, ) - await action.wait() + action.wait() - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "set-peer-relation-field", **{"component": component, "field": "not-a-secret", "value": "plain text"}, ) - await action.wait() + action.wait() - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "get-peer-relation-field", **{"component": component, "field": "monitor-password"} ) - await action.wait() + action.wait() assert action.results.get("value") == "blablabla" - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "get-peer-relation-field", **{"component": component, "field": "not-a-secret"} ) - await action.wait() + action.wait() assert action.results.get("value") == "plain text" # Upgrade - await upgrade_to_secrets(ops_test, DATABASE_APP_NAME) + upgrade_to_secrets(juju, DATABASE_APP_NAME) # Both secret and databag content can be modified - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "set-peer-relation-field", **{"component": component, "field": "monitor-password", "value": "blablabla_new"}, ) - await action.wait() + action.wait() - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "set-peer-relation-field", **{"component": component, "field": "not-a-secret", "value": "even more plain text"}, ) - await action.wait() + action.wait() # ...and secret is moved away from the databag assert not ( - await get_application_relation_data( - ops_test, + get_application_relation_data( + juju, DATABASE_APP_NAME, "database-peers", "monitor-password", @@ -157,56 +156,56 @@ async def test_peer_relation(component, ops_test: OpsTest): ) assert ( - await get_application_relation_data( - ops_test, DATABASE_APP_NAME, "database-peers", "not-a-secret", app_or_unit=component + get_application_relation_data( + juju, DATABASE_APP_NAME, "database-peers", "not-a-secret", app_or_unit=component ) ) == "even more plain text" assert not ( - await get_application_relation_data( - ops_test, DATABASE_APP_NAME, "database-peers", "internal-secret", app_or_unit=component + get_application_relation_data( + juju, DATABASE_APP_NAME, "database-peers", "internal-secret", app_or_unit=component ) ) - secret = await get_secret_by_label(ops_test, f"database-peers.database.{component}") + secret = get_secret_by_label(juju, f"database-peers.database.{component}") assert secret.get("monitor-password") == "blablabla_new" - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "get-peer-relation-field", **{"component": component, "field": "monitor-password"} ) - await action.wait() + action.wait() assert action.results.get("value") == "blablabla_new" - await upgrade_to_secrets(ops_test, DATABASE_APP_NAME) + upgrade_to_secrets(juju, DATABASE_APP_NAME) - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "get-peer-relation-field", **{"component": component, "field": "not-a-secret"} ) - await action.wait() + action.wait() assert action.results.get("value") == "even more plain text" # Removing both secret and databag content can be modified - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "delete-peer-relation-field", **{"component": component, "field": "monitor-password"} ) - await action.wait() + action.wait() - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "delete-peer-relation-field", **{"component": component, "field": "not-a-secret"} ) - await action.wait() + action.wait() # ...successfully - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "get-peer-relation-field", **{"component": component, "field": "monitor-password"} ) - await action.wait() + action.wait() assert not action.results.get("value") - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "get-peer-relation-field", **{"component": component, "field": "not-a-secret"} ) - await action.wait() + action.wait() assert not action.results.get("value") @@ -221,78 +220,78 @@ async def test_peer_relation(component, ops_test: OpsTest): @pytest.mark.abort_on_fail -async def test_unbalanced_versions_falling_back_to_databag_req_databag_vs_prov_secrets( - ops_test: OpsTest, +def test_unbalanced_versions_falling_back_to_databag_req_databag_vs_prov_secrets( + juju: JujuFixture, ): """Relating Requires (databag only) with Provides (that could use secrets). This should fall back to databag usage. """ - await downgrade_to_databag(ops_test, APPLICATION_APP_NAME) + downgrade_to_databag(juju, APPLICATION_APP_NAME) # Storing Relation object in 'pytest' global namespace for the session - pytest.first_database_relation = await ops_test.model.add_relation( + pytest.first_database_relation = juju.ext.model.add_relation( f"{APPLICATION_APP_NAME}:{DB_FIRST_DATABASE_RELATION_NAME}", DATABASE_APP_NAME ) - await ops_test.model.wait_for_idle(apps=APP_NAMES, status="active") + juju.ext.model.wait_for_idle(apps=APP_NAMES, status="active") # Secrest are correctly set in databag - username = await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, DB_FIRST_DATABASE_RELATION_NAME, "username" + username = get_application_relation_data( + juju, APPLICATION_APP_NAME, DB_FIRST_DATABASE_RELATION_NAME, "username" ) - password = await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, DB_FIRST_DATABASE_RELATION_NAME, "password" + password = get_application_relation_data( + juju, APPLICATION_APP_NAME, DB_FIRST_DATABASE_RELATION_NAME, "password" ) assert username assert password assert ( - await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, DB_FIRST_DATABASE_RELATION_NAME, "secret-user" + get_application_relation_data( + juju, APPLICATION_APP_NAME, DB_FIRST_DATABASE_RELATION_NAME, "secret-user" ) is None ) # Interface functions (invoked by actions below) are consistent - leader_id = await get_leader_id(ops_test, DATABASE_APP_NAME) + leader_id = get_leader_id(juju, DATABASE_APP_NAME) leader_name = f"{DATABASE_APP_NAME}/{leader_id}" - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "get-relation-self-side-field", **{"relation_id": pytest.first_database_relation.id, "field": "username"}, ) - await action.wait() + action.wait() assert action.results.get("value") == username - leader_id = await get_leader_id(ops_test, DATABASE_APP_NAME) + leader_id = get_leader_id(juju, DATABASE_APP_NAME) leader_name = f"{DATABASE_APP_NAME}/{leader_id}" - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "get-relation-self-side-field", **{"relation_id": pytest.first_database_relation.id, "field": "password"}, ) - await action.wait() + action.wait() assert action.results.get("value") == password @pytest.mark.abort_on_fail -async def test_transparent_upgrade_requires_databag_vs_provides_secrets(ops_test: OpsTest): +def test_transparent_upgrade_requires_databag_vs_provides_secrets(juju: JujuFixture): """Upgrading Requires to use secrets (if possible). YET, already existing relations keep using the databag for all operations (fetch, update, delete) """ - await upgrade_to_secrets(ops_test, APPLICATION_APP_NAME) + upgrade_to_secrets(juju, APPLICATION_APP_NAME) # Application charm leader - leader_app_id = await get_leader_id(ops_test, DATABASE_APP_NAME) + leader_app_id = get_leader_id(juju, DATABASE_APP_NAME) leader_app_name = f"{APPLICATION_APP_NAME}/{leader_app_id}" # DB leader - leader_id = await get_leader_id(ops_test, DATABASE_APP_NAME) + leader_id = get_leader_id(juju, DATABASE_APP_NAME) leader_name = f"{DATABASE_APP_NAME}/{leader_id}" # Set new sensitive information (if secrets: stored in 'secret-user') uris_new_val = "http://username:password@example.com" - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "set-relation-field", **{ "relation_id": pytest.first_database_relation.id, @@ -300,80 +299,80 @@ async def test_transparent_upgrade_requires_databag_vs_provides_secrets(ops_test "value": uris_new_val, }, ) - await action.wait() + action.wait() # Relation data is correct - assert uris_new_val == await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, DB_FIRST_DATABASE_RELATION_NAME, "uris" + assert uris_new_val == get_application_relation_data( + juju, APPLICATION_APP_NAME, DB_FIRST_DATABASE_RELATION_NAME, "uris" ) assert ( - await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, DB_FIRST_DATABASE_RELATION_NAME, "secret-user" + get_application_relation_data( + juju, APPLICATION_APP_NAME, DB_FIRST_DATABASE_RELATION_NAME, "secret-user" ) is None ) # Interface functions (invoked by actions below) are consistent - action = await ops_test.model.units.get(leader_app_name).run_action( + action = juju.ext.model.units.get(leader_app_name).run_action( "get-relation-field", **{"relation_id": pytest.first_database_relation.id, "field": "uris"}, ) - await action.wait() + action.wait() assert action.results.get("value") == uris_new_val - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "get-relation-self-side-field", **{"relation_id": pytest.first_database_relation.id, "field": "uris"}, ) - await action.wait() + action.wait() assert action.results.get("value") == uris_new_val - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "delete-relation-field", **{"relation_id": pytest.first_database_relation.id, "field": "uris"}, ) - await action.wait() + action.wait() # Relation data is correct assert ( - await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, DB_FIRST_DATABASE_RELATION_NAME, "uris" + get_application_relation_data( + juju, APPLICATION_APP_NAME, DB_FIRST_DATABASE_RELATION_NAME, "uris" ) is None ) assert ( - await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, DB_FIRST_DATABASE_RELATION_NAME, "secret-user" + get_application_relation_data( + juju, APPLICATION_APP_NAME, DB_FIRST_DATABASE_RELATION_NAME, "secret-user" ) is None ) # Interface functions (invoked by actions below) are consistent - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "get-relation-self-side-field", **{"relation_id": pytest.first_database_relation.id, "field": "uris"}, ) - await action.wait() + action.wait() assert not action.results.get("value") @pytest.mark.abort_on_fail -async def test_transparent_upgrade_requires_databag_vs_provides_secrets_upgrade_new_secret( - ops_test: OpsTest, +def test_transparent_upgrade_requires_databag_vs_provides_secrets_upgrade_new_secret( + juju: JujuFixture, ): """After Requires upgrade, we stay on the databag.""" # Application charm leader - leader_app_id = await get_leader_id(ops_test, DATABASE_APP_NAME) + leader_app_id = get_leader_id(juju, DATABASE_APP_NAME) leader_app_name = f"{APPLICATION_APP_NAME}/{leader_app_id}" # DB leader - leader_id = await get_leader_id(ops_test, DATABASE_APP_NAME) + leader_id = get_leader_id(juju, DATABASE_APP_NAME) leader_name = f"{DATABASE_APP_NAME}/{leader_id}" # Set new sensitive information (if secrets: stored in 'secret-tls') tls_ca_val = "" - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "set-relation-field", **{ "relation_id": pytest.first_database_relation.id, @@ -381,73 +380,73 @@ async def test_transparent_upgrade_requires_databag_vs_provides_secrets_upgrade_ "value": tls_ca_val, }, ) - await action.wait() + action.wait() # We stay on the databag - assert await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, DB_FIRST_DATABASE_RELATION_NAME, "tls-ca" + assert get_application_relation_data( + juju, APPLICATION_APP_NAME, DB_FIRST_DATABASE_RELATION_NAME, "tls-ca" ) assert ( - await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, DB_FIRST_DATABASE_RELATION_NAME, "secret-tls" + get_application_relation_data( + juju, APPLICATION_APP_NAME, DB_FIRST_DATABASE_RELATION_NAME, "secret-tls" ) is None ) # Interface functions (invoked by actions below) are consistent - action = await ops_test.model.units.get(leader_app_name).run_action( + action = juju.ext.model.units.get(leader_app_name).run_action( "get-relation-field", **{"relation_id": pytest.first_database_relation.id, "field": "tls-ca"}, ) - await action.wait() + action.wait() assert action.results.get("value") == tls_ca_val - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "get-relation-self-side-field", **{"relation_id": pytest.first_database_relation.id, "field": "tls-ca"}, ) - await action.wait() + action.wait() assert action.results.get("value") == tls_ca_val - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "delete-relation-field", **{"relation_id": pytest.first_database_relation.id, "field": "tls-ca"}, ) - await action.wait() + action.wait() - action = await ops_test.model.units.get(leader_app_name).run_action( + action = juju.ext.model.units.get(leader_app_name).run_action( "get-relation-field", **{"relation_id": pytest.first_database_relation.id, "field": "tls-ca"}, ) - await action.wait() + action.wait() assert not action.results.get("value") # Previously existing sensitive data stays where it was - password = await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, DB_FIRST_DATABASE_RELATION_NAME, "password" + password = get_application_relation_data( + juju, APPLICATION_APP_NAME, DB_FIRST_DATABASE_RELATION_NAME, "password" ) assert password assert ( - await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, DB_FIRST_DATABASE_RELATION_NAME, "secret-user" + get_application_relation_data( + juju, APPLICATION_APP_NAME, DB_FIRST_DATABASE_RELATION_NAME, "secret-user" ) is None ) - action = await ops_test.model.units.get(leader_app_name).run_action( + action = juju.ext.model.units.get(leader_app_name).run_action( "get-relation-field", **{"relation_id": pytest.first_database_relation.id, "field": "password"}, ) - await action.wait() + action.wait() assert action.results.get("value") == password - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "get-relation-self-side-field", **{"relation_id": pytest.first_database_relation.id, "field": "password"}, ) - await action.wait() + action.wait() assert action.results.get("value") == password @@ -457,77 +456,77 @@ async def test_transparent_upgrade_requires_databag_vs_provides_secrets_upgrade_ @pytest.mark.abort_on_fail -async def test_unbalanced_versions_falling_back_to_databag_req_secrets_vs_prov_databag( - ops_test: OpsTest, +def test_unbalanced_versions_falling_back_to_databag_req_secrets_vs_prov_databag( + juju: JujuFixture, ): """Relating Requires (secrets available) with Provides (databag only). This should fall back to databag usage. """ - await downgrade_to_databag(ops_test, DATABASE_APP_NAME) + downgrade_to_databag(juju, DATABASE_APP_NAME) # Storing Relation object in 'pytest' global namespace for the session - pytest.second_database_relation = await ops_test.model.add_relation( + pytest.second_database_relation = juju.ext.model.add_relation( f"{APPLICATION_APP_NAME}:{DB_SECOND_DATABASE_RELATION_NAME}", DATABASE_APP_NAME ) - await ops_test.model.wait_for_idle(apps=APP_NAMES, status="active") + juju.ext.model.wait_for_idle(apps=APP_NAMES, status="active") # Relation data is correct - username = await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, DB_SECOND_DATABASE_RELATION_NAME, "username" + username = get_application_relation_data( + juju, APPLICATION_APP_NAME, DB_SECOND_DATABASE_RELATION_NAME, "username" ) - password = await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, DB_SECOND_DATABASE_RELATION_NAME, "password" + password = get_application_relation_data( + juju, APPLICATION_APP_NAME, DB_SECOND_DATABASE_RELATION_NAME, "password" ) assert ( - await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, DB_SECOND_DATABASE_RELATION_NAME, "secret-user" + get_application_relation_data( + juju, APPLICATION_APP_NAME, DB_SECOND_DATABASE_RELATION_NAME, "secret-user" ) is None ) # Interface functions (invoked by actions below) are consistent - leader_id = await get_leader_id(ops_test, DATABASE_APP_NAME) + leader_id = get_leader_id(juju, DATABASE_APP_NAME) leader_name = f"{DATABASE_APP_NAME}/{leader_id}" - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "get-relation-self-side-field", **{"relation_id": pytest.second_database_relation.id, "field": "username"}, ) - await action.wait() + action.wait() assert action.results.get("value") == username - leader_id = await get_leader_id(ops_test, DATABASE_APP_NAME) + leader_id = get_leader_id(juju, DATABASE_APP_NAME) leader_name = f"{DATABASE_APP_NAME}/{leader_id}" - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "get-relation-self-side-field", **{"relation_id": pytest.second_database_relation.id, "field": "password"}, ) - await action.wait() + action.wait() assert action.results.get("value") == password @pytest.mark.abort_on_fail -async def test_transparent_upgrade_keeping_databag_requires_secrets_vs_provides_databag( - ops_test: OpsTest, +def test_transparent_upgrade_keeping_databag_requires_secrets_vs_provides_databag( + juju: JujuFixture, ): """Upgrading Provides to use secrets (if possible). YET, already existing relations keep using the databag for all operations (fetch, update, delete) """ - await upgrade_to_secrets(ops_test, DATABASE_APP_NAME) + upgrade_to_secrets(juju, DATABASE_APP_NAME) # Application charm leader - leader_app_id = await get_leader_id(ops_test, DATABASE_APP_NAME) + leader_app_id = get_leader_id(juju, DATABASE_APP_NAME) leader_app_name = f"{APPLICATION_APP_NAME}/{leader_app_id}" # DB leader - leader_id = await get_leader_id(ops_test, DATABASE_APP_NAME) + leader_id = get_leader_id(juju, DATABASE_APP_NAME) leader_name = f"{DATABASE_APP_NAME}/{leader_id}" # Set new sensitive information (if secrets: stored in 'secret-user') uris_new_val = "http://username:password@example.com" - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "set-relation-field", **{ "relation_id": pytest.second_database_relation.id, @@ -535,80 +534,80 @@ async def test_transparent_upgrade_keeping_databag_requires_secrets_vs_provides_ "value": uris_new_val, }, ) - await action.wait() + action.wait() # Relation data is correct - assert uris_new_val == await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, DB_SECOND_DATABASE_RELATION_NAME, "uris" + assert uris_new_val == get_application_relation_data( + juju, APPLICATION_APP_NAME, DB_SECOND_DATABASE_RELATION_NAME, "uris" ) assert ( - await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, DB_SECOND_DATABASE_RELATION_NAME, "secret-user" + get_application_relation_data( + juju, APPLICATION_APP_NAME, DB_SECOND_DATABASE_RELATION_NAME, "secret-user" ) is None ) # Interface functions (invoked by actions below) are consistent - action = await ops_test.model.units.get(leader_app_name).run_action( + action = juju.ext.model.units.get(leader_app_name).run_action( "get-relation-field", **{"relation_id": pytest.second_database_relation.id, "field": "uris"}, ) - await action.wait() + action.wait() assert action.results.get("value") == uris_new_val - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "get-relation-self-side-field", **{"relation_id": pytest.second_database_relation.id, "field": "uris"}, ) - await action.wait() + action.wait() assert action.results.get("value") == uris_new_val - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "delete-relation-field", **{"relation_id": pytest.second_database_relation.id, "field": "uris"}, ) - await action.wait() + action.wait() # Relation data is correct assert ( - await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, DB_SECOND_DATABASE_RELATION_NAME, "uris" + get_application_relation_data( + juju, APPLICATION_APP_NAME, DB_SECOND_DATABASE_RELATION_NAME, "uris" ) is None ) assert ( - await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, DB_SECOND_DATABASE_RELATION_NAME, "secret-user" + get_application_relation_data( + juju, APPLICATION_APP_NAME, DB_SECOND_DATABASE_RELATION_NAME, "secret-user" ) is None ) # Interface functions (invoked by actions below) are consistent - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "get-relation-self-side-field", **{"relation_id": pytest.second_database_relation.id, "field": "uris"}, ) - await action.wait() + action.wait() assert not action.results.get("value") @pytest.mark.abort_on_fail -async def test_transparent_upgrade_keeping_databag_requires_secrets_vs_provides_databag_upgrade_new_secret( - ops_test: OpsTest, +def test_transparent_upgrade_keeping_databag_requires_secrets_vs_provides_databag_upgrade_new_secret( + juju: JujuFixture, ): """After Provider upgrade, the relation remains on databag.""" # Application charm leader - leader_app_id = await get_leader_id(ops_test, DATABASE_APP_NAME) + leader_app_id = get_leader_id(juju, DATABASE_APP_NAME) leader_app_name = f"{APPLICATION_APP_NAME}/{leader_app_id}" # DB leader - leader_id = await get_leader_id(ops_test, DATABASE_APP_NAME) + leader_id = get_leader_id(juju, DATABASE_APP_NAME) leader_name = f"{DATABASE_APP_NAME}/{leader_id}" # Set new sensitive information (if secrets: stored in 'secret-tls') tls_ca_val = "" - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "set-relation-field", **{ "relation_id": pytest.second_database_relation.id, @@ -616,70 +615,70 @@ async def test_transparent_upgrade_keeping_databag_requires_secrets_vs_provides_ "value": tls_ca_val, }, ) - await action.wait() + action.wait() - tls_ca_val = await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, DB_SECOND_DATABASE_RELATION_NAME, "tls-ca" + tls_ca_val = get_application_relation_data( + juju, APPLICATION_APP_NAME, DB_SECOND_DATABASE_RELATION_NAME, "tls-ca" ) assert ( - await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, DB_SECOND_DATABASE_RELATION_NAME, "secret-tls" + get_application_relation_data( + juju, APPLICATION_APP_NAME, DB_SECOND_DATABASE_RELATION_NAME, "secret-tls" ) is None ) # Interface functions (invoked by actions below) are consistent - action = await ops_test.model.units.get(leader_app_name).run_action( + action = juju.ext.model.units.get(leader_app_name).run_action( "get-relation-field", **{"relation_id": pytest.second_database_relation.id, "field": "tls-ca"}, ) - await action.wait() + action.wait() assert action.results.get("value") == tls_ca_val - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "get-relation-self-side-field", **{"relation_id": pytest.second_database_relation.id, "field": "tls-ca"}, ) - await action.wait() + action.wait() assert action.results.get("value") == tls_ca_val - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "delete-relation-field", **{"relation_id": pytest.second_database_relation.id, "field": "tls-ca"}, ) - await action.wait() + action.wait() - action = await ops_test.model.units.get(leader_app_name).run_action( + action = juju.ext.model.units.get(leader_app_name).run_action( "get-relation-field", **{"relation_id": pytest.second_database_relation.id, "field": "tls-ca"}, ) - await action.wait() + action.wait() assert not action.results.get("value") # Previously existing sensitive data stays where it was - password = await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, DB_SECOND_DATABASE_RELATION_NAME, "password" + password = get_application_relation_data( + juju, APPLICATION_APP_NAME, DB_SECOND_DATABASE_RELATION_NAME, "password" ) assert password assert ( - await get_application_relation_data( - ops_test, APPLICATION_APP_NAME, DB_SECOND_DATABASE_RELATION_NAME, "secret-user" + get_application_relation_data( + juju, APPLICATION_APP_NAME, DB_SECOND_DATABASE_RELATION_NAME, "secret-user" ) is None ) - action = await ops_test.model.units.get(leader_app_name).run_action( + action = juju.ext.model.units.get(leader_app_name).run_action( "get-relation-field", **{"relation_id": pytest.second_database_relation.id, "field": "password"}, ) - await action.wait() + action.wait() assert action.results.get("value") == password - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "get-relation-self-side-field", **{"relation_id": pytest.second_database_relation.id, "field": "password"}, ) - await action.wait() + action.wait() assert action.results.get("value") == password diff --git a/tests/v0/integration/test_rolling_upgrade_from_specific_version.py b/tests/v0/integration/test_rolling_upgrade_from_specific_version.py index b7e70548..33a96146 100644 --- a/tests/v0/integration/test_rolling_upgrade_from_specific_version.py +++ b/tests/v0/integration/test_rolling_upgrade_from_specific_version.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 # Copyright 2023 Canonical Ltd. # See LICENSE file for licensing details. -import asyncio import logging import os import subprocess @@ -9,8 +8,8 @@ import pytest import yaml +from jubilant_adapters import JujuFixture, gather from lib.charms.data_platform_libs.v0.data_interfaces import LIBPATCH -from pytest_operator.plugin import OpsTest from .helpers import get_leader_id @@ -37,14 +36,14 @@ def old_version_to_upgrade_from(): return LIBPATCH - go_backwards -async def downgrade_to_old_version(ops_test, app_name): +def downgrade_to_old_version(juju, app_name): """Helper function simulating a "rolling downgrade". The data_interfaces module is replaced "on-the-fly" by an older version. """ version = old_version_to_upgrade_from() logger.info(f"Downgrading {app_name} to version {version}") - for unit in ops_test.model.applications[app_name].units: + for unit in juju.ext.model.applications[app_name].units: unit_name_with_dash = unit.name.replace("/", "-") path = f"tests/v0/integration/data/data_interfaces.py.v{version}" @@ -60,42 +59,42 @@ async def downgrade_to_old_version(ops_test, app_name): f"{unit.name}:/var/lib/juju/agents/unit-{unit_name_with_dash}" "/charm/lib/charms/data_platform_libs/v0/data_interfaces.py" ) - ret_code, stdout, _ = await ops_test.juju(*complete_command.split()) + ret_code, stdout, _ = juju.juju(*complete_command.split()) # scp was successful assert not ret_code, f"Couldn't perform copy to {unit.name}." -async def upgrade_to_new_version(ops_test, app_name): +def upgrade_to_new_version(juju, app_name): """Helper function simulating a "rolling upgrade". The data_interfaces module is replaced "on-the-fly" by the latest version. """ logger.info(f"Upgrading {app_name} to latest version") - for unit in ops_test.model.applications[app_name].units: + for unit in juju.ext.model.applications[app_name].units: unit_name_with_dash = unit.name.replace("/", "-") complete_command = ( "scp lib/charms/data_platform_libs/v0/data_interfaces.py " f"{unit.name}:/var/lib/juju/agents/unit-{unit_name_with_dash}/charm/lib/charms/data_platform_libs/v0/" ) - ret_code, stdout, _ = await ops_test.juju(*complete_command.split()) + ret_code, stdout, _ = juju.juju(*complete_command.split()) # scp was successful assert not ret_code, f"Couldn't perform copy to {unit.name}." @pytest.mark.usefixtures("fetch_old_versions") @pytest.mark.abort_on_fail -async def test_deploy_charms( - ops_test: OpsTest, application_charm, database_charm, dp_libs_ubuntu_series +def test_deploy_charms( + juju: JujuFixture, application_charm, database_charm, dp_libs_ubuntu_series ): """Deploy both charms (application and database) to use in the tests.""" - await asyncio.gather( - ops_test.model.deploy( + gather( + juju.ext.model.deploy( application_charm, application_name=APPLICATION_APP_NAME, num_units=1, series=dp_libs_ubuntu_series, ), - ops_test.model.deploy( + juju.ext.model.deploy( database_charm, resources={ "database-image": DATABASE_APP_METADATA["resources"]["database-image"][ @@ -107,7 +106,7 @@ async def test_deploy_charms( series=dp_libs_ubuntu_series, ), ) - await ops_test.model.wait_for_idle( + juju.ext.model.wait_for_idle( apps=[APPLICATION_APP_NAME, DATABASE_APP_NAME], status="active", wait_for_exact_units=1, @@ -121,89 +120,89 @@ async def test_deploy_charms( @pytest.mark.abort_on_fail @pytest.mark.parametrize("component", ["app", "unit"]) -async def test_peer_relation(component, ops_test: OpsTest): +def test_peer_relation(component, juju: JujuFixture): """Peer relation safe across upgrades.""" - await downgrade_to_old_version(ops_test, DATABASE_APP_NAME) + downgrade_to_old_version(juju, DATABASE_APP_NAME) # Setting and verifying two fields (one that should be a secret, one plain text) - leader_id = await get_leader_id(ops_test, DATABASE_APP_NAME) + leader_id = get_leader_id(juju, DATABASE_APP_NAME) unit_name = f"{DATABASE_APP_NAME}/{leader_id}" - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "set-peer-relation-field", **{"component": component, "field": "monitor-password", "value": "blablabla"}, ) - await action.wait() + action.wait() - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "set-peer-relation-field", **{"component": component, "field": "not-a-secret", "value": "plain text"}, ) - await action.wait() + action.wait() - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "get-peer-relation-field", **{"component": component, "field": "monitor-password"} ) - await action.wait() + action.wait() assert action.results.get("value") == "blablabla" - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "get-peer-relation-field", **{"component": component, "field": "not-a-secret"} ) - await action.wait() + action.wait() assert action.results.get("value") == "plain text" # Upgrade - await upgrade_to_new_version(ops_test, DATABASE_APP_NAME) + upgrade_to_new_version(juju, DATABASE_APP_NAME) # Both secret and databag content can be modified -- even twice ;-) - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "set-peer-relation-field-multiple", **{"component": component, "field": "monitor-password", "value": "blablabla_new"}, ) - await action.wait() + action.wait() - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "set-peer-relation-field-multiple", **{"component": component, "field": "not-a-secret", "value": "even more plain text"}, ) - await action.wait() + action.wait() - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "get-peer-relation-field", **{"component": component, "field": "monitor-password"} ) - await action.wait() + action.wait() assert action.results.get("value") == "blablabla_new2" - await upgrade_to_new_version(ops_test, DATABASE_APP_NAME) + upgrade_to_new_version(juju, DATABASE_APP_NAME) - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "get-peer-relation-field", **{"component": component, "field": "not-a-secret"} ) - await action.wait() + action.wait() assert action.results.get("value") == "even more plain text2" # Removing both secret and databag content can be modified - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "delete-peer-relation-field", **{"component": component, "field": "monitor-password"} ) - await action.wait() + action.wait() - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "delete-peer-relation-field", **{"component": component, "field": "not-a-secret"} ) - await action.wait() + action.wait() # ...successfully - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "get-peer-relation-field", **{"component": component, "field": "monitor-password"} ) - await action.wait() + action.wait() assert not action.results.get("value") - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "get-peer-relation-field", **{"component": component, "field": "not-a-secret"} ) - await action.wait() + action.wait() assert not action.results.get("value") @@ -218,75 +217,75 @@ async def test_peer_relation(component, ops_test: OpsTest): @pytest.mark.abort_on_fail -async def test_unbalanced_versions_req_old_vs_prov_new( - ops_test: OpsTest, +def test_unbalanced_versions_req_old_vs_prov_new( + juju: JujuFixture, ): """Relating Requires (old version) with Provides (latest).""" - await downgrade_to_old_version(ops_test, APPLICATION_APP_NAME) + downgrade_to_old_version(juju, APPLICATION_APP_NAME) # Storing Relation object in 'pytest' global namespace for the session - pytest.first_database_relation = await ops_test.model.add_relation( + pytest.first_database_relation = juju.ext.model.add_relation( f"{APPLICATION_APP_NAME}:{DB_FIRST_DATABASE_RELATION_NAME}", DATABASE_APP_NAME ) - await ops_test.model.wait_for_idle(apps=APP_NAMES, status="active") + juju.ext.model.wait_for_idle(apps=APP_NAMES, status="active") # Username - leader_app_id = await get_leader_id(ops_test, APPLICATION_APP_NAME) + leader_app_id = get_leader_id(juju, APPLICATION_APP_NAME) leader_app_name = f"{APPLICATION_APP_NAME}/{leader_app_id}" - action = await ops_test.model.units.get(leader_app_name).run_action( + action = juju.ext.model.units.get(leader_app_name).run_action( "get-relation-field", **{"relation_id": pytest.first_database_relation.id, "field": "username"}, ) - await action.wait() + action.wait() assert action.results.get("value") username = action.results.get("value") # Password - action = await ops_test.model.units.get(leader_app_name).run_action( + action = juju.ext.model.units.get(leader_app_name).run_action( "get-relation-field", **{"relation_id": pytest.first_database_relation.id, "field": "password"}, ) - await action.wait() + action.wait() assert action.results.get("value") password = action.results.get("value") # Username is correct - leader_id = await get_leader_id(ops_test, DATABASE_APP_NAME) + leader_id = get_leader_id(juju, DATABASE_APP_NAME) leader_name = f"{DATABASE_APP_NAME}/{leader_id}" - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "get-relation-self-side-field", **{"relation_id": pytest.first_database_relation.id, "field": "username"}, ) - await action.wait() + action.wait() assert action.results.get("value") == username # Password is correct - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "get-relation-self-side-field", **{"relation_id": pytest.first_database_relation.id, "field": "password"}, ) - await action.wait() + action.wait() assert action.results.get("value") == password @pytest.mark.abort_on_fail -async def test_rolling_upgrade_requires_old_vs_provides_new(ops_test: OpsTest): +def test_rolling_upgrade_requires_old_vs_provides_new(juju: JujuFixture): """Upgrading Requires to the latest version of the libs.""" - await upgrade_to_new_version(ops_test, APPLICATION_APP_NAME) + upgrade_to_new_version(juju, APPLICATION_APP_NAME) # Application charm leader - leader_app_id = await get_leader_id(ops_test, APPLICATION_APP_NAME) + leader_app_id = get_leader_id(juju, APPLICATION_APP_NAME) leader_app_name = f"{APPLICATION_APP_NAME}/{leader_app_id}" # DB leader - leader_id = await get_leader_id(ops_test, DATABASE_APP_NAME) + leader_id = get_leader_id(juju, DATABASE_APP_NAME) leader_name = f"{DATABASE_APP_NAME}/{leader_id}" # Set new sensitive information (if secrets: stored in 'secret-user') uris_new_val = "http://username:password@example.com" - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "set-relation-field", **{ "relation_id": pytest.first_database_relation.id, @@ -294,54 +293,54 @@ async def test_rolling_upgrade_requires_old_vs_provides_new(ops_test: OpsTest): "value": uris_new_val, }, ) - await action.wait() + action.wait() # Interface functions (invoked by actions below) are consistent - action = await ops_test.model.units.get(leader_app_name).run_action( + action = juju.ext.model.units.get(leader_app_name).run_action( "get-relation-field", **{"relation_id": pytest.first_database_relation.id, "field": "uris"}, ) - await action.wait() + action.wait() assert action.results.get("value") == uris_new_val - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "get-relation-self-side-field", **{"relation_id": pytest.first_database_relation.id, "field": "uris"}, ) - await action.wait() + action.wait() assert action.results.get("value") == uris_new_val - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "delete-relation-field", **{"relation_id": pytest.first_database_relation.id, "field": "uris"}, ) - await action.wait() + action.wait() # Interface functions (invoked by actions below) are consistent - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "get-relation-self-side-field", **{"relation_id": pytest.first_database_relation.id, "field": "uris"}, ) - await action.wait() + action.wait() assert not action.results.get("value") @pytest.mark.abort_on_fail -async def test_rolling_upgrade_requires_old_vs_provides_new_upgrade_new_secret( - ops_test: OpsTest, +def test_rolling_upgrade_requires_old_vs_provides_new_upgrade_new_secret( + juju: JujuFixture, ): """After Requires upgrade new secrets are possible to define.""" # Application charm leader - leader_app_id = await get_leader_id(ops_test, APPLICATION_APP_NAME) + leader_app_id = get_leader_id(juju, APPLICATION_APP_NAME) leader_app_name = f"{APPLICATION_APP_NAME}/{leader_app_id}" # DB leader - leader_id = await get_leader_id(ops_test, DATABASE_APP_NAME) + leader_id = get_leader_id(juju, DATABASE_APP_NAME) leader_name = f"{DATABASE_APP_NAME}/{leader_id}" # Set new sensitive information (if secrets: stored in 'secret-tls') tls_ca_val = "" - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "set-relation-field", **{ "relation_id": pytest.first_database_relation.id, @@ -349,50 +348,50 @@ async def test_rolling_upgrade_requires_old_vs_provides_new_upgrade_new_secret( "value": tls_ca_val, }, ) - await action.wait() + action.wait() # Interface functions (invoked by actions below) are consistent - action = await ops_test.model.units.get(leader_app_name).run_action( + action = juju.ext.model.units.get(leader_app_name).run_action( "get-relation-field", **{"relation_id": pytest.first_database_relation.id, "field": "tls-ca"}, ) - await action.wait() + action.wait() assert action.results.get("value") == tls_ca_val - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "get-relation-self-side-field", **{"relation_id": pytest.first_database_relation.id, "field": "tls-ca"}, ) - await action.wait() + action.wait() assert action.results.get("value") == tls_ca_val - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "delete-relation-field", **{"relation_id": pytest.first_database_relation.id, "field": "tls-ca"}, ) - await action.wait() + action.wait() - action = await ops_test.model.units.get(leader_app_name).run_action( + action = juju.ext.model.units.get(leader_app_name).run_action( "get-relation-field", **{"relation_id": pytest.first_database_relation.id, "field": "tls-ca"}, ) - await action.wait() + action.wait() assert not action.results.get("value") # Username exists - action = await ops_test.model.units.get(leader_app_name).run_action( + action = juju.ext.model.units.get(leader_app_name).run_action( "get-relation-field", **{"relation_id": pytest.first_database_relation.id, "field": "username"}, ) - await action.wait() + action.wait() assert action.results.get("value") # Password exists - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "get-relation-self-side-field", **{"relation_id": pytest.first_database_relation.id, "field": "password"}, ) - await action.wait() + action.wait() assert action.results.get("value") @@ -402,55 +401,55 @@ async def test_rolling_upgrade_requires_old_vs_provides_new_upgrade_new_secret( @pytest.mark.abort_on_fail -async def test_unbalanced_versions_req_new_vs_prov_old( - ops_test: OpsTest, +def test_unbalanced_versions_req_new_vs_prov_old( + juju: JujuFixture, ): """Relating Requires (latest) with Provides (old version).""" - await downgrade_to_old_version(ops_test, DATABASE_APP_NAME) + downgrade_to_old_version(juju, DATABASE_APP_NAME) # Storing Relation object in 'pytest' global namespace for the session - pytest.second_database_relation = await ops_test.model.add_relation( + pytest.second_database_relation = juju.ext.model.add_relation( f"{APPLICATION_APP_NAME}:{DB_SECOND_DATABASE_RELATION_NAME}", DATABASE_APP_NAME ) - await ops_test.model.wait_for_idle(apps=APP_NAMES, status="active") + juju.ext.model.wait_for_idle(apps=APP_NAMES, status="active") # Username exists - leader_id = await get_leader_id(ops_test, DATABASE_APP_NAME) + leader_id = get_leader_id(juju, DATABASE_APP_NAME) leader_name = f"{DATABASE_APP_NAME}/{leader_id}" - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "get-relation-self-side-field", **{"relation_id": pytest.second_database_relation.id, "field": "username"}, ) - await action.wait() + action.wait() assert action.results.get("value") # Password exists - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "get-relation-self-side-field", **{"relation_id": pytest.second_database_relation.id, "field": "password"}, ) - await action.wait() + action.wait() assert action.results.get("value") @pytest.mark.abort_on_fail -async def test_rolling_upgrade_requires_new_vs_provides_old( - ops_test: OpsTest, +def test_rolling_upgrade_requires_new_vs_provides_old( + juju: JujuFixture, ): """Upgrading Provides to latest version.""" - await upgrade_to_new_version(ops_test, DATABASE_APP_NAME) + upgrade_to_new_version(juju, DATABASE_APP_NAME) # Application charm leader - leader_app_id = await get_leader_id(ops_test, APPLICATION_APP_NAME) + leader_app_id = get_leader_id(juju, APPLICATION_APP_NAME) leader_app_name = f"{APPLICATION_APP_NAME}/{leader_app_id}" # DB leader - leader_id = await get_leader_id(ops_test, DATABASE_APP_NAME) + leader_id = get_leader_id(juju, DATABASE_APP_NAME) leader_name = f"{DATABASE_APP_NAME}/{leader_id}" # Set new sensitive information (if secrets: stored in 'secret-user') uris_new_val = "http://username:password@example.com" - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "set-relation-field", **{ "relation_id": pytest.second_database_relation.id, @@ -458,54 +457,54 @@ async def test_rolling_upgrade_requires_new_vs_provides_old( "value": uris_new_val, }, ) - await action.wait() + action.wait() # Interface functions (invoked by actions below) are consistent - action = await ops_test.model.units.get(leader_app_name).run_action( + action = juju.ext.model.units.get(leader_app_name).run_action( "get-relation-field", **{"relation_id": pytest.second_database_relation.id, "field": "uris"}, ) - await action.wait() + action.wait() assert action.results.get("value") == uris_new_val - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "get-relation-self-side-field", **{"relation_id": pytest.second_database_relation.id, "field": "uris"}, ) - await action.wait() + action.wait() assert action.results.get("value") == uris_new_val - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "delete-relation-field", **{"relation_id": pytest.second_database_relation.id, "field": "uris"}, ) - await action.wait() + action.wait() # Interface functions (invoked by actions below) are consistent - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "get-relation-self-side-field", **{"relation_id": pytest.second_database_relation.id, "field": "uris"}, ) - await action.wait() + action.wait() assert not action.results.get("value") @pytest.mark.abort_on_fail -async def test_rolling_upgrade_requires_new_vs_provides_old_upgrade_new_secret( - ops_test: OpsTest, +def test_rolling_upgrade_requires_new_vs_provides_old_upgrade_new_secret( + juju: JujuFixture, ): """After Provider upgrade, we can safely define new secrets.""" # Application charm leader - leader_app_id = await get_leader_id(ops_test, APPLICATION_APP_NAME) + leader_app_id = get_leader_id(juju, APPLICATION_APP_NAME) leader_app_name = f"{APPLICATION_APP_NAME}/{leader_app_id}" # DB leader - leader_id = await get_leader_id(ops_test, DATABASE_APP_NAME) + leader_id = get_leader_id(juju, DATABASE_APP_NAME) leader_name = f"{DATABASE_APP_NAME}/{leader_id}" # Set new sensitive information (if secrets: stored in 'secret-tls') tls_ca_val = "" - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "set-relation-field", **{ "relation_id": pytest.second_database_relation.id, @@ -513,47 +512,47 @@ async def test_rolling_upgrade_requires_new_vs_provides_old_upgrade_new_secret( "value": tls_ca_val, }, ) - await action.wait() + action.wait() # Interface functions (invoked by actions below) are consistent - action = await ops_test.model.units.get(leader_app_name).run_action( + action = juju.ext.model.units.get(leader_app_name).run_action( "get-relation-field", **{"relation_id": pytest.second_database_relation.id, "field": "tls-ca"}, ) - await action.wait() + action.wait() assert action.results.get("value") == tls_ca_val - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "get-relation-self-side-field", **{"relation_id": pytest.second_database_relation.id, "field": "tls-ca"}, ) - await action.wait() + action.wait() assert action.results.get("value") == tls_ca_val - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "delete-relation-field", **{"relation_id": pytest.second_database_relation.id, "field": "tls-ca"}, ) - await action.wait() + action.wait() - action = await ops_test.model.units.get(leader_app_name).run_action( + action = juju.ext.model.units.get(leader_app_name).run_action( "get-relation-field", **{"relation_id": pytest.second_database_relation.id, "field": "tls-ca"}, ) - await action.wait() + action.wait() assert not action.results.get("value") - action = await ops_test.model.units.get(leader_app_name).run_action( + action = juju.ext.model.units.get(leader_app_name).run_action( "get-relation-field", **{"relation_id": pytest.second_database_relation.id, "field": "password"}, ) - await action.wait() + action.wait() assert action.results.get("value") password = action.results.get("value") - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "get-relation-self-side-field", **{"relation_id": pytest.second_database_relation.id, "field": "password"}, ) - await action.wait() + action.wait() assert action.results.get("value") == password diff --git a/tests/v0/integration/test_s3_charm.py b/tests/v0/integration/test_s3_charm.py index 7bfc0e5b..5de621d3 100644 --- a/tests/v0/integration/test_s3_charm.py +++ b/tests/v0/integration/test_s3_charm.py @@ -1,11 +1,10 @@ #!/usr/bin/env python3 # Copyright 2022 Canonical Ltd. # See LICENSE file for licensing details. -import asyncio import logging import pytest -from pytest_operator.plugin import OpsTest +from jubilant_adapters import JujuFixture, gather from .helpers import get_connection_info @@ -19,48 +18,42 @@ @pytest.mark.abort_on_fail -async def test_deploy_charms( - ops_test: OpsTest, application_s3_charm, s3_charm, dp_libs_ubuntu_series -): +def test_deploy_charms(juju: JujuFixture, application_s3_charm, s3_charm, dp_libs_ubuntu_series): """Deploy both charms (application and s3 provider app) to use in the tests.""" # Deploy both charms (2 units for each application to test that later they correctly # set data in the relation application databag using only the leader unit). - await asyncio.gather( - ops_test.model.deploy( + gather( + juju.ext.model.deploy( application_s3_charm, application_name=APPLICATION_APP_NAME, num_units=2, series=dp_libs_ubuntu_series, ), - ops_test.model.deploy(s3_charm, application_name=S3_APP_NAME, num_units=2, series="jammy"), + juju.ext.model.deploy(s3_charm, application_name=S3_APP_NAME, num_units=2, series="jammy"), ) - await ops_test.model.wait_for_idle(apps=[S3_APP_NAME], status="active", wait_for_exact_units=2) - await ops_test.model.wait_for_idle( + juju.ext.model.wait_for_idle(apps=[S3_APP_NAME], status="active", wait_for_exact_units=2) + juju.ext.model.wait_for_idle( apps=[APPLICATION_APP_NAME], status="waiting", wait_for_exact_units=2 ) @pytest.mark.abort_on_fail -async def test_s3_relation_with_charm_libraries(ops_test: OpsTest): +def test_s3_relation_with_charm_libraries(juju: JujuFixture): """Test basic functionality of s3-credentials relation interface.""" # Relate the charms and wait for them exchanging some connection data. - await ops_test.model.add_relation( - S3_APP_NAME, f"{APPLICATION_APP_NAME}:{FIRST_S3_RELATION_NAME}" - ) - await ops_test.model.wait_for_idle(apps=APP_NAMES, status="active") + juju.ext.model.add_relation(S3_APP_NAME, f"{APPLICATION_APP_NAME}:{FIRST_S3_RELATION_NAME}") + juju.ext.model.wait_for_idle(apps=APP_NAMES, status="active") # Get the connection info to connect to the S3 endpoint. - connection_info = await get_connection_info( - ops_test, APPLICATION_APP_NAME, FIRST_S3_RELATION_NAME - ) + connection_info = get_connection_info(juju, APPLICATION_APP_NAME, FIRST_S3_RELATION_NAME) # Get connection info from relation and check their correctness. assert connection_info["access-key"] == "test-access-key" assert connection_info["secret-key"] == "test-secret-key" assert connection_info["bucket"] == f"{APPLICATION_APP_NAME}_first_bucket" -async def test_two_applications_doesnt_share_the_same_relation_data( - ops_test: OpsTest, application_s3_charm +def test_two_applications_doesnt_share_the_same_relation_data( + juju: JujuFixture, application_s3_charm ): """Test that two applications connect to the s3 provider with different credentials.""" # Set some variables to use in this test. @@ -69,43 +62,41 @@ async def test_two_applications_doesnt_share_the_same_relation_data( all_app_names.extend(APP_NAMES) # Deploy another application. - await ops_test.model.deploy( + juju.ext.model.deploy( application_s3_charm, application_name=another_application_app_name, series="jammy" ) - await ops_test.model.wait_for_idle(apps=[S3_APP_NAME, APPLICATION_APP_NAME], status="active") - await ops_test.model.wait_for_idle(apps=[another_application_app_name], status="waiting") + juju.ext.model.wait_for_idle(apps=[S3_APP_NAME, APPLICATION_APP_NAME], status="active") + juju.ext.model.wait_for_idle(apps=[another_application_app_name], status="waiting") # Relate the new application with the s3 provider # and wait for them exchanging some connection data. - await ops_test.model.add_relation( + juju.ext.model.add_relation( f"{another_application_app_name}:{FIRST_S3_RELATION_NAME}", S3_APP_NAME ) - await ops_test.model.wait_for_idle(apps=all_app_names, status="active") + juju.ext.model.wait_for_idle(apps=all_app_names, status="active") # Assert the two applications have different relation (connection) data. - application_connection_info = await get_connection_info( - ops_test, APPLICATION_APP_NAME, FIRST_S3_RELATION_NAME + application_connection_info = get_connection_info( + juju, APPLICATION_APP_NAME, FIRST_S3_RELATION_NAME ) - another_application_connection_info = await get_connection_info( - ops_test, another_application_app_name, FIRST_S3_RELATION_NAME + another_application_connection_info = get_connection_info( + juju, another_application_app_name, FIRST_S3_RELATION_NAME ) assert application_connection_info != another_application_connection_info -async def test_an_application_can_request_multiple_s3_providers(ops_test: OpsTest): +def test_an_application_can_request_multiple_s3_providers(juju: JujuFixture): """Test that an application can request additional s3 credentials using the same interface.""" # Relate the charms using another relation and wait for them exchanging some connection data. - await ops_test.model.add_relation( - f"{APPLICATION_APP_NAME}:{SECOND_S3_RELATION_NAME}", S3_APP_NAME - ) - await ops_test.model.wait_for_idle(apps=APP_NAMES, status="active") + juju.ext.model.add_relation(f"{APPLICATION_APP_NAME}:{SECOND_S3_RELATION_NAME}", S3_APP_NAME) + juju.ext.model.wait_for_idle(apps=APP_NAMES, status="active") # Get the connection infos from the two different relations. - first_s3_connection_info = await get_connection_info( - ops_test, APPLICATION_APP_NAME, FIRST_S3_RELATION_NAME + first_s3_connection_info = get_connection_info( + juju, APPLICATION_APP_NAME, FIRST_S3_RELATION_NAME ) - second_s3_connection_info = await get_connection_info( - ops_test, APPLICATION_APP_NAME, SECOND_S3_RELATION_NAME + second_s3_connection_info = get_connection_info( + juju, APPLICATION_APP_NAME, SECOND_S3_RELATION_NAME ) # Assert the two applications have different relation (connection) data. diff --git a/tests/v0/integration/test_secrets.py b/tests/v0/integration/test_secrets.py index 1b9ce6e0..db6c0a8d 100644 --- a/tests/v0/integration/test_secrets.py +++ b/tests/v0/integration/test_secrets.py @@ -4,7 +4,7 @@ import logging import pytest -from pytest_operator.plugin import OpsTest +from jubilant_adapters import JujuFixture from .helpers import get_leader_id, get_non_leader_id @@ -14,20 +14,18 @@ @pytest.mark.abort_on_fail -async def test_deploy_charms(ops_test: OpsTest, secrets_charm): +def test_deploy_charms(juju: JujuFixture, secrets_charm): """Deploy both charm to use in the tests.""" - await ops_test.model.deploy( - secrets_charm, application_name=APP_NAME, num_units=2, series="jammy" - ) - await ops_test.model.wait_for_idle(apps=[APP_NAME], status="active", wait_for_exact_units=2) + juju.ext.model.deploy(secrets_charm, application_name=APP_NAME, num_units=2, series="jammy") + juju.ext.model.wait_for_idle(apps=[APP_NAME], status="active", wait_for_exact_units=2) @pytest.mark.abort_on_fail -async def test_add_get_secret_app(ops_test: OpsTest): +def test_add_get_secret_app(juju: JujuFixture): """Test basic functionality of getting/setting cached secrets.""" - leader_id = await get_leader_id(ops_test, APP_NAME) + leader_id = get_leader_id(juju, APP_NAME) leader_name = f"{APP_NAME}/{leader_id}" - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "add-secret", **{ "label": "grandmas-apple-pie", @@ -35,13 +33,13 @@ async def test_add_get_secret_app(ops_test: OpsTest): "scope": "app", }, ) - await action.wait() + action.wait() assert action.results["return-code"] == 0 - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "get-secret", **{"label": "grandmas-apple-pie"} ) - await action.wait() + action.wait() assert action.results.get("grandmas-apple-pie") == {"secret-ingredient": "cinnamon"} @@ -49,11 +47,11 @@ async def test_add_get_secret_app(ops_test: OpsTest): @pytest.mark.log_errors_allowed( 'cannot apply changes: creating secrets: secret with label "grandmas-apple-pie" already exists' ) -async def test_secret_label_unique(ops_test: OpsTest): +def test_secret_label_unique(juju: JujuFixture): """Test basic functionality of getting/setting cached secrets.""" - leader_id = await get_leader_id(ops_test, APP_NAME) + leader_id = get_leader_id(juju, APP_NAME) leader_name = f"{APP_NAME}/{leader_id}" - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "add-secret", **{ "label": "grandmas-apple-pie", @@ -61,14 +59,14 @@ async def test_secret_label_unique(ops_test: OpsTest): "scope": "unit", }, ) - await action.wait() + action.wait() @pytest.mark.abort_on_fail -async def test_add_get_secret_unit(ops_test: OpsTest): - unit_id = await get_non_leader_id(ops_test, APP_NAME) +def test_add_get_secret_unit(juju: JujuFixture): + unit_id = get_non_leader_id(juju, APP_NAME) unit_name = f"{APP_NAME}/{unit_id}" - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "add-secret", **{ "label": "grandmas-cranberry-pie", @@ -76,12 +74,12 @@ async def test_add_get_secret_unit(ops_test: OpsTest): "scope": "unit", }, ) - await action.wait() + action.wait() - action = await ops_test.model.units.get(unit_name).run_action( + action = juju.ext.model.units.get(unit_name).run_action( "get-secret", **{"label": "grandmas-cranberry-pie"} ) - await action.wait() + action.wait() assert action.results.get("grandmas-cranberry-pie") == { "secret-ingredient": "cranberry jam added!" @@ -89,11 +87,11 @@ async def test_add_get_secret_unit(ops_test: OpsTest): @pytest.mark.abort_on_fail -async def test_set_secret(ops_test: OpsTest): +def test_set_secret(juju: JujuFixture): """Test basic functionality of getting/setting cached secrets.""" - leader_id = await get_leader_id(ops_test, APP_NAME) + leader_id = get_leader_id(juju, APP_NAME) leader_name = f"{APP_NAME}/{leader_id}" - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "add-secret", **{ "label": "auntie-susans-muffins", @@ -101,9 +99,9 @@ async def test_set_secret(ops_test: OpsTest): "scope": "app", }, ) - await action.wait() + action.wait() - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "set-secret", **{ "label": "auntie-susans-muffins", @@ -114,12 +112,12 @@ async def test_set_secret(ops_test: OpsTest): "scope": "app", }, ) - await action.wait() + action.wait() - action = await ops_test.model.units.get(leader_name).run_action( + action = juju.ext.model.units.get(leader_name).run_action( "get-secret", **{"label": "auntie-susans-muffins"} ) - await action.wait() + action.wait() assert action.results.get("auntie-susans-muffins") == { "secret-ingredient": "nutella and chocolate chips", "baking-time": "25mins sharp", diff --git a/tests/v0/integration/test_status.py b/tests/v0/integration/test_status.py index 33e5c976..34629015 100644 --- a/tests/v0/integration/test_status.py +++ b/tests/v0/integration/test_status.py @@ -1,11 +1,10 @@ -import asyncio import json import logging from pathlib import Path import pytest import yaml -from pytest_operator.plugin import OpsTest +from jubilant_adapters import JujuFixture, gather from .helpers import ( get_application_relation_data, @@ -25,27 +24,27 @@ @pytest.mark.abort_on_fail @pytest.mark.skip_if_deployed -async def test_deploy_charms( - ops_test: OpsTest, +def test_deploy_charms( + juju: JujuFixture, application_charm, database_charm, dp_libs_ubuntu_series, ): """Deploy both charms (application and database) to use in the tests.""" - await asyncio.gather( - ops_test.model.deploy( + gather( + juju.ext.model.deploy( application_charm, application_name=APPLICATION_APP_1, num_units=1, series=dp_libs_ubuntu_series, ), - ops_test.model.deploy( + juju.ext.model.deploy( application_charm, application_name=APPLICATION_APP_2, num_units=1, series=dp_libs_ubuntu_series, ), - ops_test.model.deploy( + juju.ext.model.deploy( database_charm, resources={ "database-image": DATABASE_APP_METADATA["resources"]["database-image"][ @@ -58,60 +57,60 @@ async def test_deploy_charms( ), ) - await ops_test.model.wait_for_idle(apps=APP_NAMES, status="active", idle_period=30) + juju.ext.model.wait_for_idle(apps=APP_NAMES, status="active", idle_period=30) @pytest.mark.abort_on_fail -async def test_relate_application(ops_test: OpsTest): +def test_relate_application(juju: JujuFixture): # Relate the charms and wait for them exchanging some connection data. - await ops_test.model.add_relation(DATABASE_APP_NAME, f"{APPLICATION_APP_1}:{RELATION_NAME}") - await ops_test.model.add_relation(DATABASE_APP_NAME, f"{APPLICATION_APP_2}:{RELATION_NAME}") - await ops_test.model.wait_for_idle(apps=APP_NAMES, status="active") + juju.ext.model.add_relation(DATABASE_APP_NAME, f"{APPLICATION_APP_1}:{RELATION_NAME}") + juju.ext.model.add_relation(DATABASE_APP_NAME, f"{APPLICATION_APP_2}:{RELATION_NAME}") + juju.ext.model.wait_for_idle(apps=APP_NAMES, status="active") @pytest.mark.abort_on_fail -async def test_raise_status(ops_test: OpsTest): - app_1_rel = next(iter(ops_test.model.applications[APPLICATION_APP_1].relations)) - app_2_rel = next(iter(ops_test.model.applications[APPLICATION_APP_2].relations)) - db_unit = ops_test.model.applications[DATABASE_APP_NAME].units[0].name +def test_raise_status(juju: JujuFixture): + app_1_rel = next(iter(juju.ext.model.applications[APPLICATION_APP_1].relations)) + app_2_rel = next(iter(juju.ext.model.applications[APPLICATION_APP_2].relations)) + db_unit = juju.ext.model.applications[DATABASE_APP_NAME].units[0].name # raise different status on different relations - action = await ops_test.model.units.get(db_unit).run_action( + action = juju.ext.model.units.get(db_unit).run_action( "raise-status", **{"relation-id": app_1_rel.id, "status-code": 4001}, ) - await action.wait() + action.wait() - action = await ops_test.model.units.get(db_unit).run_action( + action = juju.ext.model.units.get(db_unit).run_action( "raise-status", **{"relation-id": app_2_rel.id, "status-code": 4002}, ) - await action.wait() + action.wait() - await ops_test.model.wait_for_idle(apps=APP_NAMES, status="active", idle_period=30) + juju.ext.model.wait_for_idle(apps=APP_NAMES, status="active", idle_period=30) assert ( - "[4001]" in ops_test.model.applications[APPLICATION_APP_1].units[0].workload_status_message + "[4001]" in juju.ext.model.applications[APPLICATION_APP_1].units[0].workload_status_message ) assert ( - "[4002]" in ops_test.model.applications[APPLICATION_APP_2].units[0].workload_status_message + "[4002]" in juju.ext.model.applications[APPLICATION_APP_2].units[0].workload_status_message ) # raise another status on appi - action = await ops_test.model.units.get(db_unit).run_action( + action = juju.ext.model.units.get(db_unit).run_action( "raise-status", **{"relation-id": app_1_rel.id, "status-code": 1000}, ) - await action.wait() + action.wait() - await ops_test.model.wait_for_idle(apps=APP_NAMES, status="active", idle_period=30) + juju.ext.model.wait_for_idle(apps=APP_NAMES, status="active", idle_period=30) assert ( "[1000, 4001]" - in ops_test.model.applications[APPLICATION_APP_1].units[0].workload_status_message + in juju.ext.model.applications[APPLICATION_APP_1].units[0].workload_status_message ) assert ( - "[4002]" in ops_test.model.applications[APPLICATION_APP_2].units[0].workload_status_message + "[4002]" in juju.ext.model.applications[APPLICATION_APP_2].units[0].workload_status_message ) status_schema_raw = json.load( @@ -120,9 +119,7 @@ async def test_raise_status(ops_test: OpsTest): status_schema_map = {o.get("code"): o for o in status_schema_raw.get("statuses", [])} # Verify rel data matches status schema - rel_data_1 = await get_application_relation_data( - ops_test, APPLICATION_APP_1, RELATION_NAME, "status" - ) + rel_data_1 = get_application_relation_data(juju, APPLICATION_APP_1, RELATION_NAME, "status") assert rel_data_1 assert len(json.loads(rel_data_1)) == 2 for obj in json.loads(rel_data_1): @@ -134,54 +131,54 @@ async def test_raise_status(ops_test: OpsTest): @pytest.mark.abort_on_fail -async def test_resolve_status(ops_test: OpsTest): - app_1_rel = next(iter(ops_test.model.applications[APPLICATION_APP_1].relations)) - app_2_rel = next(iter(ops_test.model.applications[APPLICATION_APP_2].relations)) - db_unit = ops_test.model.applications[DATABASE_APP_NAME].units[0].name +def test_resolve_status(juju: JujuFixture): + app_1_rel = next(iter(juju.ext.model.applications[APPLICATION_APP_1].relations)) + app_2_rel = next(iter(juju.ext.model.applications[APPLICATION_APP_2].relations)) + db_unit = juju.ext.model.applications[DATABASE_APP_NAME].units[0].name # resolve different status on different relations - action = await ops_test.model.units.get(db_unit).run_action( + action = juju.ext.model.units.get(db_unit).run_action( "resolve-status", **{"relation-id": app_1_rel.id, "status-code": 4001}, ) - await action.wait() + action.wait() - action = await ops_test.model.units.get(db_unit).run_action( + action = juju.ext.model.units.get(db_unit).run_action( "resolve-status", **{"relation-id": app_2_rel.id, "status-code": 4002}, ) - await action.wait() + action.wait() - await ops_test.model.wait_for_idle(apps=APP_NAMES, status="active", idle_period=30) + juju.ext.model.wait_for_idle(apps=APP_NAMES, status="active", idle_period=30) assert ( - "[1000]" in ops_test.model.applications[APPLICATION_APP_1].units[0].workload_status_message + "[1000]" in juju.ext.model.applications[APPLICATION_APP_1].units[0].workload_status_message ) - assert "[]" in ops_test.model.applications[APPLICATION_APP_2].units[0].workload_status_message + assert "[]" in juju.ext.model.applications[APPLICATION_APP_2].units[0].workload_status_message @pytest.mark.abort_on_fail -async def test_clear_statuses(ops_test: OpsTest): - app_1_rel = next(iter(ops_test.model.applications[APPLICATION_APP_1].relations)) - db_unit = ops_test.model.applications[DATABASE_APP_NAME].units[0].name +def test_clear_statuses(juju: JujuFixture): + app_1_rel = next(iter(juju.ext.model.applications[APPLICATION_APP_1].relations)) + db_unit = juju.ext.model.applications[DATABASE_APP_NAME].units[0].name # raise 4002 status on appi - action = await ops_test.model.units.get(db_unit).run_action( + action = juju.ext.model.units.get(db_unit).run_action( "raise-status", **{"relation-id": app_1_rel.id, "status-code": 4002}, ) - await action.wait() + action.wait() - await ops_test.model.wait_for_idle(apps=APP_NAMES, status="active", idle_period=30) + juju.ext.model.wait_for_idle(apps=APP_NAMES, status="active", idle_period=30) assert ( "[1000, 4002]" - in ops_test.model.applications[APPLICATION_APP_1].units[0].workload_status_message + in juju.ext.model.applications[APPLICATION_APP_1].units[0].workload_status_message ) - action = await ops_test.model.units.get(db_unit).run_action( + action = juju.ext.model.units.get(db_unit).run_action( "clear-statuses", **{"relation-id": app_1_rel.id}, ) # All statuses should be cleared - await ops_test.model.wait_for_idle(apps=APP_NAMES, status="active", idle_period=30) - assert "[]" in ops_test.model.applications[APPLICATION_APP_1].units[0].workload_status_message + juju.ext.model.wait_for_idle(apps=APP_NAMES, status="active", idle_period=30) + assert "[]" in juju.ext.model.applications[APPLICATION_APP_1].units[0].workload_status_message diff --git a/tox.ini b/tox.ini index 37436acb..e1c280b8 100644 --- a/tox.ini +++ b/tox.ini @@ -156,11 +156,10 @@ description = Run Kafka integration tests deps = psycopg2-binary pytest<8.2.0 - juju{env:LIBJUJU_VERSION_SPECIFIER:==3.6.1.0} - pytest-operator<0.43 pytest-mock websockets{env:WEBSOCKETS_VERSION_SPECIFIER:} jubilant + git+https://github.com/imanenami/jubilant_adapters.git@alpha.1 -r {[vars]reqs_path}/v0/requirements.txt commands = pytest -v --tb native --log-cli-level=INFO -s {posargs} {[vars]tests_path}/v0/integration/test_kafka_charm.py From 9d778ea86e823f06098c5087d417b8280f5c6776 Mon Sep 17 00:00:00 2001 From: Iman Enami Date: Thu, 19 Mar 2026 14:13:43 +0400 Subject: [PATCH 2/5] tidy up --- tests/v0/integration/conftest.py | 8 +++ tests/v0/integration/test_charm.py | 17 ------- tests/v0/integration/test_kafka_charm.py | 8 --- .../integration/test_kafka_connect_charm.py | 5 -- tests/v0/integration/test_opensearch_charm.py | 6 --- tests/v0/integration/test_rolling_upgrade.py | 8 --- ...t_rolling_upgrade_from_specific_version.py | 8 --- tests/v0/integration/test_s3_charm.py | 3 -- tests/v0/integration/test_secrets.py | 5 -- tests/v0/integration/test_status.py | 7 --- tox.ini | 49 +++++++++---------- 11 files changed, 30 insertions(+), 94 deletions(-) diff --git a/tests/v0/integration/conftest.py b/tests/v0/integration/conftest.py index eaebff4f..a545ddc8 100644 --- a/tests/v0/integration/conftest.py +++ b/tests/v0/integration/conftest.py @@ -54,6 +54,14 @@ def juju(request: pytest.FixtureRequest): yield juju +@pytest.fixture(scope="module", autouse=True) +def switch_model(juju: JujuFixture): + if not juju.model: + return + + juju.cli("switch", juju.model, include_model=False) + + @pytest.fixture(scope="module", autouse=True) def copy_data_interfaces_library_into_charm(juju: JujuFixture): """Copy the data_interfaces library to the different charm folder.""" diff --git a/tests/v0/integration/test_charm.py b/tests/v0/integration/test_charm.py index b52192ae..f3edc941 100644 --- a/tests/v0/integration/test_charm.py +++ b/tests/v0/integration/test_charm.py @@ -54,7 +54,6 @@ NUM_APP = 2 -@pytest.mark.abort_on_fail def test_deploy_charms( juju: JujuFixture, application_charm, @@ -122,7 +121,6 @@ def test_deploy_charms( ) -@pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_without_juju_secrets") @pytest.mark.parametrize("component", ["app", "unit"]) def test_peer_relation(component, juju: JujuFixture): @@ -195,7 +193,6 @@ def test_peer_relation(component, juju: JujuFixture): ) -@pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_with_juju_secrets") @pytest.mark.parametrize("component", ["app", "unit"]) def test_peer_relation_secrets(component, juju: JujuFixture): @@ -299,7 +296,6 @@ def test_peer_relation_secrets(component, juju: JujuFixture): action.wait() -@pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_with_juju_secrets") @pytest.mark.parametrize("component", ["app", "unit"]) def test_peer_relation_secret_revisions(component, juju: JujuFixture): @@ -345,7 +341,6 @@ def test_peer_relation_secret_revisions(component, juju: JujuFixture): assert changed_secret_revision == unchanged_secret_revision -@pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_with_juju_secrets") @pytest.mark.parametrize("component", ["app", "unit"]) def test_peer_relation_set_secret(component, juju: JujuFixture): @@ -425,7 +420,6 @@ def test_peer_relation_set_secret(component, juju: JujuFixture): action.wait() -@pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_with_juju_secrets") def test_peer_relation_non_leader_unit_secrets(juju: JujuFixture): """Testing peer relation using the DataPeer class.""" @@ -523,7 +517,6 @@ def test_peer_relation_non_leader_unit_secrets(juju: JujuFixture): action.wait() -@pytest.mark.abort_on_fail def test_peer_relation_non_leader_can_read_app_data(juju: JujuFixture): """Testing peer relation using the DataPeer class.""" # Setting and verifying two secret fields @@ -557,7 +550,6 @@ def test_peer_relation_non_leader_can_read_app_data(juju: JujuFixture): assert action.results.get("value") == "plain text" -@pytest.mark.abort_on_fail def test_other_peer_relation(juju: JujuFixture): """Testing peer relation using the DataPeer class.""" # Setting and verifying two secret fields @@ -596,7 +588,6 @@ def test_other_peer_relation(juju: JujuFixture): assert action.results.get(unit.name.replace("/", "-")) == "blablabla2" -@pytest.mark.abort_on_fail def test_other_peer_relation_scale(juju: JujuFixture): """The scaling test is the 'continuation' of the previous (test_other_peer_relation()) test. @@ -646,7 +637,6 @@ def test_other_peer_relation_scale(juju: JujuFixture): assert action.results.get(unit) is None -@pytest.mark.abort_on_fail def test_database_relation_with_charm_libraries(juju: JujuFixture): """Test basic functionality of database relation interface.""" # Relate the charms and wait for them exchanging some connection data. @@ -705,7 +695,6 @@ def test_user_with_extra_roles(juju: JujuFixture): connection.close() -@pytest.mark.abort_on_fail def test_postgresql_plugin(juju: JujuFixture): """Test that the application charm can check whether a plugin is enabled.""" # Check that the plugin is disabled. @@ -785,7 +774,6 @@ def test_secrets_usage_correct_secrets(juju: JujuFixture, application_charm): ) -@pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_without_juju_secrets") def test_database_roles_relation_with_charm_libraries(juju: JujuFixture): """Test basic functionality of database-roles relation interface.""" @@ -807,7 +795,6 @@ def test_database_roles_relation_with_charm_libraries(juju: JujuFixture): assert entity_pass is not None -@pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_with_juju_secrets") def test_database_roles_relation_with_charm_libraries_secrets(juju: JujuFixture): """Test basic functionality of database-roles relation interface.""" @@ -833,7 +820,6 @@ def test_database_roles_relation_with_charm_libraries_secrets(juju: JujuFixture) assert entity_pass is not None -@pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_with_juju_secrets") def test_database_username(juju: JujuFixture): """Test basic functionality of database-roles relation interface.""" @@ -859,7 +845,6 @@ def test_database_username(juju: JujuFixture): assert password is not None -@pytest.mark.abort_on_fail def test_database_prefix(juju: JujuFixture): """Test basic functionality of database-roles relation interface.""" # Relate the charms and wait for them exchanging some connection data. @@ -1057,7 +1042,6 @@ def test_provider_with_additional_secrets(juju: JujuFixture, database_charm): assert topsecret1 != topsecret2 -@pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_with_juju_secrets") def test_relation_secret_revisions(juju: JujuFixture): """Check that only a content change triggers the emission of a new revision.""" @@ -1274,7 +1258,6 @@ def test_provider_get_set_delete_fields_secrets(field, value, relation_field, ju assert action.results["return-code"] == 0 -@pytest.mark.abort_on_fail @pytest.mark.log_errors_allowed("Can't delete secret for relation") @pytest.mark.usefixtures("only_with_juju_secrets") def test_provider_deleted_secret_is_removed(juju: JujuFixture): diff --git a/tests/v0/integration/test_kafka_charm.py b/tests/v0/integration/test_kafka_charm.py index 636e9898..e938e9cb 100644 --- a/tests/v0/integration/test_kafka_charm.py +++ b/tests/v0/integration/test_kafka_charm.py @@ -22,9 +22,7 @@ PROV_SECRET_PREFIX = "secret-" -@pytest.mark.abort_on_fail @pytest.mark.log_errors_allowed -@pytest.mark.skip_if_deployed def test_deploy_charms(juju: JujuFixture, application_charm, kafka_charm): """Deploy both charms (application and the testing kafka app) to use in the tests.""" # Deploy both charms (1 unit for each application to test that later they correctly @@ -52,7 +50,6 @@ def test_deploy_charms(juju: JujuFixture, application_charm, kafka_charm): ) -@pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_without_juju_secrets") def test_kafka_relation_with_charm_libraries(juju: JujuFixture): """Test basic functionality of kafka relation interface.""" @@ -91,7 +88,6 @@ def test_kafka_relation_with_charm_libraries(juju: JujuFixture): assert topic == "test-topic" -@pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_without_juju_secrets") def test_kafka_relation_with_charm_libraries_split_pattern(juju: JujuFixture): """Test basic functionality of kafka relation interface.""" @@ -137,7 +133,6 @@ def test_kafka_relation_with_charm_libraries_split_pattern(juju: JujuFixture): assert topic == "test-topic-split-pattern" -@pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_with_juju_secrets") def test_kafka_relation_with_charm_libraries_secrets(juju: JujuFixture): """Test basic functionality of kafka relation interface.""" @@ -217,7 +212,6 @@ def test_kafka_bootstrap_server_changed(juju: JujuFixture): assert unit.workload_status_message == "" -@pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_with_juju_secrets") def test_kafka_mtls(juju: JujuFixture): """Tests mtls-cert is set as a secret from the requirer side and proper event triggered on provider side.""" @@ -252,7 +246,6 @@ def test_kafka_mtls(juju: JujuFixture): assert unit_cert.strip() == mtls_cert.strip() -@pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_without_juju_secrets") def test_kafka_roles_relation_with_charm_libraries(juju: JujuFixture): """Test basic functionality of kafka-roles relation interface.""" @@ -278,7 +271,6 @@ def test_kafka_roles_relation_with_charm_libraries(juju: JujuFixture): assert entity_pass == "password" -@pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_with_juju_secrets") def test_kafka_roles_relation_with_charm_libraries_secrets(juju: JujuFixture): """Test basic functionality of kafka-roles relation interface.""" diff --git a/tests/v0/integration/test_kafka_connect_charm.py b/tests/v0/integration/test_kafka_connect_charm.py index 036abd78..98a4594c 100644 --- a/tests/v0/integration/test_kafka_connect_charm.py +++ b/tests/v0/integration/test_kafka_connect_charm.py @@ -18,8 +18,6 @@ PROV_SECRET_PREFIX = "secret-" -@pytest.mark.abort_on_fail -@pytest.mark.skip_if_deployed @pytest.mark.log_errors_allowed( 'ERROR juju.worker.meterstatus error running "meter-status-changed": charm missing from disk' ) @@ -45,7 +43,6 @@ def test_deploy_charms(juju: JujuFixture, application_charm: str, kafka_connect_ assert juju.ext.model.applications[PROVIDER_APP_NAME].status == "active" -@pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_with_juju_secrets") def test_connect_client_relation_with_charm_libraries( juju: JujuFixture, request: pytest.FixtureRequest @@ -83,7 +80,6 @@ def test_connect_client_relation_with_charm_libraries( assert endpoints == "http://worker1:8083,http://worker2:8083" -@pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_with_juju_secrets") def test_kafka_connect_credentials_change(juju: JujuFixture, request: pytest.FixtureRequest): """Test Kafka Connect credentials change functionality.""" @@ -120,7 +116,6 @@ def test_kafka_connect_credentials_change(juju: JujuFixture, request: pytest.Fix assert new_password == "newpass" -@pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_with_juju_secrets") def test_kafka_connect_endpoints_change(juju: JujuFixture, request: pytest.FixtureRequest): """Test Kafka Connect endpoints change functionality.""" diff --git a/tests/v0/integration/test_opensearch_charm.py b/tests/v0/integration/test_opensearch_charm.py index 2ed81625..60d81e0b 100644 --- a/tests/v0/integration/test_opensearch_charm.py +++ b/tests/v0/integration/test_opensearch_charm.py @@ -19,7 +19,6 @@ PROV_SECRET_PREFIX = "secret-" -@pytest.mark.abort_on_fail def test_deploy_charms(juju: JujuFixture, application_charm, opensearch_charm): """Deploy both charms (application and the testing opensearch app) to use in the tests.""" # Deploy both charms (1 unit for each application to test that later they correctly @@ -42,7 +41,6 @@ def test_deploy_charms(juju: JujuFixture, application_charm, opensearch_charm): ) -@pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_without_juju_secrets") def test_opensearch_relation_with_charm_libraries(juju: JujuFixture): """Test basic functionality of opensearch relation interface.""" @@ -76,7 +74,6 @@ def test_opensearch_relation_with_charm_libraries(juju: JujuFixture): assert index == "test-index" -@pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_with_juju_secrets") def test_opensearch_relation_with_charm_libraries_secrets(juju: JujuFixture): """Test basic functionality of opensearch relation interface.""" @@ -112,7 +109,6 @@ def test_opensearch_relation_with_charm_libraries_secrets(juju: JujuFixture): assert index == "test-index" -@pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_with_juju_secrets") def test_opensearch_relation_secret_changed(juju: JujuFixture): """Test basic functionality of opensearch relation interface.""" @@ -141,7 +137,6 @@ def test_opensearch_relation_secret_changed(juju: JujuFixture): assert unit.workload_status_message == "opensearch_authentication_updated" -@pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_without_juju_secrets") def test_opensearch_roles_relation_with_charm_libraries(juju: JujuFixture): """Test basic functionality of opensearch-roles relation interface.""" @@ -169,7 +164,6 @@ def test_opensearch_roles_relation_with_charm_libraries(juju: JujuFixture): assert entity_pass == "password" -@pytest.mark.abort_on_fail @pytest.mark.usefixtures("only_with_juju_secrets") def test_opensearch_roles_relation_with_charm_libraries_secrets(juju: JujuFixture): """Test basic functionality of opensearch relation interface.""" diff --git a/tests/v0/integration/test_rolling_upgrade.py b/tests/v0/integration/test_rolling_upgrade.py index bea259e7..9d40498f 100644 --- a/tests/v0/integration/test_rolling_upgrade.py +++ b/tests/v0/integration/test_rolling_upgrade.py @@ -56,7 +56,6 @@ def upgrade_to_secrets(juju, app_name): x, stdout, y = juju.juju(*complete_command.split()) -@pytest.mark.abort_on_fail def test_deploy_charms( juju: JujuFixture, application_charm, database_charm, dp_libs_ubuntu_series ): @@ -92,7 +91,6 @@ def test_deploy_charms( # ----------------------------------------------------- -@pytest.mark.abort_on_fail @pytest.mark.parametrize("component", ["app", "unit"]) def test_peer_relation(component, juju: JujuFixture): """Relating Requires (databag only) with Provides (that could use secrets). @@ -219,7 +217,6 @@ def test_peer_relation(component, juju: JujuFixture): # ----------------------------------------------------- -@pytest.mark.abort_on_fail def test_unbalanced_versions_falling_back_to_databag_req_databag_vs_prov_secrets( juju: JujuFixture, ): @@ -273,7 +270,6 @@ def test_unbalanced_versions_falling_back_to_databag_req_databag_vs_prov_secrets assert action.results.get("value") == password -@pytest.mark.abort_on_fail def test_transparent_upgrade_requires_databag_vs_provides_secrets(juju: JujuFixture): """Upgrading Requires to use secrets (if possible). @@ -357,7 +353,6 @@ def test_transparent_upgrade_requires_databag_vs_provides_secrets(juju: JujuFixt assert not action.results.get("value") -@pytest.mark.abort_on_fail def test_transparent_upgrade_requires_databag_vs_provides_secrets_upgrade_new_secret( juju: JujuFixture, ): @@ -455,7 +450,6 @@ def test_transparent_upgrade_requires_databag_vs_provides_secrets_upgrade_new_se # ----------------------------------------------------- -@pytest.mark.abort_on_fail def test_unbalanced_versions_falling_back_to_databag_req_secrets_vs_prov_databag( juju: JujuFixture, ): @@ -506,7 +500,6 @@ def test_unbalanced_versions_falling_back_to_databag_req_secrets_vs_prov_databag assert action.results.get("value") == password -@pytest.mark.abort_on_fail def test_transparent_upgrade_keeping_databag_requires_secrets_vs_provides_databag( juju: JujuFixture, ): @@ -592,7 +585,6 @@ def test_transparent_upgrade_keeping_databag_requires_secrets_vs_provides_databa assert not action.results.get("value") -@pytest.mark.abort_on_fail def test_transparent_upgrade_keeping_databag_requires_secrets_vs_provides_databag_upgrade_new_secret( juju: JujuFixture, ): diff --git a/tests/v0/integration/test_rolling_upgrade_from_specific_version.py b/tests/v0/integration/test_rolling_upgrade_from_specific_version.py index 33a96146..56364f10 100644 --- a/tests/v0/integration/test_rolling_upgrade_from_specific_version.py +++ b/tests/v0/integration/test_rolling_upgrade_from_specific_version.py @@ -82,7 +82,6 @@ def upgrade_to_new_version(juju, app_name): @pytest.mark.usefixtures("fetch_old_versions") -@pytest.mark.abort_on_fail def test_deploy_charms( juju: JujuFixture, application_charm, database_charm, dp_libs_ubuntu_series ): @@ -118,7 +117,6 @@ def test_deploy_charms( # ----------------------------------------------------- -@pytest.mark.abort_on_fail @pytest.mark.parametrize("component", ["app", "unit"]) def test_peer_relation(component, juju: JujuFixture): """Peer relation safe across upgrades.""" @@ -216,7 +214,6 @@ def test_peer_relation(component, juju: JujuFixture): # ----------------------------------------------------- -@pytest.mark.abort_on_fail def test_unbalanced_versions_req_old_vs_prov_new( juju: JujuFixture, ): @@ -270,7 +267,6 @@ def test_unbalanced_versions_req_old_vs_prov_new( assert action.results.get("value") == password -@pytest.mark.abort_on_fail def test_rolling_upgrade_requires_old_vs_provides_new(juju: JujuFixture): """Upgrading Requires to the latest version of the libs.""" upgrade_to_new_version(juju, APPLICATION_APP_NAME) @@ -325,7 +321,6 @@ def test_rolling_upgrade_requires_old_vs_provides_new(juju: JujuFixture): assert not action.results.get("value") -@pytest.mark.abort_on_fail def test_rolling_upgrade_requires_old_vs_provides_new_upgrade_new_secret( juju: JujuFixture, ): @@ -400,7 +395,6 @@ def test_rolling_upgrade_requires_old_vs_provides_new_upgrade_new_secret( # ----------------------------------------------------- -@pytest.mark.abort_on_fail def test_unbalanced_versions_req_new_vs_prov_old( juju: JujuFixture, ): @@ -432,7 +426,6 @@ def test_unbalanced_versions_req_new_vs_prov_old( assert action.results.get("value") -@pytest.mark.abort_on_fail def test_rolling_upgrade_requires_new_vs_provides_old( juju: JujuFixture, ): @@ -489,7 +482,6 @@ def test_rolling_upgrade_requires_new_vs_provides_old( assert not action.results.get("value") -@pytest.mark.abort_on_fail def test_rolling_upgrade_requires_new_vs_provides_old_upgrade_new_secret( juju: JujuFixture, ): diff --git a/tests/v0/integration/test_s3_charm.py b/tests/v0/integration/test_s3_charm.py index 5de621d3..a42bc25d 100644 --- a/tests/v0/integration/test_s3_charm.py +++ b/tests/v0/integration/test_s3_charm.py @@ -3,7 +3,6 @@ # See LICENSE file for licensing details. import logging -import pytest from jubilant_adapters import JujuFixture, gather from .helpers import get_connection_info @@ -17,7 +16,6 @@ SECOND_S3_RELATION_NAME = "second-s3-credentials" -@pytest.mark.abort_on_fail def test_deploy_charms(juju: JujuFixture, application_s3_charm, s3_charm, dp_libs_ubuntu_series): """Deploy both charms (application and s3 provider app) to use in the tests.""" # Deploy both charms (2 units for each application to test that later they correctly @@ -37,7 +35,6 @@ def test_deploy_charms(juju: JujuFixture, application_s3_charm, s3_charm, dp_lib ) -@pytest.mark.abort_on_fail def test_s3_relation_with_charm_libraries(juju: JujuFixture): """Test basic functionality of s3-credentials relation interface.""" # Relate the charms and wait for them exchanging some connection data. diff --git a/tests/v0/integration/test_secrets.py b/tests/v0/integration/test_secrets.py index db6c0a8d..1a95cb05 100644 --- a/tests/v0/integration/test_secrets.py +++ b/tests/v0/integration/test_secrets.py @@ -13,14 +13,12 @@ APP_NAME = "secrets-test" -@pytest.mark.abort_on_fail def test_deploy_charms(juju: JujuFixture, secrets_charm): """Deploy both charm to use in the tests.""" juju.ext.model.deploy(secrets_charm, application_name=APP_NAME, num_units=2, series="jammy") juju.ext.model.wait_for_idle(apps=[APP_NAME], status="active", wait_for_exact_units=2) -@pytest.mark.abort_on_fail def test_add_get_secret_app(juju: JujuFixture): """Test basic functionality of getting/setting cached secrets.""" leader_id = get_leader_id(juju, APP_NAME) @@ -43,7 +41,6 @@ def test_add_get_secret_app(juju: JujuFixture): assert action.results.get("grandmas-apple-pie") == {"secret-ingredient": "cinnamon"} -@pytest.mark.abort_on_fail @pytest.mark.log_errors_allowed( 'cannot apply changes: creating secrets: secret with label "grandmas-apple-pie" already exists' ) @@ -62,7 +59,6 @@ def test_secret_label_unique(juju: JujuFixture): action.wait() -@pytest.mark.abort_on_fail def test_add_get_secret_unit(juju: JujuFixture): unit_id = get_non_leader_id(juju, APP_NAME) unit_name = f"{APP_NAME}/{unit_id}" @@ -86,7 +82,6 @@ def test_add_get_secret_unit(juju: JujuFixture): } -@pytest.mark.abort_on_fail def test_set_secret(juju: JujuFixture): """Test basic functionality of getting/setting cached secrets.""" leader_id = get_leader_id(juju, APP_NAME) diff --git a/tests/v0/integration/test_status.py b/tests/v0/integration/test_status.py index 34629015..76696b24 100644 --- a/tests/v0/integration/test_status.py +++ b/tests/v0/integration/test_status.py @@ -2,7 +2,6 @@ import logging from pathlib import Path -import pytest import yaml from jubilant_adapters import JujuFixture, gather @@ -22,8 +21,6 @@ RELATION_NAME = "database-with-status" -@pytest.mark.abort_on_fail -@pytest.mark.skip_if_deployed def test_deploy_charms( juju: JujuFixture, application_charm, @@ -60,7 +57,6 @@ def test_deploy_charms( juju.ext.model.wait_for_idle(apps=APP_NAMES, status="active", idle_period=30) -@pytest.mark.abort_on_fail def test_relate_application(juju: JujuFixture): # Relate the charms and wait for them exchanging some connection data. juju.ext.model.add_relation(DATABASE_APP_NAME, f"{APPLICATION_APP_1}:{RELATION_NAME}") @@ -68,7 +64,6 @@ def test_relate_application(juju: JujuFixture): juju.ext.model.wait_for_idle(apps=APP_NAMES, status="active") -@pytest.mark.abort_on_fail def test_raise_status(juju: JujuFixture): app_1_rel = next(iter(juju.ext.model.applications[APPLICATION_APP_1].relations)) app_2_rel = next(iter(juju.ext.model.applications[APPLICATION_APP_2].relations)) @@ -130,7 +125,6 @@ def test_raise_status(juju: JujuFixture): # TODO: -@pytest.mark.abort_on_fail def test_resolve_status(juju: JujuFixture): app_1_rel = next(iter(juju.ext.model.applications[APPLICATION_APP_1].relations)) app_2_rel = next(iter(juju.ext.model.applications[APPLICATION_APP_2].relations)) @@ -156,7 +150,6 @@ def test_resolve_status(juju: JujuFixture): assert "[]" in juju.ext.model.applications[APPLICATION_APP_2].units[0].workload_status_message -@pytest.mark.abort_on_fail def test_clear_statuses(juju: JujuFixture): app_1_rel = next(iter(juju.ext.model.applications[APPLICATION_APP_1].relations)) db_unit = juju.ext.model.applications[DATABASE_APP_NAME].units[0].name diff --git a/tox.ini b/tox.ini index e1c280b8..b362b5ec 100644 --- a/tox.ini +++ b/tox.ini @@ -97,14 +97,13 @@ description = Run database integration tests deps = psycopg2-binary pytest<8.2.0 - juju{env:LIBJUJU_VERSION_SPECIFIER:==3.6.1.0} - pytest-operator<0.43 pytest-mock websockets{env:WEBSOCKETS_VERSION_SPECIFIER:} jubilant + git+https://github.com/imanenami/jubilant_adapters.git@alpha.1 -r {[vars]reqs_path}/v0/requirements.txt commands = - pytest -v --tb native --log-cli-level=INFO -s {posargs} {[vars]tests_path}/v0/integration/test_charm.py + pytest -x -v --tb native --log-cli-level=INFO -s {posargs} {[vars]tests_path}/v0/integration/test_charm.py [testenv:integration-db-v1] description = Run database integration tests @@ -126,30 +125,28 @@ description = Run database integration tests deps = psycopg2-binary pytest<8.2.0 - juju{env:LIBJUJU_VERSION_SPECIFIER:==3.6.1.0} - pytest-operator<0.43 pytest-mock websockets{env:WEBSOCKETS_VERSION_SPECIFIER:} jubilant + git+https://github.com/imanenami/jubilant_adapters.git@alpha.1 -r {[vars]reqs_path}/v0/requirements.txt commands = - pytest -v --tb native --log-cli-level=INFO -s {posargs} {[vars]tests_path}/v0/integration/test_rolling_upgrade.py + pytest -x -v --tb native --log-cli-level=INFO -s {posargs} {[vars]tests_path}/v0/integration/test_rolling_upgrade.py [testenv:integration-upgrade-v0-{1,2,3,4,5,6,7}] description = Run database integration tests deps = psycopg2-binary pytest<8.2.0 - juju{env:LIBJUJU_VERSION_SPECIFIER:==3.6.1.0} - pytest-operator<0.43 pytest-mock websockets{env:WEBSOCKETS_VERSION_SPECIFIER:} jubilant + git+https://github.com/imanenami/jubilant_adapters.git@alpha.1 -r {[vars]reqs_path}/v0/requirements.txt set_env = TOX_ENV = {envname} commands = - pytest -v --tb native --log-cli-level=INFO -s {posargs} {[vars]tests_path}/v0/integration/test_rolling_upgrade_from_specific_version.py + pytest -x -v --tb native --log-cli-level=INFO -s {posargs} {[vars]tests_path}/v0/integration/test_rolling_upgrade_from_specific_version.py [testenv:integration-kafka-v0] description = Run Kafka integration tests @@ -183,28 +180,26 @@ description = Run S3 integration tests deps = psycopg2-binary pytest<8.2.0 - juju{env:LIBJUJU_VERSION_SPECIFIER:==3.6.1.0} - pytest-operator<0.43 pytest-mock websockets{env:WEBSOCKETS_VERSION_SPECIFIER:} jubilant + git+https://github.com/imanenami/jubilant_adapters.git@alpha.1 -r {[vars]reqs_path}/v0/requirements.txt commands = - pytest -v --tb native --log-cli-level=INFO -s {posargs} {[vars]tests_path}/v0/integration/test_s3_charm.py + pytest -x -v --tb native --log-cli-level=INFO -s {posargs} {[vars]tests_path}/v0/integration/test_s3_charm.py [testenv:integration-opensearch-v0] description = Run opensearch integration tests deps = psycopg2-binary pytest<8.2.0 - juju{env:LIBJUJU_VERSION_SPECIFIER:==3.6.1.0} - pytest-operator<0.43 pytest-mock websockets{env:WEBSOCKETS_VERSION_SPECIFIER:} jubilant + git+https://github.com/imanenami/jubilant_adapters.git@alpha.1 -r {[vars]reqs_path}/v0/requirements.txt commands = - pytest -v --tb native --log-cli-level=INFO -s {posargs} {[vars]tests_path}/v0/integration/test_opensearch_charm.py + pytest -x -v --tb native --log-cli-level=INFO -s {posargs} {[vars]tests_path}/v0/integration/test_opensearch_charm.py [testenv:integration-opensearch-v1] description = Run opensearch integration tests @@ -222,11 +217,12 @@ commands = [testenv:integration-etcd-v0] description = Run charmed-etcd integration tests deps = + psycopg2-binary pytest<8.2.0 - juju{env:LIBJUJU_VERSION_SPECIFIER:==3.6.1.0} - pytest-operator<0.43 pytest-mock + websockets{env:WEBSOCKETS_VERSION_SPECIFIER:} jubilant + git+https://github.com/imanenami/jubilant_adapters.git@alpha.1 charmlibs-interfaces-tls-certificates>=1.1.0 -r {[vars]reqs_path}/v0/requirements.txt allowlist_externals = @@ -245,7 +241,7 @@ commands_pre = tar -xvf /tmp/etcd_install/etcd.tar.gz -C /tmp/etcd_install sh -c 'sudo mv /tmp/etcd_install/etcd-v3.4.35-linux-*/etcdctl /usr/local/bin' commands = - pytest -v --maxfail=1 --tb native --log-cli-level=INFO -s {posargs} {[vars]tests_path}/v0/integration/test_etcd_charm.py + pytest -x -v --maxfail=1 --tb native --log-cli-level=INFO -s {posargs} {[vars]tests_path}/v0/integration/test_etcd_charm.py [testenv:integration-etcd-v1] description = Run charmed-etcd integration tests @@ -279,29 +275,29 @@ commands = [testenv:integration-secrets-v0] description = Run secrets integration tests deps = + psycopg2-binary pytest<8.2.0 - juju{env:LIBJUJU_VERSION_SPECIFIER:==3.6.1.0} - pytest-operator<0.43 pytest-mock websockets{env:WEBSOCKETS_VERSION_SPECIFIER:} jubilant + git+https://github.com/imanenami/jubilant_adapters.git@alpha.1 -r {[vars]reqs_path}/v0/requirements.txt commands = - pytest -v --tb native --log-cli-level=INFO -s {posargs} {[vars]tests_path}/v0/integration/test_secrets.py + pytest -x -v --tb native --log-cli-level=INFO -s {posargs} {[vars]tests_path}/v0/integration/test_secrets.py [testenv:integration-kafka-connect-v0] description = Run Kafka Connect integration tests deps = + psycopg2-binary pytest<8.2.0 - juju{env:LIBJUJU_VERSION_SPECIFIER:==3.6.1.0} - pytest-operator<0.43 pytest-mock websockets{env:WEBSOCKETS_VERSION_SPECIFIER:} jubilant + git+https://github.com/imanenami/jubilant_adapters.git@alpha.1 -r {[vars]reqs_path}/v0/requirements.txt commands = - pytest -v --tb native --log-cli-level=INFO -s {posargs} {[vars]tests_path}/v0/integration/test_kafka_connect_charm.py + pytest -x -v --tb native --log-cli-level=INFO -s {posargs} {[vars]tests_path}/v0/integration/test_kafka_connect_charm.py [testenv:integration-kafka-connect-v1] description = Run Kafka Connect integration tests @@ -334,14 +330,13 @@ description = Run status/error propagation integration tests deps = psycopg2-binary pytest<8.2.0 - juju{env:LIBJUJU_VERSION_SPECIFIER:==3.6.1.0} - pytest-operator<0.43 pytest-mock websockets{env:WEBSOCKETS_VERSION_SPECIFIER:} jubilant + git+https://github.com/imanenami/jubilant_adapters.git@alpha.1 -r {[vars]reqs_path}/v0/requirements.txt commands = - pytest -v --tb native --log-cli-level=INFO -s {posargs} {[vars]tests_path}/v0/integration/test_status.py + pytest -x -v --tb native --log-cli-level=INFO -s {posargs} {[vars]tests_path}/v0/integration/test_status.py [testenv:integration-status-v1] description = Run status/error propagation integration tests From 3b22963a7565ab3a6c4f0cc7314f54af65346969 Mon Sep 17 00:00:00 2001 From: Iman Enami Date: Thu, 19 Mar 2026 14:25:40 +0400 Subject: [PATCH 3/5] ci: disable build cache --- tests/v0/integration/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/v0/integration/conftest.py b/tests/v0/integration/conftest.py index a545ddc8..3d18558c 100644 --- a/tests/v0/integration/conftest.py +++ b/tests/v0/integration/conftest.py @@ -13,7 +13,7 @@ logger = logging.getLogger(__name__) -USE_CACHED_BUILD = bool(os.environ.get("CI", False)) +USE_CACHED_BUILD = False @pytest.fixture(scope="session") From ff36e69aac5a3a681cd620a973c28c697156ae01 Mon Sep 17 00:00:00 2001 From: Iman Enami Date: Thu, 19 Mar 2026 14:44:57 +0400 Subject: [PATCH 4/5] ci: unpin juju agent version --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index aab9ef71..f281f751 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -98,7 +98,7 @@ jobs: - juju-bootstrap-option: "2.9.51" juju-snap-channel: "2.9/stable" libjuju-version: "2.9.49.1" - - juju-bootstrap-option: "3.6.1" + - juju-bootstrap-option: "3.6.19" juju-snap-channel: "3.6/stable" libjuju-version: "3.6.1.0" exclude: From 5a584d864a8bf3ae0ada77f97d9886848f2a8e9f Mon Sep 17 00:00:00 2001 From: Iman Enami Date: Thu, 19 Mar 2026 15:21:30 +0400 Subject: [PATCH 5/5] update dep. ref --- tox.ini | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tox.ini b/tox.ini index b362b5ec..f314b455 100644 --- a/tox.ini +++ b/tox.ini @@ -100,7 +100,7 @@ deps = pytest-mock websockets{env:WEBSOCKETS_VERSION_SPECIFIER:} jubilant - git+https://github.com/imanenami/jubilant_adapters.git@alpha.1 + git+https://github.com/imanenami/jubilant_adapters.git@main -r {[vars]reqs_path}/v0/requirements.txt commands = pytest -x -v --tb native --log-cli-level=INFO -s {posargs} {[vars]tests_path}/v0/integration/test_charm.py @@ -128,7 +128,7 @@ deps = pytest-mock websockets{env:WEBSOCKETS_VERSION_SPECIFIER:} jubilant - git+https://github.com/imanenami/jubilant_adapters.git@alpha.1 + git+https://github.com/imanenami/jubilant_adapters.git@main -r {[vars]reqs_path}/v0/requirements.txt commands = pytest -x -v --tb native --log-cli-level=INFO -s {posargs} {[vars]tests_path}/v0/integration/test_rolling_upgrade.py @@ -141,7 +141,7 @@ deps = pytest-mock websockets{env:WEBSOCKETS_VERSION_SPECIFIER:} jubilant - git+https://github.com/imanenami/jubilant_adapters.git@alpha.1 + git+https://github.com/imanenami/jubilant_adapters.git@main -r {[vars]reqs_path}/v0/requirements.txt set_env = TOX_ENV = {envname} @@ -156,7 +156,7 @@ deps = pytest-mock websockets{env:WEBSOCKETS_VERSION_SPECIFIER:} jubilant - git+https://github.com/imanenami/jubilant_adapters.git@alpha.1 + git+https://github.com/imanenami/jubilant_adapters.git@main -r {[vars]reqs_path}/v0/requirements.txt commands = pytest -v --tb native --log-cli-level=INFO -s {posargs} {[vars]tests_path}/v0/integration/test_kafka_charm.py @@ -183,7 +183,7 @@ deps = pytest-mock websockets{env:WEBSOCKETS_VERSION_SPECIFIER:} jubilant - git+https://github.com/imanenami/jubilant_adapters.git@alpha.1 + git+https://github.com/imanenami/jubilant_adapters.git@main -r {[vars]reqs_path}/v0/requirements.txt commands = pytest -x -v --tb native --log-cli-level=INFO -s {posargs} {[vars]tests_path}/v0/integration/test_s3_charm.py @@ -196,7 +196,7 @@ deps = pytest-mock websockets{env:WEBSOCKETS_VERSION_SPECIFIER:} jubilant - git+https://github.com/imanenami/jubilant_adapters.git@alpha.1 + git+https://github.com/imanenami/jubilant_adapters.git@main -r {[vars]reqs_path}/v0/requirements.txt commands = pytest -x -v --tb native --log-cli-level=INFO -s {posargs} {[vars]tests_path}/v0/integration/test_opensearch_charm.py @@ -222,7 +222,7 @@ deps = pytest-mock websockets{env:WEBSOCKETS_VERSION_SPECIFIER:} jubilant - git+https://github.com/imanenami/jubilant_adapters.git@alpha.1 + git+https://github.com/imanenami/jubilant_adapters.git@main charmlibs-interfaces-tls-certificates>=1.1.0 -r {[vars]reqs_path}/v0/requirements.txt allowlist_externals = @@ -280,7 +280,7 @@ deps = pytest-mock websockets{env:WEBSOCKETS_VERSION_SPECIFIER:} jubilant - git+https://github.com/imanenami/jubilant_adapters.git@alpha.1 + git+https://github.com/imanenami/jubilant_adapters.git@main -r {[vars]reqs_path}/v0/requirements.txt commands = pytest -x -v --tb native --log-cli-level=INFO -s {posargs} {[vars]tests_path}/v0/integration/test_secrets.py @@ -294,7 +294,7 @@ deps = pytest-mock websockets{env:WEBSOCKETS_VERSION_SPECIFIER:} jubilant - git+https://github.com/imanenami/jubilant_adapters.git@alpha.1 + git+https://github.com/imanenami/jubilant_adapters.git@main -r {[vars]reqs_path}/v0/requirements.txt commands = pytest -x -v --tb native --log-cli-level=INFO -s {posargs} {[vars]tests_path}/v0/integration/test_kafka_connect_charm.py @@ -333,7 +333,7 @@ deps = pytest-mock websockets{env:WEBSOCKETS_VERSION_SPECIFIER:} jubilant - git+https://github.com/imanenami/jubilant_adapters.git@alpha.1 + git+https://github.com/imanenami/jubilant_adapters.git@main -r {[vars]reqs_path}/v0/requirements.txt commands = pytest -x -v --tb native --log-cli-level=INFO -s {posargs} {[vars]tests_path}/v0/integration/test_status.py