test_gateway_card_building.py
1 """ 2 Unit tests for gateway card building functionality. 3 Tests _build_gateway_card and _detect_gateway_type methods in BaseGatewayComponent. 4 """ 5 6 import pytest 7 from unittest.mock import MagicMock, patch 8 from a2a.types import AgentCard 9 10 from solace_agent_mesh.common.a2a.utils import is_gateway_card, extract_gateway_info 11 12 13 class TestDetectGatewayType: 14 """Test _detect_gateway_type method behavior.""" 15 16 def test_returns_configured_gateway_type_when_set(self): 17 """Test that configured gateway_type takes precedence.""" 18 mock_component = MagicMock() 19 mock_component.get_config.return_value = "custom_type" 20 21 from solace_agent_mesh.gateway.base.component import BaseGatewayComponent 22 23 result = BaseGatewayComponent._detect_gateway_type(mock_component) 24 25 assert result == "custom_type" 26 mock_component.get_config.assert_called_with("gateway_type") 27 28 def test_detects_http_sse_from_webui_class_name(self): 29 """Test detection of http_sse type from WebUI class name.""" 30 mock_component = MagicMock() 31 mock_component.get_config.return_value = None 32 mock_component.__class__.__name__ = "WebUIGatewayComponent" 33 34 from solace_agent_mesh.gateway.base.component import BaseGatewayComponent 35 36 result = BaseGatewayComponent._detect_gateway_type(mock_component) 37 38 assert result == "http_sse" 39 40 def test_detects_http_sse_from_httpsse_class_name(self): 41 """Test detection of http_sse type from HttpSse class name.""" 42 mock_component = MagicMock() 43 mock_component.get_config.return_value = None 44 mock_component.__class__.__name__ = "HttpSseComponent" 45 46 from solace_agent_mesh.gateway.base.component import BaseGatewayComponent 47 48 result = BaseGatewayComponent._detect_gateway_type(mock_component) 49 50 assert result == "http_sse" 51 52 def test_detects_rest_from_adapter_name(self): 53 """Test detection of rest type from adapter class name.""" 54 mock_component = MagicMock() 55 mock_component.get_config.return_value = None 56 mock_component.__class__.__name__ = "GenericGatewayComponent" 57 mock_component.adapter = MagicMock() 58 mock_component.adapter.__class__.__name__ = "RestAdapter" 59 60 from solace_agent_mesh.gateway.base.component import BaseGatewayComponent 61 62 result = BaseGatewayComponent._detect_gateway_type(mock_component) 63 64 assert result == "rest" 65 66 def test_detects_slack_from_adapter_name(self): 67 """Test detection of slack type from adapter class name.""" 68 mock_component = MagicMock() 69 mock_component.get_config.return_value = None 70 mock_component.__class__.__name__ = "GenericGatewayComponent" 71 mock_component.adapter = MagicMock() 72 mock_component.adapter.__class__.__name__ = "SlackBotAdapter" 73 74 from solace_agent_mesh.gateway.base.component import BaseGatewayComponent 75 76 result = BaseGatewayComponent._detect_gateway_type(mock_component) 77 78 assert result == "slack" 79 80 def test_detects_teams_from_adapter_name(self): 81 """Test detection of teams type from adapter class name.""" 82 mock_component = MagicMock() 83 mock_component.get_config.return_value = None 84 mock_component.__class__.__name__ = "GenericGatewayComponent" 85 mock_component.adapter = MagicMock() 86 mock_component.adapter.__class__.__name__ = "MicrosoftTeamsAdapter" 87 88 from solace_agent_mesh.gateway.base.component import BaseGatewayComponent 89 90 result = BaseGatewayComponent._detect_gateway_type(mock_component) 91 92 assert result == "teams" 93 94 def test_returns_generic_when_no_match(self): 95 """Test fallback to 'generic' when no patterns match.""" 96 mock_component = MagicMock() 97 mock_component.get_config.return_value = None 98 mock_component.__class__.__name__ = "UnknownComponent" 99 mock_component.adapter = None 100 101 from solace_agent_mesh.gateway.base.component import BaseGatewayComponent 102 103 result = BaseGatewayComponent._detect_gateway_type(mock_component) 104 105 assert result == "generic" 106 107 def test_returns_generic_when_adapter_has_no_matching_name(self): 108 """Test fallback to generic when adapter name doesn't match patterns.""" 109 mock_component = MagicMock() 110 mock_component.get_config.return_value = None 111 mock_component.__class__.__name__ = "GenericGatewayComponent" 112 mock_component.adapter = MagicMock() 113 mock_component.adapter.__class__.__name__ = "CustomAdapter" 114 115 from solace_agent_mesh.gateway.base.component import BaseGatewayComponent 116 117 result = BaseGatewayComponent._detect_gateway_type(mock_component) 118 119 assert result == "generic" 120 121 122 class TestBuildGatewayCard: 123 """Test _build_gateway_card method behavior.""" 124 125 def _create_mock_component(self, gateway_id, namespace, gateway_type, deployment=None, card_config=None): 126 """Helper to create properly configured mock component.""" 127 mock_component = MagicMock() 128 mock_component.gateway_id = gateway_id 129 mock_component.namespace = namespace 130 mock_component._gateway_card_config = card_config or {} 131 mock_component._detect_gateway_type = MagicMock(return_value=gateway_type) 132 mock_component.get_config.side_effect = lambda key, default=None: { 133 "gateway_type": gateway_type, 134 "deployment": deployment, 135 }.get(key, default) 136 return mock_component 137 138 def test_builds_valid_gateway_card(self): 139 """Test that _build_gateway_card creates a valid AgentCard.""" 140 mock_component = self._create_mock_component( 141 gateway_id="test-gateway-001", 142 namespace="test/namespace", 143 gateway_type="http_sse" 144 ) 145 146 from solace_agent_mesh.gateway.base.component import BaseGatewayComponent 147 148 card = BaseGatewayComponent._build_gateway_card(mock_component) 149 150 assert isinstance(card, AgentCard) 151 assert card.name == "test-gateway-001" 152 assert "test/namespace" in card.url 153 assert card.url == "solace:test/namespace/a2a/v1/gateway/request/test-gateway-001" 154 155 def test_gateway_card_has_gateway_role_extension(self): 156 """Test that built card includes gateway-role extension.""" 157 mock_component = self._create_mock_component( 158 gateway_id="my-gateway", 159 namespace="prod/sam", 160 gateway_type="http_sse" 161 ) 162 163 from solace_agent_mesh.gateway.base.component import BaseGatewayComponent 164 165 card = BaseGatewayComponent._build_gateway_card(mock_component) 166 167 assert is_gateway_card(card) is True 168 169 info = extract_gateway_info(card) 170 assert info is not None 171 assert info["gateway_id"] == "my-gateway" 172 assert info["gateway_type"] == "http_sse" 173 assert info["namespace"] == "prod/sam" 174 175 def test_gateway_card_includes_deployment_extension_when_configured(self): 176 """Test that deployment extension is added when deployment.id is configured.""" 177 mock_component = self._create_mock_component( 178 gateway_id="deployed-gateway", 179 namespace="test/sam", 180 gateway_type="http_sse", 181 deployment={"id": "k8s-pod-abc123"} 182 ) 183 184 from solace_agent_mesh.gateway.base.component import BaseGatewayComponent 185 186 card = BaseGatewayComponent._build_gateway_card(mock_component) 187 188 info = extract_gateway_info(card) 189 assert info is not None 190 assert info.get("deployment_id") == "k8s-pod-abc123" 191 192 def test_gateway_card_uses_custom_description(self): 193 """Test that custom description from config is used.""" 194 mock_component = self._create_mock_component( 195 gateway_id="custom-gw", 196 namespace="test/sam", 197 gateway_type="http_sse", 198 card_config={"description": "My Custom Gateway Description"} 199 ) 200 201 from solace_agent_mesh.gateway.base.component import BaseGatewayComponent 202 203 card = BaseGatewayComponent._build_gateway_card(mock_component) 204 205 assert card.description == "My Custom Gateway Description" 206 207 def test_gateway_card_uses_default_description(self): 208 """Test that default description is generated from gateway type.""" 209 mock_component = self._create_mock_component( 210 gateway_id="default-gw", 211 namespace="test/sam", 212 gateway_type="slack" 213 ) 214 215 from solace_agent_mesh.gateway.base.component import BaseGatewayComponent 216 217 card = BaseGatewayComponent._build_gateway_card(mock_component) 218 219 assert card.description == "SLACK Gateway" 220 221 def test_gateway_card_has_capabilities(self): 222 """Test that gateway card has capabilities set.""" 223 mock_component = self._create_mock_component( 224 gateway_id="capabilities-gw", 225 namespace="test/sam", 226 gateway_type="http_sse" 227 ) 228 229 from solace_agent_mesh.gateway.base.component import BaseGatewayComponent 230 231 card = BaseGatewayComponent._build_gateway_card(mock_component) 232 233 assert card.capabilities is not None 234 assert card.capabilities.extensions is not None 235 assert len(card.capabilities.extensions) >= 1 236 237 def test_gateway_card_uses_custom_input_output_modes(self): 238 """Test that custom input/output modes are used from config.""" 239 mock_component = self._create_mock_component( 240 gateway_id="custom-modes-gw", 241 namespace="test/sam", 242 gateway_type="http_sse", 243 card_config={ 244 "defaultInputModes": ["text", "image"], 245 "defaultOutputModes": ["text", "markdown"], 246 } 247 ) 248 249 from solace_agent_mesh.gateway.base.component import BaseGatewayComponent 250 251 card = BaseGatewayComponent._build_gateway_card(mock_component) 252 253 assert card.default_input_modes == ["text", "image"] 254 assert card.default_output_modes == ["text", "markdown"] 255 256 257 class TestGatewayCardIntegration: 258 """Integration tests ensuring built cards work with registry and utils.""" 259 260 def _create_mock_component(self, gateway_id, namespace, gateway_type, deployment=None, card_config=None): 261 """Helper to create properly configured mock component.""" 262 mock_component = MagicMock() 263 mock_component.gateway_id = gateway_id 264 mock_component.namespace = namespace 265 mock_component._gateway_card_config = card_config or {} 266 mock_component._detect_gateway_type = MagicMock(return_value=gateway_type) 267 mock_component.get_config.side_effect = lambda key, default=None: { 268 "gateway_type": gateway_type, 269 "deployment": deployment, 270 }.get(key, default) 271 return mock_component 272 273 def test_built_card_can_be_added_to_registry(self): 274 """Test that built gateway card can be stored in GatewayRegistry.""" 275 from solace_agent_mesh.common.gateway_registry import GatewayRegistry 276 277 mock_component = self._create_mock_component( 278 gateway_id="registry-test-gw", 279 namespace="test/sam", 280 gateway_type="http_sse", 281 deployment={"id": "pod-123"} 282 ) 283 284 from solace_agent_mesh.gateway.base.component import BaseGatewayComponent 285 286 card = BaseGatewayComponent._build_gateway_card(mock_component) 287 288 registry = GatewayRegistry() 289 is_new = registry.add_or_update_gateway(card) 290 291 assert is_new is True 292 assert "registry-test-gw" in registry.get_gateway_ids() 293 294 stored_card = registry.get_gateway("registry-test-gw") 295 assert stored_card.name == card.name 296 assert registry.get_gateway_type("registry-test-gw") == "http_sse" 297 assert registry.get_deployment_id("registry-test-gw") == "pod-123" 298 299 def test_built_card_is_identified_as_gateway_card(self): 300 """Test that is_gateway_card() correctly identifies built cards.""" 301 mock_component = self._create_mock_component( 302 gateway_id="identification-test-gw", 303 namespace="test/sam", 304 gateway_type="rest" 305 ) 306 307 from solace_agent_mesh.gateway.base.component import BaseGatewayComponent 308 309 card = BaseGatewayComponent._build_gateway_card(mock_component) 310 311 assert is_gateway_card(card) is True 312 313 def test_built_card_metadata_extraction(self): 314 """Test that extract_gateway_info() extracts correct metadata.""" 315 mock_component = self._create_mock_component( 316 gateway_id="metadata-test-gw", 317 namespace="production/sam", 318 gateway_type="teams", 319 deployment={"id": "aks-deployment-xyz"} 320 ) 321 322 from solace_agent_mesh.gateway.base.component import BaseGatewayComponent 323 324 card = BaseGatewayComponent._build_gateway_card(mock_component) 325 326 info = extract_gateway_info(card) 327 328 assert info["gateway_id"] == "metadata-test-gw" 329 assert info["gateway_type"] == "teams" 330 assert info["namespace"] == "production/sam" 331 assert info["deployment_id"] == "aks-deployment-xyz"