pygeai 0.2.7b51__py3-none-any.whl → 0.2.7b53__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,304 @@
1
+ import unittest
2
+ from unittest.mock import Mock, patch, MagicMock, AsyncMock
3
+ import asyncio
4
+ import uuid
5
+ import json
6
+ import sys
7
+ import os
8
+ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../../..')))
9
+ from pygeai.proxy.config import ProxySettingsManager
10
+ from pygeai.proxy.tool import ProxiedTool
11
+ from pygeai.proxy.clients import ProxyClient, ToolProxyData, ToolProxyJob, ToolProxyJobResult
12
+ from pygeai.proxy.managers import ServerManager
13
+ from pygeai.proxy.servers import A2AServer, MCPServer
14
+ from types import SimpleNamespace
15
+
16
+
17
+ class TestProxyIntegration(unittest.IsolatedAsyncioTestCase):
18
+ """
19
+ python -m unittest pygeai.tests.proxy.test_integration.TestProxyIntegration
20
+ """
21
+
22
+ def setUp(self):
23
+ """Set up test fixtures."""
24
+ self.settings = ProxySettingsManager()
25
+ self.test_uuid = uuid.uuid4()
26
+ self.test_affinity = uuid.uuid4()
27
+
28
+ def test_tool_to_proxy_data_integration(self):
29
+ """Test integration between ProxiedTool and ToolProxyData."""
30
+ # Create a tool
31
+ tool = ProxiedTool(
32
+ server_name="test_server",
33
+ name="test_tool",
34
+ description="Test tool description",
35
+ public_prefix="public.prefix",
36
+ input_schema={
37
+ "type": "object",
38
+ "properties": {
39
+ "param1": {"type": "string"},
40
+ "param2": {"type": "number"}
41
+ }
42
+ }
43
+ )
44
+
45
+ # Create proxy data with the tool
46
+ proxy_data = ToolProxyData(
47
+ id=self.test_uuid,
48
+ name="Test Proxy",
49
+ description="Test Description",
50
+ affinity=self.test_affinity,
51
+ tools=[tool]
52
+ )
53
+
54
+ # Convert to dictionary
55
+ data_dict = proxy_data.to_dict()
56
+
57
+ # Verify the tool data is correctly included
58
+ self.assertEqual(len(data_dict["tools"]), 1)
59
+ tool_dict = data_dict["tools"][0]
60
+
61
+ self.assertEqual(tool_dict["name"], "test_server__test_tool")
62
+ self.assertEqual(tool_dict["publicName"], "public.prefix.test_server__test_tool")
63
+ self.assertEqual(tool_dict["server"], "test_server")
64
+ self.assertEqual(tool_dict["description"], "Test tool description")
65
+
66
+ # Verify the input schema is correctly formatted
67
+ input_schema = json.loads(tool_dict["inputSchema"])
68
+ self.assertEqual(input_schema["function"]["parameters"]["type"], "object")
69
+
70
+ def test_job_processing_integration(self):
71
+ """Test integration between job creation and result processing."""
72
+ # Create a job
73
+ job = ToolProxyJob(
74
+ id=uuid.uuid4(),
75
+ proxy_id=self.test_uuid,
76
+ proxy_status="active",
77
+ job_status="pending",
78
+ input=json.dumps({
79
+ "function": {
80
+ "name": "test_server__test_tool",
81
+ "arguments": '{"param1": "value1", "param2": 42}'
82
+ }
83
+ }),
84
+ server="test_server"
85
+ )
86
+
87
+ # Simulate job execution
88
+ job.job_status = "completed"
89
+ job.output = "Execution completed successfully"
90
+
91
+ # Create result
92
+ result = ToolProxyJobResult(success=True, job=job)
93
+ result_dict = result.to_dict()
94
+
95
+ # Verify result structure
96
+ self.assertTrue(result_dict["success"])
97
+ self.assertEqual(result_dict["job"]["jobStatus"], "completed")
98
+ self.assertEqual(result_dict["job"]["output"], "Execution completed successfully")
99
+
100
+ @patch('pygeai.proxy.managers.ProxyClient')
101
+ async def test_server_manager_integration(self, mock_client_class):
102
+ """Test integration between ServerManager and its components."""
103
+ # Configure settings
104
+ self.settings.set_proxy_id(self.test_uuid)
105
+ self.settings.set_proxy_name("Test Proxy")
106
+ self.settings.set_proxy_description("Test Description")
107
+ self.settings.set_proxy_affinity(self.test_affinity)
108
+
109
+ # Mock settings methods
110
+ self.settings.get_current_alias = Mock(return_value="default")
111
+ self.settings.get_api_key = Mock(return_value="test_api_key")
112
+ self.settings.get_base_url = Mock(return_value="https://api.example.com")
113
+
114
+ # Create server configuration
115
+ servers_cfg = [
116
+ {
117
+ "name": "test_mcp_server",
118
+ "type": "mcp",
119
+ "command": "test_command",
120
+ "args": ["arg1", "arg2"]
121
+ }
122
+ ]
123
+
124
+ # Create manager
125
+ manager = ServerManager(servers_cfg, self.settings)
126
+
127
+ # Add a mock server and tool
128
+ tool = ProxiedTool(
129
+ server_name="test_mcp_server",
130
+ name="test_tool",
131
+ description="Test tool",
132
+ public_prefix="public.prefix",
133
+ input_schema={"type": "object"}
134
+ )
135
+ manager.tools["test_mcp_server__test_tool"] = tool
136
+
137
+ # Mock client
138
+ mock_client = Mock()
139
+ mock_client.register.return_value = {"status": "registered"}
140
+ mock_client_class.return_value = mock_client
141
+
142
+ # Add a mock server and tool
143
+ mock_server = AsyncMock()
144
+ mock_server.name = "test_mcp_server"
145
+ mock_server.exit_stack = AsyncMock()
146
+ mock_server.list_tools.return_value = []
147
+
148
+ manager.servers["test_mcp_server"] = mock_server
149
+
150
+ # Test client initialization
151
+ client = await manager._initialize_client()
152
+
153
+ # Verifica que el mock fue llamado
154
+ mock_client.register.assert_called_once()
155
+ self.assertEqual(client, mock_client)
156
+
157
+ # Verify registration data - get the actual arguments passed to register
158
+ call_args = mock_client.register.call_args
159
+ self.assertIsNotNone(call_args)
160
+
161
+ # The register method is called with keyword arguments, so we need to access them properly
162
+ proxy_data = call_args.kwargs.get('proxy_data')
163
+ self.assertIsNotNone(proxy_data)
164
+ self.assertEqual(proxy_data.id, self.test_uuid)
165
+ self.assertEqual(proxy_data.name, "Test Proxy")
166
+ self.assertEqual(proxy_data.description, "Test Description")
167
+ self.assertEqual(proxy_data.affinity, self.test_affinity)
168
+ self.assertEqual(len(proxy_data.tools), 1)
169
+
170
+ @patch('pygeai.proxy.managers.Console')
171
+ async def test_tool_execution_integration(self, mock_console):
172
+ """Test integration of tool execution flow."""
173
+ # Create manager
174
+ servers_cfg = []
175
+ manager = ServerManager(servers_cfg, self.settings)
176
+
177
+ # Add mock server
178
+ mock_server = AsyncMock()
179
+ mock_server.execute_tool.return_value = "execution_result"
180
+ manager.servers["test_server"] = mock_server
181
+
182
+ # Add tool
183
+ tool = ProxiedTool(
184
+ server_name="test_server",
185
+ name="test_tool",
186
+ description="Test tool",
187
+ public_prefix="public.prefix",
188
+ input_schema={"type": "object"}
189
+ )
190
+ manager.tools["test_server__test_tool"] = tool
191
+
192
+ # Test tool execution
193
+ result = await manager.execute_tool("test_server", "test_server__test_tool", {"param": "value"})
194
+
195
+ # Verify execution
196
+ self.assertEqual(result, "execution_result")
197
+ mock_server.execute_tool.assert_called_once_with("test_tool", {"param": "value"}, 2, 1.0)
198
+
199
+ def test_function_call_parsing_integration(self):
200
+ """Test integration of function call parsing."""
201
+ # Create manager
202
+ servers_cfg = []
203
+ manager = ServerManager(servers_cfg, self.settings)
204
+
205
+ # Test function call parsing
206
+ function_call_json = json.dumps({
207
+ "function": {
208
+ "name": "test_server__test_tool",
209
+ "arguments": '{"param1": "value1", "param2": 42}'
210
+ }
211
+ })
212
+
213
+ name, arguments = manager.extract_function_call_info(function_call_json)
214
+
215
+ # Verify parsing
216
+ self.assertEqual(name, "test_server__test_tool")
217
+ self.assertEqual(arguments, '{"param1": "value1", "param2": 42}')
218
+
219
+ # Test with parsed arguments
220
+ parsed_args = json.loads(arguments)
221
+ self.assertEqual(parsed_args["param1"], "value1")
222
+ self.assertEqual(parsed_args["param2"], 42)
223
+
224
+ @patch('pygeai.proxy.clients.requests.Session')
225
+ def test_client_request_integration(self, mock_session):
226
+ """Test integration of client request handling."""
227
+ # Create client
228
+ client = ProxyClient("test_api_key", "https://api.example.com", self.test_uuid)
229
+
230
+ # Mock response
231
+ mock_response = Mock()
232
+ mock_response.json.return_value = {"status": "success", "data": "test_data"}
233
+ mock_response.raise_for_status.return_value = None
234
+ mock_session.return_value.request.return_value = mock_response
235
+
236
+ # Test request
237
+ result = client._make_request("GET", "/test")
238
+
239
+ # Verify request
240
+ self.assertEqual(result, {"status": "success", "data": "test_data"})
241
+ mock_session.return_value.request.assert_called_once()
242
+
243
+ def test_settings_integration(self):
244
+ """Test integration of settings management."""
245
+ # Set various settings
246
+ self.settings.set_proxy_id(self.test_uuid)
247
+ self.settings.set_proxy_name("Test Proxy")
248
+ self.settings.set_proxy_description("Test Description")
249
+ self.settings.set_proxy_affinity(self.test_affinity)
250
+
251
+ # Get complete configuration
252
+ config = self.settings.get_proxy_config()
253
+
254
+ # Verify configuration
255
+ expected = {
256
+ "id": str(self.test_uuid),
257
+ "name": "Test Proxy",
258
+ "description": "Test Description",
259
+ "affinity": str(self.test_affinity)
260
+ }
261
+ self.assertEqual(config, expected)
262
+
263
+ def test_tool_formatting_integration(self):
264
+ """Test integration of tool formatting for different scenarios."""
265
+ # Test public tool
266
+ public_tool = ProxiedTool(
267
+ server_name="test_server",
268
+ name="public_tool",
269
+ description="Public tool description",
270
+ public_prefix="public.prefix",
271
+ input_schema={"type": "object"}
272
+ )
273
+
274
+ # Test private tool
275
+ private_tool = ProxiedTool(
276
+ server_name="test_server",
277
+ name="private_tool",
278
+ description="Private tool description",
279
+ public_prefix=None,
280
+ input_schema={"type": "object"}
281
+ )
282
+
283
+ # Create proxy data with both tools
284
+ proxy_data = ToolProxyData(
285
+ id=self.test_uuid,
286
+ name="Test Proxy",
287
+ tools=[public_tool, private_tool]
288
+ )
289
+
290
+ # Convert to dictionary
291
+ data_dict = proxy_data.to_dict()
292
+
293
+ # Verify public tool has publicName
294
+ public_tool_dict = data_dict["tools"][0]
295
+ self.assertIn("publicName", public_tool_dict)
296
+ self.assertEqual(public_tool_dict["publicName"], "public.prefix.test_server__public_tool")
297
+
298
+ # Verify private tool doesn't have publicName
299
+ private_tool_dict = data_dict["tools"][1]
300
+ self.assertNotIn("publicName", private_tool_dict)
301
+
302
+
303
+ if __name__ == '__main__':
304
+ unittest.main()
@@ -0,0 +1,312 @@
1
+ import unittest
2
+ from unittest.mock import Mock, patch, MagicMock, AsyncMock
3
+ import asyncio
4
+ import json
5
+ import uuid
6
+ from pygeai.proxy.managers import ServerManager
7
+ from pygeai.proxy.config import ProxySettingsManager
8
+ from pygeai.proxy.clients import ProxyClient, ToolProxyData, ToolProxyJobResult
9
+ from pygeai.proxy.tool import ProxiedTool
10
+
11
+
12
+ class TestServerManager(unittest.IsolatedAsyncioTestCase):
13
+ """
14
+ python -m unittest pygeai.tests.proxy.test_managers.TestServerManager
15
+ """
16
+
17
+ def setUp(self):
18
+ """Set up test fixtures."""
19
+ self.servers_cfg = [
20
+ {
21
+ "name": "test_mcp_server",
22
+ "type": "mcp",
23
+ "command": "test_command",
24
+ "args": ["arg1", "arg2"]
25
+ },
26
+ {
27
+ "name": "test_a2a_server",
28
+ "type": "a2a",
29
+ "url": "https://test.a2a.com",
30
+ "headers": {"Authorization": "Bearer token"}
31
+ }
32
+ ]
33
+ self.settings = ProxySettingsManager()
34
+ self.manager = ServerManager(self.servers_cfg, self.settings)
35
+
36
+ def test_initialization(self):
37
+ """Test manager initialization."""
38
+ self.assertEqual(self.manager.servers_cfg, self.servers_cfg)
39
+ self.assertEqual(self.manager.settings, self.settings)
40
+ self.assertEqual(self.manager.servers, {})
41
+ self.assertEqual(self.manager.tools, {})
42
+
43
+ @patch('pygeai.proxy.managers.MCPServer')
44
+ @patch('pygeai.proxy.managers.A2AServer')
45
+ @patch('pygeai.proxy.managers.Console')
46
+ async def test_initialize_servers_success(self, mock_console, mock_a2a_server_class, mock_mcp_server_class):
47
+ """Test successful server initialization."""
48
+ # Mock server instances
49
+ mock_mcp_server = AsyncMock()
50
+ mock_mcp_server.name = "test_mcp_server"
51
+ mock_mcp_server.exit_stack = AsyncMock()
52
+ mock_mcp_server.list_tools.return_value = []
53
+
54
+ mock_a2a_server = AsyncMock()
55
+ mock_a2a_server.name = "test_a2a_server"
56
+ mock_a2a_server.exit_stack = AsyncMock()
57
+ mock_a2a_server.list_tools.return_value = []
58
+
59
+ mock_mcp_server_class.return_value = mock_mcp_server
60
+ mock_a2a_server_class.return_value = mock_a2a_server
61
+
62
+ await self.manager._initialize_servers()
63
+
64
+ # Verify servers were created and initialized
65
+ mock_mcp_server_class.assert_called_once_with(
66
+ "test_mcp_server", self.servers_cfg[0], self.settings
67
+ )
68
+ mock_a2a_server_class.assert_called_once_with(
69
+ "test_a2a_server", self.servers_cfg[1], self.settings
70
+ )
71
+
72
+ mock_mcp_server.initialize.assert_called_once()
73
+ mock_a2a_server.initialize.assert_called_once()
74
+
75
+ @patch('pygeai.proxy.managers.MCPServer')
76
+ @patch('pygeai.proxy.managers.Console')
77
+ async def test_initialize_servers_invalid_type(self, mock_console, mock_mcp_server_class):
78
+ """Test server initialization with invalid server type."""
79
+ invalid_config = [{"name": "invalid_server", "type": "invalid_type"}]
80
+ manager = ServerManager(invalid_config, self.settings)
81
+
82
+ with self.assertRaises(ValueError, msg="Invalid server type: invalid_type"):
83
+ await manager._initialize_servers()
84
+
85
+ @patch('pygeai.proxy.managers.MCPServer')
86
+ @patch('pygeai.proxy.managers.Console')
87
+ async def test_initialize_servers_initialization_error(self, mock_console, mock_mcp_server_class):
88
+ """Test server initialization error."""
89
+ mock_server = AsyncMock()
90
+ mock_server.name = "test_server"
91
+ mock_server.initialize.side_effect = Exception("Initialization failed")
92
+ mock_mcp_server_class.return_value = mock_server
93
+
94
+ with self.assertRaises(Exception):
95
+ await self.manager._initialize_servers()
96
+
97
+ @patch('pygeai.proxy.managers.ProxyClient')
98
+ @patch('pygeai.proxy.managers.Console')
99
+ async def test_initialize_client_success(self, mock_console, mock_client_class):
100
+ """Test successful client initialization."""
101
+ # Mock settings methods
102
+ self.settings.get_current_alias = Mock(return_value="default")
103
+ self.settings.get_api_key = Mock(return_value="test_api_key")
104
+ self.settings.get_base_url = Mock(return_value="https://api.example.com")
105
+ self.settings.get_proxy_id = Mock(return_value=uuid.uuid4())
106
+ self.settings.get_proxy_name = Mock(return_value="Test Proxy")
107
+ self.settings.get_proxy_description = Mock(return_value="Test Description")
108
+ self.settings.get_proxy_affinity = Mock(return_value=uuid.uuid4())
109
+
110
+ # Mock client
111
+ mock_client = Mock()
112
+ mock_client.register.return_value = {"status": "success"}
113
+ mock_client_class.return_value = mock_client
114
+
115
+ # Add some tools to the manager
116
+ tool = ProxiedTool(
117
+ server_name="test_server",
118
+ name="test_tool",
119
+ description="Test tool",
120
+ public_prefix="public.prefix",
121
+ input_schema={"type": "object"}
122
+ )
123
+ self.manager.tools["test_server__test_tool"] = tool
124
+
125
+ result = await self.manager._initialize_client()
126
+
127
+ self.assertEqual(result, mock_client)
128
+ mock_client.register.assert_called_once()
129
+
130
+ @patch('pygeai.proxy.managers.ProxyClient')
131
+ @patch('pygeai.proxy.managers.Console')
132
+ async def test_initialize_client_connection_error(self, mock_console, mock_client_class):
133
+ """Test client initialization with connection error."""
134
+ # Mock settings methods
135
+ self.settings.get_current_alias = Mock(return_value="default")
136
+ self.settings.get_api_key = Mock(return_value="test_api_key")
137
+ self.settings.get_base_url = Mock(return_value="https://api.example.com")
138
+ self.settings.get_proxy_id = Mock(return_value=uuid.uuid4())
139
+
140
+ # Mock client to raise connection error
141
+ mock_client = Mock()
142
+ mock_client.register.side_effect = ConnectionError("Connection failed")
143
+ mock_client_class.return_value = mock_client
144
+
145
+ with self.assertRaises(ConnectionError):
146
+ await self.manager._initialize_client()
147
+
148
+ async def test_execute_tool_server_not_found(self):
149
+ """Test executing tool with non-existent server."""
150
+ with self.assertRaises(RuntimeError, msg="Server non_existent not found"):
151
+ await self.manager.execute_tool("non_existent", "test_tool", {})
152
+
153
+ async def test_execute_tool_tool_not_found(self):
154
+ """Test executing non-existent tool."""
155
+ # Add a server but no tools
156
+ self.manager.servers["test_server"] = Mock()
157
+
158
+ with self.assertRaises(RuntimeError, msg="Tool non_existent not found"):
159
+ await self.manager.execute_tool("test_server", "non_existent", {})
160
+
161
+ @patch('pygeai.proxy.managers.Console')
162
+ async def test_execute_tool_success(self, mock_console):
163
+ """Test successful tool execution."""
164
+ # Mock server
165
+ mock_server = AsyncMock()
166
+ mock_server.execute_tool.return_value = "success"
167
+ self.manager.servers["test_server"] = mock_server
168
+
169
+ # Add tool
170
+ tool = ProxiedTool(
171
+ server_name="test_server",
172
+ name="test_tool",
173
+ description="Test tool",
174
+ public_prefix="public.prefix",
175
+ input_schema={"type": "object"}
176
+ )
177
+ self.manager.tools["test_server__test_tool"] = tool
178
+
179
+ result = await self.manager.execute_tool("test_server", "test_server__test_tool", {"param": "value"})
180
+
181
+ self.assertEqual(result, "success")
182
+ mock_server.execute_tool.assert_called_once_with("test_tool", {"param": "value"}, 2, 1.0)
183
+
184
+ @patch('pygeai.proxy.managers.Console')
185
+ async def test_execute_tool_with_custom_retries(self, mock_console):
186
+ """Test tool execution with custom retry parameters."""
187
+ mock_server = AsyncMock()
188
+ mock_server.execute_tool.return_value = "success"
189
+ self.manager.servers["test_server"] = mock_server
190
+
191
+ tool = ProxiedTool(
192
+ server_name="test_server",
193
+ name="test_tool",
194
+ description="Test tool",
195
+ public_prefix="public.prefix",
196
+ input_schema={"type": "object"}
197
+ )
198
+ self.manager.tools["test_server__test_tool"] = tool
199
+
200
+ await self.manager.execute_tool("test_server", "test_server__test_tool", {}, retries=5, delay=2.0)
201
+
202
+ mock_server.execute_tool.assert_called_once_with("test_tool", {}, 5, 2.0)
203
+
204
+ @patch('pygeai.proxy.managers.Console')
205
+ async def test_execute_tool_execution_error(self, mock_console):
206
+ """Test tool execution with error."""
207
+ mock_server = AsyncMock()
208
+ mock_server.execute_tool.side_effect = RuntimeError("Tool execution failed")
209
+ self.manager.servers["test_server"] = mock_server
210
+
211
+ tool = ProxiedTool(
212
+ server_name="test_server",
213
+ name="test_tool",
214
+ description="Test tool",
215
+ public_prefix="public.prefix",
216
+ input_schema={"type": "object"}
217
+ )
218
+ self.manager.tools["test_server__test_tool"] = tool
219
+
220
+ with self.assertRaises(Exception, msg="Failed to execute tool test_server__test_tool on server test_server: Tool execution failed"):
221
+ await self.manager.execute_tool("test_server", "test_server__test_tool", {})
222
+
223
+ def test_extract_function_call_info_success(self):
224
+ """Test successful function call info extraction."""
225
+ function_call_json = json.dumps({
226
+ "function": {
227
+ "name": "test_function",
228
+ "arguments": '{"param": "value"}'
229
+ }
230
+ })
231
+
232
+ name, arguments = self.manager.extract_function_call_info(function_call_json)
233
+
234
+ self.assertEqual(name, "test_function")
235
+ self.assertEqual(arguments, '{"param": "value"}')
236
+
237
+ def test_extract_function_call_info_invalid_json(self):
238
+ """Test function call info extraction with invalid JSON."""
239
+ name, arguments = self.manager.extract_function_call_info("invalid json")
240
+
241
+ self.assertIsNone(name)
242
+ self.assertIsNone(arguments)
243
+
244
+ def test_extract_function_call_info_missing_keys(self):
245
+ """Test function call info extraction with missing keys."""
246
+ function_call_json = json.dumps({"other_key": "value"})
247
+
248
+ name, arguments = self.manager.extract_function_call_info(function_call_json)
249
+
250
+ self.assertIsNone(name)
251
+ self.assertIsNone(arguments)
252
+
253
+ @patch('pygeai.proxy.managers.ServerManager._initialize_servers')
254
+ @patch('pygeai.proxy.managers.ServerManager._initialize_client')
255
+ @patch('pygeai.proxy.managers.Console')
256
+ async def test_start_success(self, mock_console, mock_init_client, mock_init_servers):
257
+ """Test successful start method."""
258
+ # Mock client
259
+ mock_client = Mock()
260
+ mock_client.dequeue.return_value = []
261
+ mock_init_client.return_value = mock_client
262
+
263
+ # Mock settings
264
+ self.settings.get_current_alias = Mock(return_value="default")
265
+ self.settings.get_proxy_id = Mock(return_value=uuid.uuid4())
266
+
267
+ # Create a task to run start method
268
+ task = asyncio.create_task(self.manager.start())
269
+
270
+ # Let it run for a short time
271
+ await asyncio.sleep(0.1)
272
+
273
+ # Cancel the task
274
+ task.cancel()
275
+
276
+ try:
277
+ await task
278
+ except asyncio.CancelledError:
279
+ pass
280
+
281
+ # Verify initialization was called
282
+ mock_init_servers.assert_called_once()
283
+ mock_init_client.assert_called_once()
284
+
285
+ @patch('pygeai.proxy.managers.ServerManager._initialize_servers')
286
+ @patch('pygeai.proxy.managers.ServerManager._initialize_client')
287
+ @patch('pygeai.proxy.managers.Console')
288
+ async def test_start_client_initialization_error(self, mock_console, mock_init_client, mock_init_servers):
289
+ """Test start method with client initialization error."""
290
+ mock_init_client.side_effect = ConnectionError("Connection failed")
291
+
292
+ # Create a task to run start method
293
+ task = asyncio.create_task(self.manager.start())
294
+
295
+ # Let it run for a short time
296
+ await asyncio.sleep(0.1)
297
+
298
+ # Cancel the task
299
+ task.cancel()
300
+
301
+ try:
302
+ await task
303
+ except asyncio.CancelledError:
304
+ pass
305
+
306
+ # Verify initialization was called
307
+ mock_init_servers.assert_called_once()
308
+ mock_init_client.assert_called_once()
309
+
310
+
311
+ if __name__ == '__main__':
312
+ unittest.main()