Skip to content

Commit

Permalink
tests: update tests to not use watch.yaml
Browse files Browse the repository at this point in the history
- Reverted SystemdNotices in test charm back to the
  original constructor API.

Signed-off-by: Jason C. Nucciarone <[email protected]>
  • Loading branch information
NucciTheBoss committed Jul 18, 2024
1 parent c8f0767 commit 59e78a1
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
import charms.operator_libs_linux.v1.systemd as systemd
import daemon
from charms.operator_libs_linux.v0.juju_systemd_notices import (
Service,
ServiceStartedEvent,
ServiceStoppedEvent,
SystemdNotices,
Expand All @@ -30,7 +29,7 @@ class NoticesCharm(CharmBase):
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)

self._systemd_notices = SystemdNotices(self, Service("test"))
self._systemd_notices = SystemdNotices(self, ["test"])
event_handler_bindings = {
self.on.install: self._on_install,
self.on.start: self._on_start,
Expand Down
62 changes: 28 additions & 34 deletions tests/unit/test_juju_systemd_notices.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import argparse
import subprocess
import unittest
from pathlib import Path
from unittest.mock import AsyncMock, patch

from charms.operator_libs_linux.v0.juju_systemd_notices import (
Expand Down Expand Up @@ -47,7 +46,7 @@ def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)

self.notices = SystemdNotices(
self, Service("foobar"), Service("snap.test.service", alias="service")
self, ["foobar", Service("snap.test.service", alias="service")]
)
event_handler_bindings = {
self.on.install: self._on_install,
Expand Down Expand Up @@ -96,45 +95,33 @@ def test_generate_hooks(self, mock_exists, _) -> None:
self.harness.charm.notices._generate_hooks()

@patch("pathlib.Path.exists")
def test_generate_config(self, mock_exists) -> None:
def test_generate_service(self, mock_exists) -> None:
"""Test that watch configuration file is generated correctly."""
with Patcher() as patcher:
# Set charm_dir to "/" because mocked charm_dir default
# `no-disk-path` does not exist within the fake filesystem.
# Tried creating `no-disk-path` in the fake filesystem, but
# the mocked objects could not find it.
self.harness.charm.framework.charm_dir = Path("/")
patcher.fs.create_file("watch.yaml")
name = self.harness.charm.unit.name.replace("/", "-")
patcher.fs.create_file(f"/etc/systemd/system/juju-{name}-systemd-notices.service")

# Scenario 1 - Generate success but no pre-existing watch configuration.
mock_exists.return_value = False
self.harness.charm.notices._generate_config()
self.harness.charm.notices._generate_service()

# Scenario 2 - Generate success but pre-existing watch configuration.
mock_exists.return_value = True
self.harness.charm.notices._generate_config()
self.harness.charm.notices._generate_service()

@patch("pathlib.Path.write_text")
@patch("pathlib.Path.symlink_to")
@patch("subprocess.check_output")
@patch("pathlib.Path.exists")
def test_start(self, mock_exists, mock_subp, *_) -> None:
def test_start(self, mock_subp) -> None:
"""Test that start method correctly starts notices daemon."""
# Scenario 1 - Subscribe success but no pre-existing service file.
mock_exists.return_value = False
# Scenario 1 - systemctl successfully starts notices daemon.
self.harness.charm.notices._start()

# Scenario 2 - Subscribe success and pre-existing service file.
mock_exists.return_value = True
self.harness.charm.notices._start()

# Scenario 3 - Subscribe success but systemctl fails to start notices daemon.
# Scenario 2 - systemctl fails to start notices daemon.
mock_subp.side_effect = subprocess.CalledProcessError(1, "systemctl start foobar")
with self.assertRaises(subprocess.CalledProcessError):
self.harness.charm.notices._start()

@patch("charms.operator_libs_linux.v0.juju_systemd_notices.SystemdNotices._generate_hooks")
@patch("charms.operator_libs_linux.v0.juju_systemd_notices.SystemdNotices._generate_config")
@patch("charms.operator_libs_linux.v0.juju_systemd_notices.SystemdNotices._generate_service")
@patch("charms.operator_libs_linux.v0.juju_systemd_notices.SystemdNotices._start")
def test_subscribe(self, *_) -> None:
"""Test `subscribe` method."""
Expand Down Expand Up @@ -221,7 +208,7 @@ async def test_systemd_unit_changed(self, mock_message, mock_variant, *_) -> Non
self.assertTrue(_systemd_unit_changed(mock_message))

@patch(
"charms.operator_libs_linux.v0.juju_systemd_notices._read_config",
"charms.operator_libs_linux.v0.juju_systemd_notices._observed_services",
return_value={"foobar": "foobar"},
)
@patch("charms.operator_libs_linux.v0.juju_systemd_notices._juju_unit", "foobar/0")
Expand Down Expand Up @@ -259,35 +246,42 @@ async def test_get_service_state(self) -> None:
state = await _get_service_state(mock_sysbus, "foobar")
self.assertEqual(state, "unknown")

@patch(
"charms.operator_libs_linux.v0.juju_systemd_notices._observed_services",
{"foobar": "foobar", "snap.test.service": "service"},
)
@patch("charms.operator_libs_linux.v0.juju_systemd_notices._get_service_state")
@patch("charms.operator_libs_linux.v0.juju_systemd_notices._read_config")
async def test_async_load_services(self, mock_config, mock_state) -> None:
mock_config.return_value = {}
async def test_async_load_services(self, mock_state) -> None:
mock_state.return_value = "active"
await _async_load_services()

mock_config.return_value = {"foobar": "foobar"}
mock_state.return_value = "active"
# Somehow calling `_async_load_services()` fully
# covers the for-loop without any additional changes to
# the `_observed_services` global ¯\_(ツ)_/¯
mock_state.return_value = "stopped"
await _async_load_services()

@patch("pathlib.Path.exists", return_value=False)
@patch("asyncio.Event.wait")
async def test_juju_systemd_notices_daemon(self, *_) -> None:
# Desired outcome is that _juju_systemd_notices does not fail to start.
with Patcher() as patcher:
patcher.fs.create_file("watch.yaml", contents=mock_watch_config)
await _juju_systemd_notices_daemon()
await _juju_systemd_notices_daemon()

@patch("charms.operator_libs_linux.v0.juju_systemd_notices._juju_systemd_notices_daemon")
@patch("argparse.ArgumentParser.parse_args")
def test_main(self, mocked_args, *_) -> None:
# Scenario 1 - Desired outcome (juju-systemd-notices daemon starts successfully)
# and debug is set to True.
mocked_args.return_value = argparse.Namespace(debug=True, unit="foobar/0")
mocked_args.return_value = argparse.Namespace(
debug=True, unit="foobar/0", services=["foobar=foobar", "snap.test.service=service"]
)
_main()

# Scenario 2 - Desired outcome (juju-systemd-notices daemon starts successfully)
# and debug is set to False
mocked_args.return_value = argparse.Namespace(debug=False, unit="foobar/0")
mocked_args.return_value = argparse.Namespace(
debug=False, unit="foobar/0", services=["foobar=foobar", "snap.test.service=service"]
)
_main()

# Scenario 3 - Debug flag is passed to script but no unit name.
Expand Down

0 comments on commit 59e78a1

Please sign in to comment.