Skip to content

Commit 003ac25

Browse files
Add tests for ConfigManagingActor
Signed-off-by: Elzbieta Kotulska <[email protected]>
1 parent 2b60cf0 commit 003ac25

File tree

1 file changed

+93
-0
lines changed

1 file changed

+93
-0
lines changed

tests/config/test_config_manager.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@
88
from collections.abc import Mapping, MutableMapping
99
from dataclasses import dataclass
1010
from typing import Any
11+
from unittest.mock import MagicMock
1112

1213
import pytest
1314
from frequenz.channels import Broadcast
15+
from frequenz.channels.file_watcher import Event, EventType
16+
from pytest_mock import MockerFixture
1417

1518
from frequenz.sdk.config import ConfigManagingActor
1619
from frequenz.sdk.config._config_managing import _recursive_update
@@ -265,6 +268,96 @@ async def test_update_multiple_files(self, config_file: pathlib.Path) -> None:
265268
"dict_str_int": {"a": 1, "b": 2, "c": 4},
266269
}
267270

271+
async def test_actor_works_if_not_all_config_files_exist(
272+
self, config_file: pathlib.Path
273+
) -> None:
274+
"""Test ConfigManagingActor works if not all config files exist."""
275+
config_channel: Broadcast[Mapping[str, Any]] = Broadcast(
276+
name="Config Channel", resend_latest=True
277+
)
278+
config_receiver = config_channel.new_receiver()
279+
280+
# This file does not exist
281+
config_file2 = config_file.parent / "config2.toml"
282+
283+
async with ConfigManagingActor(
284+
[config_file, config_file2],
285+
config_channel.new_sender(),
286+
force_polling=False,
287+
):
288+
config = await config_receiver.receive()
289+
assert config is not None
290+
assert config.get("var2") is None
291+
292+
number = 5
293+
config_file.write_text(create_content(number=number))
294+
295+
config = await config_receiver.receive()
296+
assert config is not None
297+
assert config.get("var2") == str(number)
298+
299+
# Create second config file that overrides the value from the first one
300+
number = 42
301+
config_file2.write_text(create_content(number=number))
302+
303+
config = await config_receiver.receive()
304+
assert config is not None
305+
assert config.get("var2") == str(number)
306+
307+
async def test_actor_does_not_crash_if_file_is_deleted(
308+
self, config_file: pathlib.Path, mocker: MockerFixture
309+
) -> None:
310+
"""Test ConfigManagingActor does not crash if a file is deleted."""
311+
config_channel: Broadcast[Mapping[str, Any]] = Broadcast(
312+
name="Config Channel", resend_latest=True
313+
)
314+
config_receiver = config_channel.new_receiver()
315+
316+
number = 5
317+
config_file2 = config_file.parent / "config2.toml"
318+
config_file2.write_text(create_content(number=number))
319+
320+
# Not config file but existing in the same directory
321+
any_file = config_file.parent / "any_file.txt"
322+
any_file.write_text("content")
323+
324+
file_watcher_mock = MagicMock()
325+
file_watcher_mock.__anext__.side_effect = [
326+
Event(EventType.DELETE, any_file),
327+
Event(EventType.MODIFY, config_file2),
328+
Event(EventType.MODIFY, config_file),
329+
]
330+
331+
mocker.patch(
332+
"frequenz.channels.file_watcher.FileWatcher", return_value=file_watcher_mock
333+
)
334+
335+
async with ConfigManagingActor(
336+
[config_file, config_file2],
337+
config_channel.new_sender(),
338+
force_polling=False,
339+
) as actor:
340+
send_config_spy = mocker.spy(actor, "send_config")
341+
342+
config = await config_receiver.receive()
343+
assert config is not None
344+
assert config.get("var2") == str(number)
345+
send_config_spy.assert_called_once()
346+
send_config_spy.reset_mock()
347+
348+
# Remove file and send DELETE events
349+
any_file.unlink()
350+
config_file2.unlink()
351+
number = 101
352+
config_file.write_text(create_content(number=number))
353+
354+
config = await config_receiver.receive()
355+
assert config is not None
356+
assert config.get("var2") == str(number)
357+
# Config should be updated only once on MODIFY event
358+
# DELETE events are ignored
359+
send_config_spy.assert_called_once()
360+
268361

269362
@dataclass(frozen=True, kw_only=True)
270363
class RecursiveUpdateTestCase:

0 commit comments

Comments
 (0)