Files
perl_d 1774cf363b
CI / lint (push) Successful in 32s
CI / test-with-coverage (push) Successful in 38s
CI / test (3.12) (push) Successful in 38s
CI / test (3.13) (push) Successful in 38s
CI / test (3.14) (push) Successful in 42s
CI / coverage-analysis (push) Skipped
fix: spelling
2026-10-30 13:46:33 +01:00

120 lines
3.5 KiB
Python

from unittest.mock import MagicMock, patch
import pytest
from bec_lib.endpoints import EndpointInfo
from aare_bec_proto.actor_manager import AareActorManager
from aare_bec_proto.actors import all_actor_classes
from aare_bec_proto.actors.core import MXAutomationActor
from aare_bec_proto.actors.mx_automation import (
OnOpticalCenteringDoneRunXrayCentering,
OnSampleMountedRunOpticalCentering,
)
from aare_bec_proto.messages import MXMeasurementSequenceMessage
class FakeConfigValue:
"""Stand-in for RedisConfigValue: a bool with subscribers, no Redis."""
initial = False # value "found in Redis" on construction
def __init__(self, connector, endpoint, wait_for_writes=True):
self.endpoint = endpoint
self._value = self.initial
self._cbs = []
self.unregistered = False
@property
def value(self):
return self._value
@value.setter
def value(self, value):
self._value = value
for cb in self._cbs:
cb(value)
def subscribe(self, cb):
self._cbs.append(cb)
def unregister_all(self):
self.unregistered = True
class DummyActor(MXAutomationActor):
config_key = "dummy"
def default_monitor_endpoints(self) -> set[EndpointInfo[type[MXMeasurementSequenceMessage]]]:
return set()
def condition(self, client) -> bool:
return True
def action(self, client) -> None:
pass
@pytest.fixture
def manager():
with patch("aare_bec_proto.actor_manager.RedisConfigValue", FakeConfigValue):
client = MagicMock()
mgr = AareActorManager(client, actor_classes=[DummyActor])
yield mgr, client
mgr.shutdown()
def test_all_actor_classes_finds_concrete_subclasses():
classes = all_actor_classes()
assert OnSampleMountedRunOpticalCentering in classes
assert OnOpticalCenteringDoneRunXrayCentering in classes
assert MXAutomationActor not in classes
assert len({c.config_key for c in classes}) == len(classes), "config keys must be unique"
def test_default_manager_manages_all_actors():
with patch("aare_bec_proto.actor_manager.RedisConfigValue", FakeConfigValue):
mgr = AareActorManager(MagicMock())
assert mgr.actor_classes == all_actor_classes()
assert mgr.running == []
def test_disabled_actor_is_not_started(manager):
mgr, _ = manager
assert not mgr.is_enabled(DummyActor)
assert mgr.running == []
def test_enabling_starts_and_disabling_stops_actor(manager):
mgr, client = manager
mgr.set_enabled(DummyActor, True)
assert mgr.running == [DummyActor]
actor, thread, _ = mgr._actors_threads_and_stops[DummyActor]
assert isinstance(actor, DummyActor)
assert thread.is_alive()
assert client.connector.send.called # clients were pinged
mgr.set_enabled(DummyActor, False)
assert mgr.running == []
assert not thread.is_alive()
def test_actor_enabled_at_startup_is_started():
with (
patch("aare_bec_proto.actor_manager.RedisConfigValue", FakeConfigValue),
patch.object(FakeConfigValue, "initial", True),
):
mgr = AareActorManager(MagicMock(), actor_classes=[DummyActor])
try:
assert mgr.running == [DummyActor]
finally:
mgr.shutdown()
assert mgr.running == []
def test_shutdown_unregisters_flags(manager):
mgr, _ = manager
mgr.set_enabled(DummyActor, True)
mgr.shutdown()
assert mgr.running == []
assert all(flag.unregistered for flag in mgr._enabled_flags.values())