Skip to content

Commit 3ce7400

Browse files
Add tests for ConfigManagingActor
Signed-off-by: Elzbieta Kotulska <[email protected]>
1 parent 02726d8 commit 3ce7400

File tree

1 file changed

+88
-0
lines changed

1 file changed

+88
-0
lines changed

tests/config/test_config_manager.py

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

269357
@dataclass(frozen=True, kw_only=True)
270358
class RecursiveUpdateTestCase:

0 commit comments

Comments
 (0)