Skip to content

Commit 69aa052

Browse files
test(storage-constraints): update tests to reflect new logic
Unit tests have been updated to reflect the fact that parsing of storage constraints no longer happens before calling Model._deploy. Due to the difficulty in mocking the _deploy internals, the bundle test of parsing many storage variations has been moved to test_parse_storage_constraints in test_constraints. Two integration tests have been added to test deployment with parsed and unparsed storage arguments respectively.
1 parent 578f1d9 commit 69aa052

3 files changed

Lines changed: 93 additions & 112 deletions

File tree

tests/integration/test_model.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,42 @@ async def test_model_name():
4545
await model.disconnect()
4646

4747

48+
@base.bootstrapped
49+
async def test_deploy_with_storage_unparsed():
50+
async with base.CleanModel() as model:
51+
await model.deploy(
52+
"postgresql",
53+
storage={"pgdata": "1G"},
54+
)
55+
await model.wait_for_idle(status="active")
56+
storages = await model.list_storage()
57+
assert len(storages) == 1
58+
[storage] = storages
59+
# size information isn't exposed, so can't assert on that
60+
assert storage["owner-tag"].startswith(tag.unit("postgresql"))
61+
assert storage["storage-tag"].startswith(tag.storage("pgdata"))
62+
assert storage["life"] == "alive"
63+
assert storage["status"].status == "attached"
64+
65+
66+
@base.bootstrapped
67+
async def test_deploy_with_storage_preparsed():
68+
async with base.CleanModel() as model:
69+
await model.deploy(
70+
"postgresql",
71+
storage={"pgdata": {"size": 1024, "count": 1}},
72+
)
73+
await model.wait_for_idle(status="active")
74+
storages = await model.list_storage()
75+
assert len(storages) == 1
76+
[storage] = storages
77+
# size information isn't exposed, so can't assert on that
78+
assert storage["owner-tag"].startswith(tag.unit("postgresql"))
79+
assert storage["storage-tag"].startswith(tag.storage("pgdata"))
80+
assert storage["life"] == "alive"
81+
assert storage["status"].status == "attached"
82+
83+
4884
@base.bootstrapped
4985
@pytest.mark.bundle
5086
async def test_deploy_local_bundle_dir():

tests/unit/test_bundle.py

Lines changed: 5 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@
33

44
import unittest
55
from pathlib import Path
6-
from typing import Dict, List, Tuple
76
from unittest import mock
87
from unittest.mock import ANY, Mock, patch
98

109
import yaml
1110
from toposort import CircularDependencyError
1211

13-
from juju import charmhub, constraints
12+
from juju import charmhub
1413
from juju.bundle import (
1514
AddApplicationChange,
1615
AddCharmChange,
@@ -189,113 +188,13 @@ async def test_run_with_charmhub_charm(self):
189188
constraints="constraints",
190189
endpoint_bindings="endpoint_bindings",
191190
resources=["resource1"],
192-
storage={
193-
storage_label: constraints.parse_storage_constraint(storage_constraint)
194-
},
191+
storage={storage_label: storage_constraint},
195192
devices="devices",
196193
channel="channel",
197194
charm_origin=ANY,
198195
num_units="num_units",
199196
)
200197

