|
5 | 5 | import pytest |
6 | 6 |
|
7 | 7 | from google.protobuf import json_format |
| 8 | +from google.protobuf.timestamp_pb2 import Timestamp |
8 | 9 | from httpx_sse import EventSource, ServerSentEvent |
9 | 10 |
|
10 | 11 | from a2a.client import create_text_message_object |
|
16 | 17 | AgentCard, |
17 | 18 | AgentInterface, |
18 | 19 | CancelTaskRequest, |
19 | | - TaskPushNotificationConfig, |
20 | 20 | DeleteTaskPushNotificationConfigRequest, |
21 | 21 | GetExtendedAgentCardRequest, |
22 | 22 | GetTaskPushNotificationConfigRequest, |
23 | 23 | GetTaskRequest, |
24 | 24 | ListTaskPushNotificationConfigsRequest, |
25 | 25 | ListTasksRequest, |
26 | | - Message, |
27 | 26 | SendMessageRequest, |
28 | 27 | SubscribeToTaskRequest, |
| 28 | + TaskPushNotificationConfig, |
| 29 | + TaskState, |
29 | 30 | ) |
30 | 31 | from a2a.utils.constants import TransportProtocol |
31 | 32 | from a2a.utils.errors import JSON_RPC_ERROR_CODE_MAP |
@@ -175,6 +176,47 @@ async def test_send_message_with_timeout_context( |
175 | 176 | assert 'timeout' in kwargs |
176 | 177 | assert kwargs['timeout'] == httpx.Timeout(10.0) |
177 | 178 |
|
| 179 | + @pytest.mark.asyncio |
| 180 | + async def test_url_serialization( |
| 181 | + self, mock_httpx_client: AsyncMock, mock_agent_card: MagicMock |
| 182 | + ): |
| 183 | + """Test that query parameters are correctly serialized to the URL.""" |
| 184 | + client = RestTransport( |
| 185 | + httpx_client=mock_httpx_client, |
| 186 | + agent_card=mock_agent_card, |
| 187 | + url='http://agent.example.com/api', |
| 188 | + ) |
| 189 | + |
| 190 | + timestamp = Timestamp() |
| 191 | + timestamp.FromJsonString('2024-03-09T16:00:00Z') |
| 192 | + |
| 193 | + request = ListTasksRequest( |
| 194 | + tenant='my-tenant', |
| 195 | + status=TaskState.TASK_STATE_WORKING, |
| 196 | + include_artifacts=True, |
| 197 | + status_timestamp_after=timestamp, |
| 198 | + ) |
| 199 | + |
| 200 | + # Use real build_request to get actual URL serialization |
| 201 | + mock_httpx_client.build_request.side_effect = ( |
| 202 | + httpx.AsyncClient().build_request |
| 203 | + ) |
| 204 | + mock_httpx_client.send.return_value = AsyncMock( |
| 205 | + spec=httpx.Response, status_code=200, json=lambda: {'tasks': []} |
| 206 | + ) |
| 207 | + |
| 208 | + await client.list_tasks(request=request) |
| 209 | + |
| 210 | + mock_httpx_client.send.assert_called_once() |
| 211 | + sent_request = mock_httpx_client.send.call_args[0][0] |
| 212 | + |
| 213 | + # Check decoded query parameters for spec compliance |
| 214 | + params = sent_request.url.params |
| 215 | + assert params['status'] == 'TASK_STATE_WORKING' |
| 216 | + assert params['includeArtifacts'] == 'true' |
| 217 | + assert params['statusTimestampAfter'] == '2024-03-09T16:00:00Z' |
| 218 | + assert 'tenant' not in params |
| 219 | + |
178 | 220 |
|
179 | 221 | class TestRestTransportExtensions: |
180 | 222 | @pytest.mark.asyncio |
@@ -616,7 +658,7 @@ async def test_rest_get_task_prepend_empty_tenant( |
616 | 658 |
|
617 | 659 | # 3. Verify the URL |
618 | 660 | args, _ = mock_httpx_client.build_request.call_args |
619 | | - assert args[1] == f'http://agent.example.com/api/tasks/task-123' |
| 661 | + assert args[1] == 'http://agent.example.com/api/tasks/task-123' |
620 | 662 |
|
621 | 663 | @pytest.mark.parametrize( |
622 | 664 | 'method_name, request_obj, expected_path', |
|
0 commit comments