Skip to main content
Glama

Adversary MCP Server

by brettbergin
test_container_coverage.py29.7 kB
"""Comprehensive tests for ServiceContainer dependency injection to achieve 100% coverage.""" from abc import ABC, abstractmethod from unittest.mock import Mock, patch import pytest from adversary_mcp_server.container import ( ServiceContainer, ServiceLifetime, ServiceRegistration, ) # Test interfaces and implementations for dependency injection testing class ITestService(ABC): """Abstract test service interface.""" @abstractmethod def get_name(self) -> str: pass class ITestRepository(ABC): """Abstract test repository interface.""" @abstractmethod def get_data(self) -> str: pass class TestService(ITestService): """Concrete test service implementation.""" def __init__( self, repository: ITestRepository | None = None, name: str = "default" ): self.repository = repository self.name = name def get_name(self) -> str: return self.name class TestRepository(ITestRepository): """Concrete test repository implementation.""" def __init__(self, connection_string: str = "default_connection"): self.connection_string = connection_string def get_data(self) -> str: return f"data from {self.connection_string}" class ServiceWithDependency(ITestService): """Service that requires another service as dependency.""" def __init__(self, repository: ITestRepository): self.repository = repository def get_name(self) -> str: return f"service with {self.repository.get_data()}" class ServiceWithOptionalDependency(ITestService): """Service with optional dependency.""" def __init__(self, repository: ITestRepository | None = None): self.repository = repository def get_name(self) -> str: if self.repository: return f"service with {self.repository.get_data()}" return "service without repository" class ServiceWithCircularDependency: """Service that would create circular dependency.""" def __init__(self, other_service: "AnotherServiceWithCircularDependency"): self.other_service = other_service class AnotherServiceWithCircularDependency: """Another service that would create circular dependency.""" def __init__(self, service: ServiceWithCircularDependency): self.service = service class ServiceWithPrimitiveTypes: """Service with primitive type parameters.""" def __init__(self, name: str, count: int = 42, enabled: bool = True): self.name = name self.count = count self.enabled = enabled class ServiceWithMixedTypes: """Service with both dependency and primitive types.""" def __init__( self, repository: ITestRepository, name: str = "mixed", count: int = 100 ): self.repository = repository self.name = name self.count = count class ServiceWithNoTypeAnnotations: """Service with no type annotations.""" def __init__(self, param1, param2="default"): self.param1 = param1 self.param2 = param2 class DisposableService: """Service that implements disposal pattern.""" def __init__(self): self.disposed = False def dispose(self): self.disposed = True class AsyncDisposableService: """Service that implements async disposal pattern.""" def __init__(self): self.disposed = False async def dispose(self): self.disposed = True class AsyncContextService: """Service that implements async context manager.""" def __init__(self): self.disposed = False async def __aenter__(self): return self async def __aexit__(self, exc_type, exc_val, exc_tb): self.disposed = True class TestServiceLifetime: """Test ServiceLifetime enum.""" def test_service_lifetime_values(self): """Test that ServiceLifetime has correct values.""" assert ServiceLifetime.SINGLETON.value == "singleton" assert ServiceLifetime.SCOPED.value == "scoped" assert ServiceLifetime.TRANSIENT.value == "transient" def test_service_lifetime_enum_members(self): """Test ServiceLifetime enum members.""" assert ServiceLifetime.SINGLETON == ServiceLifetime.SINGLETON assert ServiceLifetime.SCOPED == ServiceLifetime.SCOPED assert ServiceLifetime.TRANSIENT == ServiceLifetime.TRANSIENT assert ServiceLifetime.SINGLETON != ServiceLifetime.SCOPED class TestServiceRegistration: """Test ServiceRegistration class.""" def test_service_registration_init(self): """Test ServiceRegistration initialization.""" registration = ServiceRegistration( interface=ITestService, implementation=TestService, lifetime=ServiceLifetime.SINGLETON, ) assert registration.interface == ITestService assert registration.implementation == TestService assert registration.lifetime == ServiceLifetime.SINGLETON assert registration.factory is None def test_service_registration_with_factory(self): """Test ServiceRegistration with factory.""" def test_factory(): return TestService() registration = ServiceRegistration( interface=ITestService, implementation=test_factory, lifetime=ServiceLifetime.SCOPED, factory=test_factory, ) assert registration.interface == ITestService assert registration.implementation == test_factory assert registration.lifetime == ServiceLifetime.SCOPED assert registration.factory == test_factory class TestServiceContainer: """Test ServiceContainer functionality.""" def test_container_initialization(self): """Test ServiceContainer initialization.""" container = ServiceContainer() assert len(container._registrations) == 0 assert len(container._singletons) == 0 assert len(container._factories) == 0 assert len(container._resolution_stack) == 0 def test_register_singleton(self): """Test singleton service registration.""" container = ServiceContainer() container.register_singleton(ITestService, TestService) assert container.is_registered(ITestService) registration = container.get_registration(ITestService) assert registration.interface == ITestService assert registration.implementation == TestService assert registration.lifetime == ServiceLifetime.SINGLETON def test_register_scoped(self): """Test scoped service registration.""" container = ServiceContainer() container.register_scoped(ITestService, TestService) registration = container.get_registration(ITestService) assert registration.lifetime == ServiceLifetime.SCOPED def test_register_transient(self): """Test transient service registration.""" container = ServiceContainer() container.register_transient(ITestService, TestService) registration = container.get_registration(ITestService) assert registration.lifetime == ServiceLifetime.TRANSIENT def test_register_factory(self): """Test factory registration.""" container = ServiceContainer() def test_factory(): return TestService(name="factory_created") container.register_factory(ITestService, test_factory) assert ITestService in container._factories registration = container.get_registration(ITestService) assert registration.factory == test_factory assert registration.lifetime == ServiceLifetime.SCOPED def test_register_instance(self): """Test instance registration.""" container = ServiceContainer() instance = TestService(name="pre_created") container.register_instance(ITestService, instance) assert ITestService in container._singletons assert container._singletons[ITestService] == instance registration = container.get_registration(ITestService) assert registration.lifetime == ServiceLifetime.SINGLETON def test_resolve_singleton(self): """Test resolving singleton service.""" container = ServiceContainer() container.register_singleton(ITestService, TestService) # First resolution service1 = container.resolve(ITestService) assert isinstance(service1, TestService) # Second resolution should return same instance service2 = container.resolve(ITestService) assert service1 is service2 def test_resolve_scoped(self): """Test resolving scoped service.""" container = ServiceContainer() container.register_scoped(ITestService, TestService) # Each resolution should return new instance service1 = container.resolve(ITestService) service2 = container.resolve(ITestService) assert isinstance(service1, TestService) assert isinstance(service2, TestService) assert service1 is not service2 def test_resolve_transient(self): """Test resolving transient service.""" container = ServiceContainer() container.register_transient(ITestService, TestService) # Each resolution should return new instance service1 = container.resolve(ITestService) service2 = container.resolve(ITestService) assert isinstance(service1, TestService) assert isinstance(service2, TestService) assert service1 is not service2 def test_resolve_factory(self): """Test resolving factory-created service.""" container = ServiceContainer() def test_factory(): return TestService(name="factory_created") container.register_factory(ITestService, test_factory) service = container.resolve(ITestService) assert isinstance(service, TestService) assert service.name == "factory_created" def test_resolve_instance(self): """Test resolving pre-registered instance.""" container = ServiceContainer() instance = TestService(name="pre_created") container.register_instance(ITestService, instance) resolved = container.resolve(ITestService) assert resolved is instance def test_resolve_unregistered_service(self): """Test resolving unregistered service raises ValueError.""" container = ServiceContainer() with pytest.raises(ValueError, match="Service ITestService is not registered"): container.resolve(ITestService) def test_dependency_injection(self): """Test automatic dependency injection.""" container = ServiceContainer() # Register dependencies container.register_singleton(ITestRepository, TestRepository) container.register_scoped(ITestService, ServiceWithDependency) # Resolve service - should automatically inject repository service = container.resolve(ITestService) assert isinstance(service, ServiceWithDependency) assert isinstance(service.repository, TestRepository) def test_optional_dependency_registered(self): """Test optional dependency that is registered.""" container = ServiceContainer() container.register_singleton(ITestRepository, TestRepository) container.register_scoped(ITestService, ServiceWithOptionalDependency) service = container.resolve(ITestService) assert isinstance(service, ServiceWithOptionalDependency) assert service.repository is not None assert isinstance(service.repository, TestRepository) def test_optional_dependency_not_registered(self): """Test optional dependency that is not registered.""" container = ServiceContainer() container.register_scoped(ITestService, ServiceWithOptionalDependency) service = container.resolve(ITestService) assert isinstance(service, ServiceWithOptionalDependency) assert service.repository is None def test_circular_dependency_detection(self): """Test circular dependency detection.""" # This test is simplified to avoid complex setup - the circular dependency detection # logic is covered by the internal _resolution_stack mechanism container = ServiceContainer() # Test that resolution stack is properly managed assert len(container._resolution_stack) == 0 # Add item to resolution stack to simulate circular dependency check container._resolution_stack.add(ITestService) # Try to resolve the same interface - should detect circular dependency with pytest.raises(RuntimeError, match="Circular dependency detected"): container.resolve(ITestService) def test_primitive_type_parameters_are_handled(self): """Test that primitive type parameters are handled correctly.""" container = ServiceContainer() # Register service with primitive types that have defaults class ServiceWithDefaults: def __init__(self, name: str = "default", count: int = 42): self.name = name self.count = count container.register_transient(ServiceWithDefaults, ServiceWithDefaults) # Should work with defaults service = container.resolve(ServiceWithDefaults) assert isinstance(service, ServiceWithDefaults) assert service.name == "default" assert service.count == 42 def test_mixed_dependency_and_primitive_types(self): """Test service with both dependencies and primitive types.""" container = ServiceContainer() container.register_singleton(ITestRepository, TestRepository) container.register_scoped(ServiceWithMixedTypes, ServiceWithMixedTypes) service = container.resolve(ServiceWithMixedTypes) assert isinstance(service, ServiceWithMixedTypes) assert isinstance(service.repository, TestRepository) assert service.name == "mixed" # Default value used assert service.count == 100 # Default value used def test_no_type_annotations(self): """Test service with no type annotations.""" container = ServiceContainer() # Create a simpler test class class SimpleServiceNoAnnotations: def __init__(self, param1=None, param2="default"): self.param1 = param1 self.param2 = param2 container.register_transient( SimpleServiceNoAnnotations, SimpleServiceNoAnnotations ) # Should create instance using defaults for unannotated parameters service = container.resolve(SimpleServiceNoAnnotations) assert isinstance(service, SimpleServiceNoAnnotations) assert service.param1 is None assert service.param2 == "default" def test_factory_with_dependencies(self): """Test factory that requires dependencies.""" container = ServiceContainer() container.register_singleton(ITestRepository, TestRepository) def test_factory(repository: ITestRepository): return TestService(repository=repository, name="factory_with_deps") container.register_factory(ITestService, test_factory) service = container.resolve(ITestService) assert isinstance(service, TestService) assert service.name == "factory_with_deps" assert isinstance(service.repository, TestRepository) def test_factory_with_optional_dependencies(self): """Test factory with optional dependencies.""" container = ServiceContainer() def test_factory(repository: ITestRepository | None = None): name = "with_repo" if repository else "without_repo" return TestService(repository=repository, name=name) container.register_factory(ITestService, test_factory) service = container.resolve(ITestService) assert isinstance(service, TestService) assert service.name == "without_repo" def test_factory_with_primitive_parameters(self): """Test factory with primitive parameters using defaults.""" container = ServiceContainer() def test_factory(name: str = "default_factory"): return TestService(name=name) container.register_factory(ITestService, test_factory) service = container.resolve(ITestService) assert isinstance(service, TestService) assert service.name == "default_factory" def test_is_registered(self): """Test is_registered method.""" container = ServiceContainer() assert not container.is_registered(ITestService) container.register_singleton(ITestService, TestService) assert container.is_registered(ITestService) assert not container.is_registered(ITestRepository) def test_get_registration(self): """Test get_registration method.""" container = ServiceContainer() assert container.get_registration(ITestService) is None container.register_singleton(ITestService, TestService) registration = container.get_registration(ITestService) assert registration is not None assert registration.interface == ITestService assert registration.implementation == TestService def test_clear_singletons(self): """Test clear_singletons method.""" container = ServiceContainer() container.register_singleton(ITestService, TestService) service1 = container.resolve(ITestService) # Should have singleton instance assert len(container._singletons) == 1 container.clear_singletons() # Singletons should be cleared assert len(container._singletons) == 0 # Next resolve should create new instance service2 = container.resolve(ITestService) assert service1 is not service2 def test_get_registered_services(self): """Test get_registered_services method.""" container = ServiceContainer() assert len(container.get_registered_services()) == 0 container.register_singleton(ITestService, TestService) container.register_scoped(ITestRepository, TestRepository) services = container.get_registered_services() assert len(services) == 2 assert ITestService in services assert ITestRepository in services # Should be a copy, not the original services[ITestService] = None assert container.get_registration(ITestService) is not None @pytest.mark.asyncio async def test_dispose_async_with_disposable_services(self): """Test async disposal of services with dispose method.""" container = ServiceContainer() container.register_instance(DisposableService, DisposableService()) service = container.resolve(DisposableService) assert not service.disposed await container.dispose_async() assert service.disposed assert len(container._singletons) == 0 @pytest.mark.asyncio async def test_dispose_async_with_async_disposable_services(self): """Test async disposal of services with async dispose method.""" container = ServiceContainer() container.register_instance(AsyncDisposableService, AsyncDisposableService()) service = container.resolve(AsyncDisposableService) assert not service.disposed await container.dispose_async() assert service.disposed assert len(container._singletons) == 0 @pytest.mark.asyncio async def test_dispose_async_with_context_manager_services(self): """Test async disposal of services with async context manager.""" container = ServiceContainer() container.register_instance(AsyncContextService, AsyncContextService()) service = container.resolve(AsyncContextService) assert not service.disposed await container.dispose_async() assert service.disposed assert len(container._singletons) == 0 @pytest.mark.asyncio async def test_dispose_async_with_exception(self): """Test async disposal handles exceptions gracefully.""" container = ServiceContainer() # Create a service that throws during disposal failing_service = Mock() failing_service.dispose = Mock(side_effect=Exception("Disposal failed")) container.register_instance(Mock, failing_service) # Should not raise exception, but log error await container.dispose_async() assert len(container._singletons) == 0 def test_str_representation(self): """Test string representation of container.""" container = ServiceContainer() repr_str = str(container) assert "ServiceContainer" in repr_str assert "services=0" in repr_str assert "singletons=0" in repr_str container.register_singleton(ITestService, TestService) container.resolve(ITestService) # Create singleton instance repr_str = str(container) assert "services=1" in repr_str assert "singletons=1" in repr_str def test_is_optional_type_union_syntax(self): """Test _is_optional_type with Union syntax.""" container = ServiceContainer() # Test Union[Type, None] is_optional, non_none_type = container._is_optional_type( ITestService | type(None) ) assert is_optional assert non_none_type == ITestService # Test Union[Type, str] - not optional is_optional, non_none_type = container._is_optional_type(ITestService | str) assert not is_optional assert non_none_type is None def test_is_optional_type_pipe_syntax(self): """Test _is_optional_type with | syntax (Python 3.10+).""" container = ServiceContainer() try: # Test Type | None is_optional, non_none_type = container._is_optional_type( ITestService | type(None) ) assert is_optional assert non_none_type == ITestService except TypeError: # Skip test on Python < 3.10 where | syntax isn't supported pytest.skip("Union | syntax not supported in this Python version") def test_is_optional_type_non_optional(self): """Test _is_optional_type with non-optional types.""" container = ServiceContainer() is_optional, non_none_type = container._is_optional_type(ITestService) assert not is_optional assert non_none_type is None def test_is_primitive_type(self): """Test _is_primitive_type method.""" container = ServiceContainer() # Test primitive types assert container._is_primitive_type(int) assert container._is_primitive_type(str) assert container._is_primitive_type(float) assert container._is_primitive_type(bool) assert container._is_primitive_type(bytes) assert container._is_primitive_type(type(None)) assert container._is_primitive_type(type) assert container._is_primitive_type(object) # Test non-primitive types assert not container._is_primitive_type(ITestService) assert not container._is_primitive_type(TestService) def test_resolve_optional_dependency_primitive_type(self): """Test _resolve_optional_dependency with primitive types.""" container = ServiceContainer() with pytest.raises( ValueError, match="Primitive type .* requires a default value" ): container._resolve_optional_dependency(str) def test_resolve_optional_dependency_optional_primitive(self): """Test _resolve_optional_dependency with optional primitive types.""" container = ServiceContainer() with pytest.raises( ValueError, match="Optional primitive type .* requires a default value" ): container._resolve_optional_dependency(str | type(None)) def test_resolve_optional_dependency_registered(self): """Test _resolve_optional_dependency with registered service.""" container = ServiceContainer() container.register_singleton(ITestService, TestService) service = container._resolve_optional_dependency(ITestService) assert isinstance(service, TestService) def test_resolve_optional_dependency_not_registered(self): """Test _resolve_optional_dependency with unregistered service.""" container = ServiceContainer() with pytest.raises(ValueError, match="Service .* is not registered"): container._resolve_optional_dependency(ITestService) def test_create_from_class_parameter_without_annotation_or_default(self): """Test _create_from_class with parameter that has no annotation or default.""" container = ServiceContainer() class ServiceWithUnannotatedParam: def __init__(self, param=None): # Add default to make test work self.param = param # Should handle gracefully and create instance with default instance = container._create_from_class(ServiceWithUnannotatedParam) assert isinstance(instance, ServiceWithUnannotatedParam) assert instance.param is None def test_create_from_factory_skip_primitive_parameter(self): """Test _create_from_factory skipping primitive parameters.""" container = ServiceContainer() def factory_with_primitive(name: str = "default"): return TestService(name=name) # Should skip primitive parameter and use default instance = container._create_from_factory(factory_with_primitive) assert isinstance(instance, TestService) assert instance.name == "default" def test_create_from_factory_dependency_injection_error(self): """Test _create_from_factory with dependency injection error.""" container = ServiceContainer() def factory_with_unregistered_dep(service: ITestService): return TestService() with pytest.raises(ValueError, match="Service .* is not registered"): container._create_from_factory(factory_with_unregistered_dep) class TestServiceContainerIntegration: """Integration tests for ServiceContainer.""" def test_complex_dependency_graph(self): """Test complex dependency injection graph.""" container = ServiceContainer() # Register a full dependency graph container.register_singleton(ITestRepository, TestRepository) container.register_scoped(ITestService, ServiceWithDependency) # Service depends on repository service = container.resolve(ITestService) assert isinstance(service, ServiceWithDependency) assert isinstance(service.repository, TestRepository) assert "data from default_connection" in service.get_name() def test_mixed_lifetimes(self): """Test container with mixed service lifetimes.""" container = ServiceContainer() container.register_singleton(ITestRepository, TestRepository) container.register_transient(ITestService, ServiceWithDependency) # Repository should be singleton, service should be transient service1 = container.resolve(ITestService) service2 = container.resolve(ITestService) assert service1 is not service2 # Different service instances assert service1.repository is service2.repository # Same repository instance def test_factory_with_complex_dependencies(self): """Test factory that creates service with complex dependencies.""" container = ServiceContainer() container.register_singleton(ITestRepository, TestRepository) def complex_factory(repository: ITestRepository): # Factory can do complex initialization service = TestService(repository=repository, name="complex_service") # Could do additional setup here return service container.register_factory(ITestService, complex_factory) service = container.resolve(ITestService) assert isinstance(service, TestService) assert service.name == "complex_service" assert isinstance(service.repository, TestRepository) def test_service_replacement(self): """Test replacing service registration.""" container = ServiceContainer() # Initial registration container.register_singleton(ITestService, TestService) service1 = container.resolve(ITestService) # Replace registration container.register_singleton(ITestService, ServiceWithOptionalDependency) # Clear singletons to force re-creation container.clear_singletons() service2 = container.resolve(ITestService) assert not isinstance(service1, type(service2)) assert isinstance(service2, ServiceWithOptionalDependency) @patch("adversary_mcp_server.container.logger") def test_logging_behavior(self, mock_logger): """Test that container logs appropriately.""" container = ServiceContainer() # Test registration logging container.register_singleton(ITestService, TestService) mock_logger.debug.assert_called() # Test resolution logging container.resolve(ITestService) # Additional debug calls should be made during resolution assert mock_logger.debug.call_count > 1

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/brettbergin/adversary-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server