/ tests / unit / gateway / base / test_gateway_card_building.py
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"