|
1 | 1 | import unittest |
| 2 | +from unittest.mock import patch, PropertyMock |
2 | 3 |
|
3 | 4 | import mock |
4 | 5 |
|
| 6 | +import asyncio |
5 | 7 | import asynctest |
| 8 | +import datetime |
6 | 9 |
|
7 | 10 | from juju.client.jujudata import FileJujuData |
8 | 11 | from juju.model import Model |
@@ -262,3 +265,50 @@ async def test_with_posargs(self, mock_connect, mock_connect_model, _): |
262 | 265 | macaroons='macaroons', |
263 | 266 | loop='loop', |
264 | 267 | max_frame_size='max_frame_size') |
| 268 | + |
| 269 | + |
| 270 | +# Patch timedelta to immediately force a timeout to avoid introducing an unnecessary delay in the test failing. |
| 271 | +# It should be safe to always set it up to lead to a timeout. |
| 272 | +@patch('juju.model.timedelta', new=lambda *a, **kw: datetime.timedelta(0)) |
| 273 | +class TestModelWaitForIdle(asynctest.TestCase): |
| 274 | + async def test_no_args(self): |
| 275 | + m = Model() |
| 276 | + with self.assertWarns(DeprecationWarning): |
| 277 | + # no apps so should return right away |
| 278 | + await m.wait_for_idle(wait_for_active=True) |
| 279 | + |
| 280 | + async def test_timeout(self): |
| 281 | + m = Model() |
| 282 | + with self.assertRaises(asyncio.TimeoutError) as cm: |
| 283 | + # no apps so should timeout after timeout period |
| 284 | + await m.wait_for_idle(apps=["nonexisting_app"]) |
| 285 | + self.assertEqual(str(cm.exception), "Timed out waiting for model:\nnonexisting_app (missing)") |
| 286 | + |
| 287 | + async def test_wait_for_active_status(self): |
| 288 | + # create a custom apps mock |
| 289 | + from types import SimpleNamespace |
| 290 | + apps = {"dummy_app": SimpleNamespace( |
| 291 | + status="active", |
| 292 | + units=[SimpleNamespace( |
| 293 | + name="mockunit/0", |
| 294 | + workload_status="active", |
| 295 | + workload_status_message="workload_status_message", |
| 296 | + machine=None, |
| 297 | + agent_status="idle", |
| 298 | + )], |
| 299 | + )} |
| 300 | + |
| 301 | + with patch.object(Model, 'applications', new_callable=PropertyMock) as mock_apps: |
| 302 | + mock_apps.return_value = apps |
| 303 | + m = Model() |
| 304 | + |
| 305 | + # pass "active" via `status` (str) |
| 306 | + await m.wait_for_idle(apps=["dummy_app"], status="active") |
| 307 | + |
| 308 | + # pass "active" via `wait_for_active` (bool; deprecated) |
| 309 | + await m.wait_for_idle(apps=["dummy_app"], wait_for_active=True) |
| 310 | + |
| 311 | + # use both `status` and `wait_for_active` - `wait_for_active` takes precedence |
| 312 | + await m.wait_for_idle(apps=["dummy_app"], wait_for_active=True, status="doesn't matter") |
| 313 | + |
| 314 | + mock_apps.assert_called_with() |
0 commit comments