Skip to content

Commit bfcd15e

Browse files
BON4Zvirovyi
andauthored
[DPE-7600] Add unit tests for basic skeleton using scenario (#4)
* Repo move * Update LICENSE Co-authored-by: Vladyslav Tarasenko <[email protected]> * Update LICENSE Co-authored-by: Vladyslav Tarasenko <[email protected]> * Update README.md Co-authored-by: Vladyslav Tarasenko <[email protected]> * Update src/common/cassandra_client.py Co-authored-by: Vladyslav Tarasenko <[email protected]> * Update src/common/exceptions.py Co-authored-by: Vladyslav Tarasenko <[email protected]> * Update src/common/literals.py Co-authored-by: Vladyslav Tarasenko <[email protected]> * Update src/common/management_client.py Co-authored-by: Vladyslav Tarasenko <[email protected]> * Update src/common/models.py Co-authored-by: Vladyslav Tarasenko <[email protected]> * Update src/common/workload.py Co-authored-by: Vladyslav Tarasenko <[email protected]> * Update src/core/charm.py Co-authored-by: Vladyslav Tarasenko <[email protected]> * Update src/core/cluster.py Co-authored-by: Vladyslav Tarasenko <[email protected]> * Update src/workload.py Co-authored-by: Vladyslav Tarasenko <[email protected]> * Update src/core/context.py Co-authored-by: Vladyslav Tarasenko <[email protected]> * Update src/events/cassandra.py Co-authored-by: Vladyslav Tarasenko <[email protected]> * Update src/managers/cluster.py Co-authored-by: Vladyslav Tarasenko <[email protected]> * Update src/managers/config.py Co-authored-by: Vladyslav Tarasenko <[email protected]> * Refactor #1 * Refactor #2 * Format. * Fix of #1. * Refactor. * Typing refactor. * Add github ci pipeline * Refactor. * Refactor. * [DPE-7600] Add unit tests for basic skeleton using `scenario` * Add unit tests * Add unit tests * Add unit tests * Add unit tests * Add unit tests * Fix Poetry * Fix tox lint * Fix poetry.lock * Add alive patch * Fix Tests * Fix Tests * Fix Tests * Fix Tests * Fix Tests * Fix Tests * Fix Tests * Fix lint --------- Co-authored-by: Vladyslav Tarasenko <[email protected]>
1 parent c05a20a commit bfcd15e

File tree

6 files changed

+400
-27
lines changed

6 files changed

+400
-27
lines changed

poetry.lock

Lines changed: 74 additions & 23 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ optional = true
3737

3838
[tool.poetry.group.unit.dependencies]
3939
coverage = {extras = ["toml"], version = "^7.8.0"}
40-
pytest = "^8.3.5"
40+
pytest = "^8.4.1"
41+
pytest-mock = "^3.14.1"
42+
ops = {version = "^2.22.0", extras = ["testing"]}
4143

4244
[tool.poetry.group.integration]
4345
optional = true

tests/unit/test_charm.py

Lines changed: 169 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,173 @@
33
#
44
# Learn more about testing at: https://juju.is/docs/sdk/testing
55

6+
from unittest.mock import PropertyMock, patch
67

7-
def test_start():
8-
pass
8+
import ops
9+
from ops import testing
10+
11+
from charm import CassandraCharm
12+
from core.state import PEER_RELATION
13+
14+
15+
def test_start_maintenance_status_when_starting():
16+
"""Charm enters MaintenanceStatus when Cassandra is not yet healthy."""
17+
ctx = testing.Context(CassandraCharm)
18+
relation = testing.PeerRelation(id=1, endpoint=PEER_RELATION)
19+
state_in = testing.State(leader=True, relations={relation})
20+
21+
with (
22+
patch("managers.config.ConfigManager.render_env"),
23+
patch("workload.CassandraWorkload.restart"),
24+
patch("workload.CassandraWorkload.alive"),
25+
patch(
26+
"managers.cluster.ClusterManager.is_healthy",
27+
new_callable=PropertyMock(return_value=False),
28+
),
29+
):
30+
state_out = ctx.run(ctx.on.start(), state_in)
31+
assert state_out.unit_status == ops.MaintenanceStatus("waiting for Cassandra to start")
32+
assert state_out.get_relation(1).local_unit_data.get("workload_state") == "active"
33+
assert state_out.get_relation(1).local_app_data.get("cluster_state") == "active"
34+
35+
36+
def test_start_sets_active_status_when_healthy():
37+
"""Charm enters ActiveStatus when Cassandra is healthy after start."""
38+
ctx = testing.Context(CassandraCharm)
39+
relation = testing.PeerRelation(id=1, endpoint=PEER_RELATION)
40+
state_in = testing.State(leader=True, relations={relation})
41+
42+
with (
43+
patch("managers.config.ConfigManager.render_env"),
44+
patch("workload.CassandraWorkload.restart"),
45+
patch("workload.CassandraWorkload.alive"),
46+
patch(
47+
"managers.cluster.ClusterManager.is_healthy",
48+
new_callable=PropertyMock(return_value=True),
49+
),
50+
):
51+
state_out = ctx.run(ctx.on.start(), state_in)
52+
assert state_out.unit_status == ops.ActiveStatus()
53+
assert state_out.get_relation(1).local_unit_data.get("workload_state") == "active"
54+
assert state_out.get_relation(1).local_app_data.get("cluster_state") == "active"
55+
56+
57+
def test_start_only_after_leader_active():
58+
"""Ensure Cassandra node starts only after leader is active and cluster_state is 'active'."""
59+
ctx = testing.Context(CassandraCharm)
60+
relation = testing.PeerRelation(id=1, endpoint=PEER_RELATION)
61+
state_in = testing.State(leader=False, relations={relation})
62+
63+
with (
64+
patch("managers.config.ConfigManager.render_env"),
65+
patch("workload.CassandraWorkload.restart") as restart,
66+
patch("workload.CassandraWorkload.alive"),
67+
patch(
68+
"managers.cluster.ClusterManager.is_healthy",
69+
new_callable=PropertyMock(return_value=False),
70+
),
71+
):
72+
state_out = ctx.run(ctx.on.start(), state_in)
73+
assert state_out.unit_status == ops.MaintenanceStatus("installing Cassandra")
74+
restart.assert_not_called()
75+
76+
relation = testing.PeerRelation(
77+
id=1, endpoint=PEER_RELATION, local_app_data={"cluster_state": "active"}
78+
)
79+
state_in = testing.State(leader=False, relations={relation})
80+
81+
with (
82+
patch("managers.config.ConfigManager.render_env"),
83+
patch("workload.CassandraWorkload.restart") as restart,
84+
patch("workload.CassandraWorkload.alive"),
85+
patch(
86+
"managers.cluster.ClusterManager.is_healthy",
87+
new_callable=PropertyMock(return_value=False),
88+
),
89+
):
90+
state_out = ctx.run(ctx.on.start(), state_in)
91+
assert state_out.unit_status == ops.MaintenanceStatus("waiting for Cassandra to start")
92+
assert state_out.get_relation(1).local_unit_data.get("workload_state") == "active"
93+
restart.assert_called()
94+
95+
96+
def test_config_changed_invalid_config():
97+
"""Ensure charm enters BlockedStatus if config is invalid during config_changed event."""
98+
ctx = testing.Context(CassandraCharm)
99+
relation = testing.PeerRelation(id=1, endpoint=PEER_RELATION)
100+
state_in = testing.State(leader=True, relations={relation}, config={"profile": "invalid"})
101+
with (
102+
patch("managers.config.ConfigManager.render_env"),
103+
patch("workload.CassandraWorkload.restart"),
104+
patch("workload.CassandraWorkload.alive"),
105+
patch(
106+
"managers.cluster.ClusterManager.is_healthy",
107+
new_callable=PropertyMock(return_value=False),
108+
),
109+
):
110+
state_out = ctx.run(ctx.on.config_changed(), state_in)
111+
assert state_out.unit_status == ops.BlockedStatus("invalid config")
112+
113+
114+
def test_config_changed_no_restart():
115+
"""Ensure node is not restarted if workload_state is 'starting' during config_changed event."""
116+
ctx = testing.Context(CassandraCharm)
117+
relation = testing.PeerRelation(
118+
id=1, endpoint=PEER_RELATION, local_unit_data={"workload_state": "starting"}
119+
)
120+
state_in = testing.State(leader=True, relations={relation})
121+
with (
122+
patch("managers.config.ConfigManager.render_env"),
123+
patch("workload.CassandraWorkload.restart") as restart,
124+
patch("workload.CassandraWorkload.alive"),
125+
patch(
126+
"managers.cluster.ClusterManager.is_healthy",
127+
new_callable=PropertyMock(return_value=False),
128+
),
129+
):
130+
state_out = ctx.run(ctx.on.config_changed(), state_in)
131+
assert state_out.unit_status == ops.MaintenanceStatus("waiting for Cassandra to start")
132+
restart.assert_not_called()
133+
134+
135+
def test_collect_unit_status_active_but_not_healthy():
136+
"""Ensure unit is MaintenanceStatus if workload_state is 'active' but node is not healthy."""
137+
ctx = testing.Context(CassandraCharm)
138+
relation = testing.PeerRelation(
139+
id=1,
140+
endpoint=PEER_RELATION,
141+
local_unit_data={"workload_state": "active"},
142+
)
143+
state_in = testing.State(leader=True, relations={relation})
144+
with (
145+
patch("managers.config.ConfigManager.render_env"),
146+
patch("workload.CassandraWorkload.restart"),
147+
patch("workload.CassandraWorkload.alive"),
148+
patch(
149+
"managers.cluster.ClusterManager.is_healthy",
150+
new_callable=PropertyMock(return_value=False),
151+
),
152+
):
153+
state_out = ctx.run(ctx.on.collect_unit_status(), state_in)
154+
assert state_out.unit_status == ops.MaintenanceStatus("waiting for Cassandra to start")
155+
156+
157+
def test_start_not_leader_and_cluster_state_not_active():
158+
"""Ensure charm does not start Cassandra if not leader and cluster_state is not 'active'."""
159+
ctx = testing.Context(CassandraCharm)
160+
relation = testing.PeerRelation(
161+
id=1, endpoint=PEER_RELATION, local_app_data={"cluster_state": "pending"}
162+
)
163+
state_in = testing.State(leader=False, relations={relation})
164+
with (
165+
patch("managers.config.ConfigManager.render_env"),
166+
patch("workload.CassandraWorkload.restart") as restart,
167+
patch("workload.CassandraWorkload.alive"),
168+
patch(
169+
"managers.cluster.ClusterManager.is_healthy",
170+
new_callable=PropertyMock(return_value=False),
171+
),
172+
):
173+
state_out = ctx.run(ctx.on.start(), state_in)
174+
assert state_out.unit_status == ops.MaintenanceStatus("installing Cassandra")
175+
restart.assert_not_called()

0 commit comments

Comments
 (0)