""" Unit tests for base service infrastructure. Tests: - BaseService registration and configuration - ServiceRegistry service management - ServiceConfig operations """ import pytest from services.base_service import BaseService, ServiceConfig, ServiceRegistry class TestServiceConfig: """Test ServiceConfig container.""" def test_initialization(self): """Test config initialization with kwargs.""" config = ServiceConfig(key1="value1", key2=42) assert config.get("key1") == "value1" assert config.get("key2") == 42 def test_get_with_default(self): """Test get with default value.""" config = ServiceConfig(key1="value1") assert config.get("key1") == "value1" assert config.get("missing", "default") == "default" assert config.get("missing") is None def test_set_value(self): """Test setting values.""" config = ServiceConfig() config.set("key1", "value1") assert config.get("key1") == "value1" def test_dictionary_access(self): """Test dictionary-style access.""" config = ServiceConfig(key1="value1") assert config["key1"] == "value1" config["key2"] = "value2" assert config["key2"] == "value2" def test_contains(self): """Test 'in' operator.""" config = ServiceConfig(key1="value1") assert "key1" in config assert "missing" not in config class TestBaseService: """Test BaseService abstract class.""" def setup_method(self): """Reset service state before each test.""" # Create concrete test service class TestService(BaseService): SERVICE_NAME = "test_service" @classmethod def register(cls, **config): super().register(**config) self.TestService = TestService # Reset state self.TestService._registered = False self.TestService._config = None def test_registration(self): """Test service registration.""" assert not self.TestService.is_registered() self.TestService.register(key1="value1", key2=42) assert self.TestService.is_registered() assert self.TestService.get_config().get("key1") == "value1" assert self.TestService.get_config().get("key2") == 42 def test_double_registration_fails(self): """Test that double registration raises error.""" self.TestService.register(key1="value1") with pytest.raises(RuntimeError, match="already registered"): self.TestService.register(key2="value2") def test_assert_registered(self): """Test assert_registered raises when not registered.""" with pytest.raises(RuntimeError, match="not registered"): self.TestService.assert_registered() self.TestService.register() self.TestService.assert_registered() # Should not raise def test_get_config_before_registration(self): """Test get_config raises before registration.""" with pytest.raises(RuntimeError, match="not registered"): self.TestService.get_config() def test_get_middleware_default(self): """Test default get_middleware returns None.""" assert self.TestService.get_middleware() is None def test_on_shutdown_default(self): """Test default on_shutdown does nothing.""" self.TestService.on_shutdown() # Should not raise class TestServiceRegistry: """Test ServiceRegistry global registry.""" def setup_method(self): """Reset registry before each test.""" ServiceRegistry._services = {} def test_register_service(self): """Test registering a service.""" class TestService(BaseService): SERVICE_NAME = "test_service" @classmethod def register(cls, **config): super().register(**config) ServiceRegistry.register_service(TestService) assert ServiceRegistry.get_service("test_service") == TestService def test_register_multiple_services(self): """Test registering multiple services.""" class Service1(BaseService): SERVICE_NAME = "service1" @classmethod def register(cls, **config): super().register(**config) class Service2(BaseService): SERVICE_NAME = "service2" @classmethod def register(cls, **config): super().register(**config) ServiceRegistry.register_service(Service1) ServiceRegistry.register_service(Service2) assert len(ServiceRegistry.get_all_services()) == 2 assert ServiceRegistry.get_service("service1") == Service1 assert ServiceRegistry.get_service("service2") == Service2 def test_get_nonexistent_service(self): """Test getting service that doesn't exist.""" assert ServiceRegistry.get_service("nonexistent") is None def test_overwrite_service(self): """Test registering service with same name overwrites.""" class Service1(BaseService): SERVICE_NAME = "test" version = 1 @classmethod def register(cls, **config): super().register(**config) class Service2(BaseService): SERVICE_NAME = "test" version = 2 @classmethod def register(cls, **config): super().register(**config) ServiceRegistry.register_service(Service1) ServiceRegistry.register_service(Service2) service = ServiceRegistry.get_service("test") assert service.version == 2 def test_get_all_middleware(self): """Test getting middleware from all services.""" class MockMiddleware: pass class ServiceWithMiddleware(BaseService): SERVICE_NAME = "with_middleware" @classmethod def register(cls, **config): super().register(**config) @classmethod def get_middleware(cls): return MockMiddleware() class ServiceWithoutMiddleware(BaseService): SERVICE_NAME = "without_middleware" @classmethod def register(cls, **config): super().register(**config) # Register services ServiceWithMiddleware.register() ServiceWithoutMiddleware.register() ServiceRegistry.register_service(ServiceWithMiddleware) ServiceRegistry.register_service(ServiceWithoutMiddleware) middleware_list = ServiceRegistry.get_all_middleware() assert len(middleware_list) == 1 assert isinstance(middleware_list[0], MockMiddleware) def test_shutdown_all(self): """Test calling shutdown on all services.""" shutdown_called = [] class Service1(BaseService): SERVICE_NAME = "service1" @classmethod def register(cls, **config): super().register(**config) @classmethod def on_shutdown(cls): shutdown_called.append("service1") class Service2(BaseService): SERVICE_NAME = "service2" @classmethod def register(cls, **config): super().register(**config) @classmethod def on_shutdown(cls): shutdown_called.append("service2") ServiceRegistry.register_service(Service1) ServiceRegistry.register_service(Service2) ServiceRegistry.shutdown_all() assert "service1" in shutdown_called assert "service2" in shutdown_called