test_service.py 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. # -*- coding: utf-8 -*-
  2. """
  3. Unit-Tests für trixy_core.service.
  4. """
  5. import pytest
  6. from unittest.mock import MagicMock, AsyncMock
  7. from trixy_core.service.enums import ServicePriority, ServiceGroup, ServiceState
  8. from trixy_core.service.iservice import IService
  9. from trixy_core.service.service_container import ServiceContainer
  10. class TestServiceEnums:
  11. """Tests für Service-Enumerationen."""
  12. def test_service_priority_ordering(self):
  13. """Testet die Prioritätsreihenfolge."""
  14. assert ServicePriority.CRITICAL < ServicePriority.CORE
  15. assert ServicePriority.CORE < ServicePriority.NETWORK
  16. assert ServicePriority.NETWORK < ServicePriority.MANAGER
  17. assert ServicePriority.MANAGER < ServicePriority.PLUGIN
  18. assert ServicePriority.PLUGIN < ServicePriority.OPTIONAL
  19. def test_service_priority_values(self):
  20. """Testet die Prioritätswerte."""
  21. assert ServicePriority.CRITICAL == 0
  22. assert ServicePriority.CORE == 10
  23. assert ServicePriority.NETWORK == 20
  24. assert ServicePriority.MANAGER == 30
  25. assert ServicePriority.PLUGIN == 40
  26. assert ServicePriority.OPTIONAL == 50
  27. def test_service_group_exists(self):
  28. """Testet, dass Service-Gruppen existieren."""
  29. assert hasattr(ServiceGroup, "CRITICAL")
  30. assert hasattr(ServiceGroup, "NETWORK")
  31. assert hasattr(ServiceGroup, "MANAGER")
  32. assert hasattr(ServiceGroup, "AUDIO")
  33. assert hasattr(ServiceGroup, "ML")
  34. assert hasattr(ServiceGroup, "UTILITY")
  35. assert hasattr(ServiceGroup, "PLUGIN")
  36. def test_service_state_values(self):
  37. """Testet die Service-Zustände."""
  38. assert hasattr(ServiceState, "STOPPED")
  39. assert hasattr(ServiceState, "STARTING")
  40. assert hasattr(ServiceState, "RUNNING")
  41. assert hasattr(ServiceState, "STOPPING")
  42. assert hasattr(ServiceState, "FAILED")
  43. class ConcreteService(IService):
  44. """Konkrete Service-Implementierung für Tests."""
  45. PRIORITY = ServicePriority.CORE
  46. GROUP = ServiceGroup.UTILITY
  47. DEPENDENCIES = []
  48. NAME = "TestService"
  49. def __init__(self, application):
  50. super().__init__(application)
  51. self.started = False
  52. self.stopped = False
  53. async def start(self):
  54. self.started = True
  55. async def stop(self):
  56. self.stopped = True
  57. class DependentService(IService):
  58. """Service mit Abhängigkeit für Tests."""
  59. PRIORITY = ServicePriority.MANAGER
  60. GROUP = ServiceGroup.UTILITY
  61. DEPENDENCIES = ["TestService"]
  62. NAME = "DependentService"
  63. async def start(self):
  64. pass
  65. async def stop(self):
  66. pass
  67. class TestIService:
  68. """Tests für IService-Basisklasse."""
  69. @pytest.fixture
  70. def mock_application(self):
  71. """Erstellt eine Mock-Anwendung."""
  72. return MagicMock()
  73. @pytest.fixture
  74. def service(self, mock_application):
  75. """Erstellt eine Test-Service-Instanz."""
  76. return ConcreteService(mock_application)
  77. def test_service_initialization(self, service, mock_application):
  78. """Testet die Service-Initialisierung."""
  79. assert service.application == mock_application
  80. assert service.state == ServiceState.STOPPED
  81. def test_service_name(self, service):
  82. """Testet den Service-Namen."""
  83. assert service.name == "TestService"
  84. def test_service_is_running(self, service):
  85. """Testet die is_running-Eigenschaft."""
  86. assert service.is_running is False
  87. service._set_state(ServiceState.RUNNING)
  88. assert service.is_running is True
  89. def test_service_priority(self, service):
  90. """Testet die Service-Priorität."""
  91. assert service.PRIORITY == ServicePriority.CORE
  92. def test_service_group(self, service):
  93. """Testet die Service-Gruppe."""
  94. assert service.GROUP == ServiceGroup.UTILITY
  95. def test_service_dependencies(self, service):
  96. """Testet die Service-Abhängigkeiten."""
  97. assert service.DEPENDENCIES == []
  98. @pytest.mark.asyncio
  99. async def test_service_lifecycle_hooks(self, service):
  100. """Testet die Lifecycle-Hooks."""
  101. # Hooks sollten ohne Fehler durchlaufen
  102. await service.on_pre_start()
  103. await service.on_post_start()
  104. await service.on_pre_stop()
  105. await service.on_post_stop()
  106. await service.on_dependency_ready("SomeService")
  107. @pytest.mark.asyncio
  108. async def test_health_check_default(self, service):
  109. """Testet die Standard-Gesundheitsprüfung."""
  110. # Nicht laufend -> False
  111. assert await service.health_check() is False
  112. # Laufend -> True
  113. service._set_state(ServiceState.RUNNING)
  114. assert await service.health_check() is True
  115. class TestServiceContainer:
  116. """Tests für ServiceContainer."""
  117. @pytest.fixture
  118. def mock_application(self):
  119. """Erstellt eine Mock-Anwendung."""
  120. return MagicMock()
  121. @pytest.fixture
  122. def container(self, mock_application):
  123. """Erstellt einen ServiceContainer."""
  124. return ServiceContainer(mock_application)
  125. def test_container_initialization(self, container):
  126. """Testet die Container-Initialisierung."""
  127. assert container.services == {}
  128. def test_register_service(self, container):
  129. """Testet die Service-Registrierung."""
  130. service = container.register(ConcreteService)
  131. assert "TestService" in container.services
  132. assert isinstance(service, ConcreteService)
  133. def test_register_duplicate_raises_error(self, container):
  134. """Testet, dass doppelte Registrierung fehlschlägt."""
  135. container.register(ConcreteService)
  136. with pytest.raises(ValueError, match="bereits registriert"):
  137. container.register(ConcreteService)
  138. def test_get_service(self, container):
  139. """Testet get_service."""
  140. container.register(ConcreteService)
  141. service = container.get_service("TestService")
  142. assert service is not None
  143. assert isinstance(service, ConcreteService)
  144. def test_get_nonexistent_service(self, container):
  145. """Testet get_service für nicht existierenden Service."""
  146. assert container.get_service("NonExistent") is None
  147. def test_getitem_by_name(self, container):
  148. """Testet __getitem__ mit Name."""
  149. container.register(ConcreteService)
  150. service = container["TestService"]
  151. assert isinstance(service, ConcreteService)
  152. def test_getitem_by_class(self, container):
  153. """Testet __getitem__ mit Klasse."""
  154. container.register(ConcreteService)
  155. service = container[ConcreteService]
  156. assert isinstance(service, ConcreteService)
  157. def test_getitem_raises_keyerror(self, container):
  158. """Testet, dass __getitem__ KeyError wirft."""
  159. with pytest.raises(KeyError):
  160. _ = container["NonExistent"]
  161. def test_contains(self, container):
  162. """Testet __contains__."""
  163. container.register(ConcreteService)
  164. assert "TestService" in container
  165. assert "NonExistent" not in container
  166. assert ConcreteService in container
  167. @pytest.mark.asyncio
  168. async def test_start_all(self, container):
  169. """Testet start_all."""
  170. service = container.register(ConcreteService)
  171. await container.start_all()
  172. assert service.started is True
  173. assert service.state == ServiceState.RUNNING
  174. @pytest.mark.asyncio
  175. async def test_stop_all(self, container):
  176. """Testet stop_all."""
  177. service = container.register(ConcreteService)
  178. await container.start_all()
  179. await container.stop_all()
  180. assert service.stopped is True
  181. assert service.state == ServiceState.STOPPED
  182. @pytest.mark.asyncio
  183. async def test_dependency_order(self, container):
  184. """Testet die Abhängigkeitsreihenfolge."""
  185. container.register(ConcreteService)
  186. container.register(DependentService)
  187. order = container._resolve_start_order()
  188. test_idx = order.index("TestService")
  189. dependent_idx = order.index("DependentService")
  190. assert test_idx < dependent_idx
  191. @pytest.mark.asyncio
  192. async def test_restart_service(self, container):
  193. """Testet den Service-Neustart."""
  194. service = container.register(ConcreteService)
  195. await container.start_all()
  196. service.started = False
  197. service.stopped = False
  198. await container.restart_service("TestService")
  199. assert service.stopped is True
  200. assert service.started is True