Skip to content

Commit ceef54b

Browse files
Add tests for ConfigManagingActor
Signed-off-by: Elzbieta Kotulska <[email protected]>
1 parent c3e3c06 commit ceef54b

File tree

1 file changed

+90
-0
lines changed

1 file changed

+90
-0
lines changed

tests/config/test_config_manager.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
import pytest
1313
from frequenz.channels import Broadcast
14+
from frequenz.channels.file_watcher import Event, EventType
15+
from pytest_mock import MockerFixture
1416

1517
from frequenz.sdk.config import ConfigManagingActor
1618
from frequenz.sdk.config._config_managing import _recursive_update
@@ -265,6 +267,94 @@ async def test_update_multiple_files(self, config_file: pathlib.Path) -> None:
265267
"dict_str_int": {"a": 1, "b": 2, "c": 4},
266268
}
267269

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

269359
@dataclass(frozen=True, kw_only=True)
270360
class RecursiveUpdateTestCase:

0 commit comments

Comments
 (0)