201-
async def test_run_with_storage_variations(self):
202-
"""Test that various valid storage constraints are parsed as expected
203-
before model._deploy is called.
204-
205-
Uses the mock call logic from test_run_with_charmhub_charm,
206-
which will run before this test.
207-
"""
208-
storage_arg_pairs: List[
209-
Tuple[Dict[str, str], Dict[str, constraints.StorageConstraintDict]]
210-
] = [
211-
# (storage_arg_for_change, storage_arg_for_deploy)
212-
(
213-
{"some-label": "ebs,100G,1"},
214-
{"some-label": {"count": 1, "pool": "ebs", "size": 102400}},
215-
),
216-
(
217-
{"some-label": "ebs,2.1G,3"},
218-
{"some-label": {"count": 3, "pool": "ebs", "size": 2150}},
219-
),
220-
(
221-
{"some-label": "ebs,100G"},
222-
{"some-label": {"count": 1, "pool": "ebs", "size": 102400}},
223-
),
224-
({"some-label": "ebs,2"}, {"some-label": {"count": 2, "pool": "ebs"}}),
225-
({"some-label": "200G,7"}, {"some-label": {"count": 7, "size": 204800}}),
226-
({"some-label": "ebs"}, {"some-label": {"count": 1, "pool": "ebs"}}),
227-
(
228-
{"some-label": "10YB"},
229-
{"some-label": {"count": 1, "size": 11529215046068469760}},
230-
),
231-
({"some-label": "1"}, {"some-label": {"count": 1}}),
232-
({"some-label": "-1"}, {"some-label": {"count": 1}}),
233-
({"some-label": ""}, {"some-label": {"count": 1}}),
234-
(
235-
{
236-
"some-label": "2.1G,3",
237-
"data": "1MiB,70",
238-
"logs": "ebs,-1",
239-
},
240-
{
241-
"some-label": {"count": 3, "size": 2150},
242-
"data": {"count": 70, "size": 1},
243-
"logs": {"count": 1, "pool": "ebs"},
244-
},
245-
),
246-
]
247-
for storage_arg_for_change, storage_arg_for_deploy in storage_arg_pairs:
248-
change = AddApplicationChange(
249-
1,
250-
[],
251-
params={
252-
"charm": "charm",
253-
"series": "series",
254-
"application": "application",
255-
"options": "options",
256-
"constraints": "constraints",
257-
"storage": storage_arg_for_change,
258-
"endpoint-bindings": "endpoint_bindings",
259-
"resources": "resources",
260-
"devices": "devices",
261-
"num-units": "num_units",
262-
"channel": "channel",
263-
},
264-
)
265-
# mock model
266-
model = Mock()
267-
model._deploy = mock.AsyncMock(return_value=None)
268-
model._add_charmhub_resources = mock.AsyncMock(return_value=["resource1"])
269-
model.applications = {}
270-
# mock context
271-
context = Mock()
272-
context.resolve.return_value = "ch:charm1"
273-
context.origins = {"ch:charm1": Mock()}
274-
context.trusted = False
275-
context.model = model
276-
# mock info_func
277-
info_func = mock.AsyncMock(return_value=["12345", "name"])
278-
# patch and call
279-
with patch.object(charmhub.CharmHub, "get_charm_id", info_func):
280-
result = await change.run(context)
281-
assert result == "application"
282-
# asserts
283-
model._deploy.assert_called_once()
284-
model._deploy.assert_called_with(
285-
charm_url="ch:charm1",
286-
application="application",
287-
series="series",
288-
config="options",
289-
constraints="constraints",
290-
endpoint_bindings="endpoint_bindings",
291-
resources=["resource1"],
292-
storage=storage_arg_for_deploy, # we're testing this
293-
devices="devices",
294-
channel="channel",
295-
charm_origin=ANY,
296-
num_units="num_units",
297-
)
298-
299198
async def test_run_with_charmhub_charm_no_channel(self):
300199
"""Test to verify if when the given channel is None, the channel
301200
defaults to "local/stable", which is the default channel value for the
@@ -347,9 +246,7 @@ async def test_run_with_charmhub_charm_no_channel(self):
347246
constraints="constraints",
348247
endpoint_bindings="endpoint_bindings",
349248
resources=["resource1"],
350-
storage={
351-
storage_label: constraints.parse_storage_constraint(storage_constraint)
352-
},
249+
storage={storage_label: storage_constraint},
353250
devices="devices",
354251
channel="latest/stable",
355252
charm_origin=ANY,
@@ -397,9 +294,7 @@ async def test_run_local(self):
397294
constraints="constraints",
398295
endpoint_bindings="endpoint_bindings",
399296
resources={},
400-
storage={
401-
storage_label: constraints.parse_storage_constraint(storage_constraint)
402-
},
297+
storage={storage_label: storage_constraint},
403298
devices="devices",
404299
num_units="num_units",
405300
channel="",
@@ -452,9 +347,7 @@ async def test_run_no_series(self):
452347
constraints="constraints",
453348
endpoint_bindings="endpoint_bindings",
454349
resources=["resource1"],
455-
storage={
456-
storage_label: constraints.parse_storage_constraint(storage_constraint)
457-
},
350+
storage={storage_label: storage_constraint},
458351
devices="devices",
459352
channel="channel",
460353
charm_origin=ANY,

tests/unit/test_constraints.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#
55
# Test our constraints parser
66
#
7+
from __future__ import annotations
78

89
import unittest
910

@@ -79,6 +80,57 @@ def test_parse_storage_constraint(self):
7980
self.assertEqual(_("3,0.5T"), {"count": 3, "size": 512 * 1024**1})
8081
self.assertEqual(_("0.5T,3"), {"count": 3, "size": 512 * 1024**1})
8182

83+
def test_parse_storage_constraints(self):
84+
"""Test that various valid storage constraints are parsed as expected."""
85+
storage_arg_pairs: list[
86+
tuple[dict[str, str], dict[str, constraints.StorageConstraintDict]]
87+
] = [
88+
# (storage_arg, parsed_storage_arg)
89+
(
90+
{"some-label": "ebs,100G,1"},
91+
{"some-label": {"count": 1, "pool": "ebs", "size": 102400}},
92+
),
93+
(
94+
{"some-label": "ebs,2.1G,3"},
95+
{"some-label": {"count": 3, "pool": "ebs", "size": 2150}},
96+
),
97+
(
98+
{"some-label": "ebs,100G"},
99+
{"some-label": {"count": 1, "pool": "ebs", "size": 102400}},
100+
),
101+
({"some-label": "ebs,2"}, {"some-label": {"count": 2, "pool": "ebs"}}),
102+
({"some-label": "200G,7"}, {"some-label": {"count": 7, "size": 204800}}),
103+
({"some-label": "ebs"}, {"some-label": {"count": 1, "pool": "ebs"}}),
104+
(
105+
{"some-label": "10YB"},
106+
{"some-label": {"count": 1, "size": 11529215046068469760}},
107+
),
108+
({"some-label": "1"}, {"some-label": {"count": 1}}),
109+
({"some-label": "-1"}, {"some-label": {"count": 1}}),
110+
({"some-label": ""}, {"some-label": {"count": 1}}),
111+
(
112+
{
113+
"some-label": "2.1G,3",
114+
"data": "1MiB,70",
115+
"logs": "ebs,-1",
116+
},
117+
{
118+
"some-label": {"count": 3, "size": 2150},
119+
"data": {"count": 70, "size": 1},
120+
"logs": {"count": 1, "pool": "ebs"},
121+
},
122+
),
123+
]
124+
for storage_arg, parsed_storage_constraint in storage_arg_pairs:
125+
self.assertEqual(
126+
constraints.parse_storage_constraints(storage_arg),
127+
parsed_storage_constraint,
128+
)
129+
self.assertEqual(
130+
constraints.parse_storage_constraints(parsed_storage_constraint),
131+
parsed_storage_constraint,
132+
)
133+
82134
def test_parse_device_constraint(self):
83135
_ = constraints.parse_device_constraint
84136

0 commit comments

Comments
 (0)