Skip to content

Commit

Permalink
[DPE-5662] increase async replication tests coverage (#748)
Browse files Browse the repository at this point in the history
Co-authored-by: Iman Enami <[email protected]>
  • Loading branch information
imanenami and Iman Enami authored Oct 30, 2024
1 parent de7d805 commit dfab630
Showing 1 changed file with 216 additions and 2 deletions.
218 changes: 216 additions & 2 deletions tests/unit/test_async_replication.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
# Copyright 2024 Canonical Ltd.
# See LICENSE file for licensing details.

from unittest.mock import patch
from unittest.mock import PropertyMock, patch

import pytest
from ops.testing import Harness

from charm import PostgresqlOperatorCharm
from constants import PEER
from constants import APP_SCOPE, PEER
from relations.async_replication import (
REPLICATION_CONSUMER_RELATION,
REPLICATION_OFFER_RELATION,
SECRET_LABEL,
)

RELATION_NAMES = ["replication-offer", "replication"]

Expand All @@ -25,6 +30,20 @@ def harness():
harness.cleanup()


@pytest.fixture(autouse=True)
def standby():
with patch("charm.KubernetesServicePatch", lambda x, y: None):
harness = Harness(PostgresqlOperatorCharm)
harness.set_model_name("standby")

# Set up the initial relation and hooks.
harness.set_leader(True)
harness.begin()

yield harness
harness.cleanup()


@pytest.mark.parametrize("relation_name", RELATION_NAMES)
@pytest.mark.parametrize("is_leader", [True, False])
def test_on_async_relation_broken(harness, is_leader, relation_name):
Expand Down Expand Up @@ -162,3 +181,198 @@ def test_on_async_relation_departed(harness, relation_name):
harness.add_relation_unit(rel_id, other_unit)
harness.remove_relation_unit(rel_id, other_unit)
assert harness.get_relation_data(peer_rel_id, harness.charm.unit.name) == {}


@pytest.mark.parametrize("wait_for_standby", [True, False])
def test_on_async_relation_changed(harness, wait_for_standby):
harness.add_relation(
PEER,
harness.charm.app.name,
unit_data={"unit-address": "10.1.1.10"},
app_data={"promoted-cluster-counter": "1"},
)
harness.set_can_connect("postgresql", True)
harness.handle_exec("postgresql", [], result=0)
harness.add_relation(REPLICATION_OFFER_RELATION, harness.charm.app.name)
assert harness.charm.async_replication.get_primary_cluster().name == harness.charm.app.name

with (
patch("ops.model.Container.stop") as _stop,
patch("ops.model.Container.start") as _start,
patch("ops.model.Container.pebble") as _pebble,
patch("lightkube.Client.__init__", return_value=None),
patch("lightkube.Client.delete") as _lightkube_delete,
patch(
"charm.Patroni.member_started", new_callable=PropertyMock
) as _patroni_member_started,
patch("charm.PostgresqlOperatorCharm._create_pgdata") as _create_pgdata,
patch("charm.PostgresqlOperatorCharm.update_config") as _update_config,
patch("charm.PostgresqlOperatorCharm._set_active_status") as _set_active_status,
patch(
"relations.async_replication.PostgreSQLAsyncReplication.get_system_identifier",
return_value=("12345", None),
),
patch(
"relations.async_replication.PostgreSQLAsyncReplication._wait_for_standby_leader",
return_value=wait_for_standby,
),
):
_pebble.get_services.return_value = ["postgresql"]
_patroni_member_started.return_value = True
harness.add_relation(
REPLICATION_CONSUMER_RELATION,
"standby",
unit_data={"unit-address": "10.2.2.10"},
app_data={"promoted-cluster-counter": "2"},
)
_stop.assert_called_once()
if not wait_for_standby:
_start.assert_called()
_create_pgdata.assert_called_once()

assert harness.charm.async_replication.get_primary_cluster().name == "standby"


@pytest.mark.parametrize("relation_name", [REPLICATION_OFFER_RELATION])
def test_create_replication(harness, relation_name):
"""Test create-replication action."""
with (
patch(
"charm.PostgresqlOperatorCharm.is_cluster_initialised",
new_callable=PropertyMock,
return_value=False,
) as _is_cluster_initialised,
patch(
"relations.async_replication.PostgreSQLAsyncReplication._get_unit_ip",
return_value="10.1.1.10",
),
patch(
"relations.async_replication.PostgreSQLAsyncReplication.get_system_identifier",
return_value=("12345", None),
),
patch(
"relations.async_replication.PostgreSQLAsyncReplication._primary_cluster_endpoint",
new_callable=PropertyMock,
return_value="10.1.1.10",
),
patch("charm.Patroni.get_standby_leader", return_value=None),
patch("charm.PostgresqlOperatorCharm.update_config") as _update_config,
patch("charm.PostgresqlOperatorCharm._set_active_status") as _set_active_status,
):
harness.charm.model.app.add_secret(
{"password": "password"}, label="database-peers.postgresql-k8s.app"
)
with harness.hooks_disabled():
harness.add_relation(PEER, harness.charm.app.name)
rel_id = harness.add_relation(
relation_name, harness.charm.app.name, unit_data={"unit-address": "10.1.1.10"}
)
_is_cluster_initialised.return_value = True
harness.run_action("create-replication")

_update_config.assert_called_once()
_set_active_status.assert_called()
assert harness.get_relation_data(rel_id, harness.charm.app.name).get("name") == "default"


@pytest.mark.parametrize("relation_name", [REPLICATION_CONSUMER_RELATION])
def test_promote_to_primary(harness, relation_name):
"""Test promote-to-primary action."""
with (
patch(
"charm.PostgresqlOperatorCharm.is_cluster_initialised",
new_callable=PropertyMock,
return_value=True,
),
patch(
"relations.async_replication.PostgreSQLAsyncReplication.get_system_identifier",
return_value=("12345", None),
),
patch("charm.PostgresqlOperatorCharm.update_config") as _update_config,
patch("charm.PostgresqlOperatorCharm._set_active_status") as _set_active_status,
patch(
"relations.async_replication.PostgreSQLAsyncReplication._primary_cluster_endpoint",
new_callable=PropertyMock,
return_value="10.1.1.10",
),
patch("charm.Patroni.get_primary"),
patch("charm.Patroni.get_standby_leader", return_value=None),
):
with harness.hooks_disabled():
harness.add_relation(
PEER, harness.charm.app.name, unit_data={"unit-address": "10.1.1.10"}
)
rel_id = harness.add_relation(
relation_name, "standby", app_data={"promoted-cluster-counter": "1"}
)
harness.update_relation_data(rel_id, "standby/0", {"unit-address": "10.2.2.10"})

harness.run_action("promote-to-primary")

assert (
harness.get_relation_data(rel_id, harness.charm.app.name).get(
"promoted-cluster-counter"
)
== "2"
)


@pytest.mark.parametrize("relation_name", RELATION_NAMES)
def test_on_secret_changed(harness, relation_name):
import json

secret_id = harness.add_model_secret("primary", {"operator-password": "old"})
peer_rel_id = harness.add_relation(PEER, "primary")
rel_id = harness.add_relation(
relation_name, harness.charm.app.name, unit_data={"unit-address": "10.1.1.10"}
)

secret_label = (
f"{PEER}.{harness.charm.app.name}.app"
if relation_name == REPLICATION_OFFER_RELATION
else SECRET_LABEL
)
harness.grant_secret(secret_id, harness.charm.app.name)
harness.charm.model.get_secret(id=secret_id, label=secret_label)
primary_cluster_data = {
"endpoint": "10.1.1.10",
"secret-id": "",
"name": "default",
}

with harness.hooks_disabled():
harness.update_relation_data(
peer_rel_id, harness.charm.unit.name, {"unit-promoted-cluster-counter": "1"}
)
harness.update_relation_data(
peer_rel_id, harness.charm.app.name, {"promoted-cluster-counter": "1"}
)
harness.update_relation_data(
rel_id,
harness.charm.app.name,
{
"promoted-cluster-counter": "1",
"primary-cluster-data": json.dumps(primary_cluster_data),
},
)

with (
patch(
"charm.PostgresqlOperatorCharm._on_peer_relation_changed", return_value=None
) as _charm_on_peer_relation_changed,
patch(
"relations.async_replication.PostgreSQLAsyncReplication._primary_cluster_endpoint",
new_callable=PropertyMock,
return_value="10.1.1.10",
),
):
harness.set_secret_content(secret_id, {"operator-password": "new"})

_charm_on_peer_relation_changed.assert_called_once()
if relation_name == REPLICATION_CONSUMER_RELATION:
assert harness.charm.get_secret(APP_SCOPE, "operator-password") == "new"
else:
updated_cluster_data = json.loads(
harness.get_relation_data(rel_id, harness.charm.app.name).get("primary-cluster-data")
)
assert primary_cluster_data.get("secret-id") != updated_cluster_data.get("secret-id")

0 comments on commit dfab630

Please sign in to comment.