|
38 | 38 | from a2a.types import ( |
39 | 39 | InternalError, |
40 | 40 | InvalidParamsError, |
| 41 | + PushNotificationNotSupportedError, |
41 | 42 | TaskNotCancelableError, |
42 | 43 | TaskNotFoundError, |
43 | 44 | UnsupportedOperationError, |
@@ -1240,168 +1241,12 @@ def sync_get_event_stream_gen(*args, **kwargs): |
1240 | 1241 | side_effect=sync_get_event_stream_gen |
1241 | 1242 | ) |
1242 | 1243 |
|
1243 | | - # Mock current_result property to return appropriate awaitables |
1244 | | - # Coroutines that will be returned by successive accesses to current_result |
1245 | | - async def current_result_coro1(): |
1246 | | - return event1_task_update |
1247 | | - |
1248 | | - async def current_result_coro2(): |
1249 | | - return event2_final_task |
1250 | | - |
1251 | | - # Use unittest.mock.PropertyMock for async property |
1252 | | - # We need to patch 'ResultAggregator.current_result' when this instance is used. |
1253 | | - # This is complex because ResultAggregator is instantiated inside the handler. |
1254 | | - # Easier: If mock_result_aggregator_instance is a MagicMock, we can assign a callable. |
1255 | | - # This part is tricky. Let's assume current_result is an async method for easier mocking first. |
1256 | | - # If it's truly a property, the mocking is harder with instance mocks. |
1257 | | - # Let's adjust the mock_result_aggregator_instance.current_result to be an AsyncMock directly |
1258 | | - # This means the code would call `await result_aggregator.current_result()` |
1259 | | - # But the actual code is `await result_aggregator.current_result` |
1260 | | - # This implies `result_aggregator.current_result` IS an awaitable. |
1261 | | - # So, we can mock it with a side_effect that returns awaitables (coroutines). |
1262 | | - |
1263 | | - # Create simple awaitables (coroutines) for side_effect |
1264 | | - async def get_event1(): |
1265 | | - return event1_task_update |
1266 | | - |
1267 | | - async def get_event2(): |
1268 | | - return event2_final_task |
1269 | | - |
1270 | | - # Make the current_result attribute of the mock instance itself an awaitable |
1271 | | - # This still means current_result is not callable. |
1272 | | - # For an async property, the mock needs to have current_result as a non-AsyncMock attribute |
1273 | | - # that is itself an awaitable. |
1274 | | - |
1275 | | - # Let's try to mock the property at the type level for ResultAggregator temporarily |
1276 | | - # This is not ideal as it affects all instances. |
1277 | | - |
1278 | | - # Alternative: Configure the AsyncMock for current_result to return a coroutine |
1279 | | - # when it's awaited. This is not directly supported by AsyncMock for property access. |
1280 | | - |
1281 | | - # Simplest for now: Assume `current_result` attribute of the mocked `ResultAggregator` instance |
1282 | | - # can be sequentially awaited if it's a list of awaitables that a test runner can handle. |
1283 | | - # This is likely to fail again but will clarify the exact point of await. |
1284 | | - # The error "TypeError: object AsyncMock can't be used in 'await' expression" means |
1285 | | - # `mock_result_aggregator_instance.current_result` is an AsyncMock, and that's what's awaited. |
1286 | | - # This AsyncMock needs to have a __await__ method. |
1287 | | - |
1288 | | - # Let's make the side_effect of the AsyncMock `current_result` provide the values. |
1289 | | - # This assumes that `await mock.property` somehow triggers a call to the mock. |
1290 | | - # This is not how AsyncMock works. |
1291 | | - |
1292 | | - # The code is `await result_aggregator.current_result`. |
1293 | | - # `result_aggregator` is an instance of `ResultAggregator`. |
1294 | | - # `current_result` is an async property. |
1295 | | - # So `result_aggregator.current_result` evaluates to a coroutine. |
1296 | | - # We need `mock_result_aggregator_instance.current_result` to be a coroutine, |
1297 | | - # or a list of coroutines if accessed multiple times. |
1298 | | - # This is best done by mocking the property itself. |
1299 | | - # Let's assume it's called twice. |
1300 | | - |
1301 | | - # We will patch ResultAggregator to be our mock_result_aggregator_instance |
1302 | | - # Then, we need to control what its `current_result` property returns. |
1303 | | - # We can use a PropertyMock for this, attached to the type of mock_result_aggregator_instance. |
1304 | | - |
1305 | | - # For this specific test, let's make current_result a simple async def method on the mock instance |
1306 | | - # This means we are slightly diverging from the "property" nature just for this mock. |
1307 | | - # Mock current_result property to return appropriate awaitables (coroutines) sequentially. |
1308 | | - async def get_event1_coro(): |
1309 | | - return event1_task_update |
1310 | | - |
1311 | | - async def get_event2_coro(): |
1312 | | - return event2_final_task |
1313 | | - |
1314 | | - # Configure the 'current_result' property on the type of the mock instance |
1315 | | - # This makes accessing `instance.current_result` call the side_effect function, |
1316 | | - # which then cycles through our list of coroutines. |
1317 | | - # We need a new PropertyMock for each instance, or patch the class. |
1318 | | - # Since mock_result_aggregator_instance is already created, we attach to its type. |
1319 | | - # This can be tricky. A more direct way is to ensure the instance's attribute `current_result` |
1320 | | - # behaves as desired. If `mock_result_aggregator_instance` is a `MagicMock`, its attributes are also mocks. |
1321 | | - |
1322 | | - # Let's make `current_result` a MagicMock whose side_effect returns the coroutines. |
1323 | | - # This means when `result_aggregator.current_result` is accessed, this mock is "called". |
1324 | | - # This isn't quite right for a property. A property isn't "called" on access. |
1325 | | - |
1326 | | - # Correct approach for mocking an async property on an instance mock: |
1327 | | - # Set the attribute `current_result` on the instance `mock_result_aggregator_instance` |
1328 | | - # to be a `PropertyMock` if we were patching the class. |
1329 | | - # Since we have the instance, we can try to replace its `current_result` attribute. |
1330 | | - # The instance `mock_result_aggregator_instance` is a `MagicMock`. |
1331 | | - # We can make `mock_result_aggregator_instance.current_result` a `PropertyMock` |
1332 | | - # that returns a coroutine. For multiple calls, `side_effect` on `PropertyMock` is a list of return_values. |
1333 | | - |
1334 | | - # Create a PropertyMock that will cycle through coroutines |
1335 | | - # This requires Python 3.8+ for PropertyMock to be directly usable with side_effect list for properties. |
1336 | | - # For older versions or for clarity with async properties, directly mocking the attribute |
1337 | | - # to be a series of awaitables is hard. |
1338 | | - # The easiest is to ensure `current_result` is an AsyncMock that returns the values. |
1339 | | - # The product code `await result_aggregator.current_result` means `current_result` must be an awaitable. |
1340 | | - |
1341 | | - # Let's make current_result an AsyncMock whose __call__ returns the sequence. |
1342 | | - # Mock current_result as an async property |
1343 | | - # Create coroutines that will be the "result" of awaiting the property |
1344 | | - async def get_current_result_coro1(): |
1345 | | - return event1_task_update |
1346 | | - |
1347 | | - async def get_current_result_coro2(): |
1348 | | - return event2_final_task |
1349 | | - |
1350 | | - # Configure the 'current_result' property on the mock_result_aggregator_instance |
1351 | | - # using PropertyMock attached to its type. This makes instance.current_result return |
1352 | | - # items from side_effect sequentially on each access. |
1353 | | - # Since current_result is an async property, these items should be coroutines. |
1354 | | - # We need to ensure that mock_result_aggregator_instance itself is the one patched. |
1355 | | - # The patch for ResultAggregator returns this instance. |
1356 | | - # So, we configure PropertyMock on the type of this specific mock instance. |
1357 | | - # This is slightly unusual; typically PropertyMock is used when patching a class. |
1358 | | - # A more straightforward approach for an instance is if its type is already a mock. |
1359 | | - # As mock_result_aggregator_instance is a MagicMock, we can configure its 'current_result' |
1360 | | - # attribute to be a PropertyMock. |
1361 | | - |
1362 | | - # Let's directly assign a PropertyMock to the type of the instance for `current_result` |
1363 | | - # This ensures that when `instance.current_result` is accessed, the PropertyMock's logic is triggered. |
1364 | | - # However, PropertyMock is usually used with `patch.object` or by setting it on the class. |
1365 | | - # |
1366 | | - # A simpler way for MagicMock instance: |
1367 | | - # `mock_result_aggregator_instance.current_result` is already a MagicMock (or AsyncMock if spec'd). |
1368 | | - # We need to make it return a coroutine upon access. |
1369 | | - # The most direct way to mock an async property on a MagicMock instance |
1370 | | - # such that it returns a sequence of awaitables: |
1371 | | - async def side_effect_current_result(): |
1372 | | - yield event1_task_update |
1373 | | - yield event2_final_task |
1374 | | - |
1375 | | - # Create an async generator from the side effect |
1376 | | - current_result_gen = side_effect_current_result() |
1377 | | - |
1378 | | - # Make current_result return the next item from this generator (wrapped in a coroutine) |
1379 | | - # each time it's accessed. |
1380 | | - async def get_next_current_result(): |
1381 | | - try: |
1382 | | - return await current_result_gen.__anext__() |
1383 | | - except StopAsyncIteration: |
1384 | | - # Handle case where it's awaited more times than values provided |
1385 | | - return None # Or raise an error |
1386 | | - |
1387 | | - # Since current_result is a property, accessing it should return a coroutine. |
1388 | | - # We can achieve this by making mock_result_aggregator_instance.current_result |
1389 | | - # a MagicMock whose side_effect returns these coroutines. |
1390 | | - # This is still tricky because it's a property access. |
1391 | | - |
1392 | | - # Let's use the PropertyMock on the class being mocked via the patch. |
1393 | | - # Setup for consume_and_emit |
1394 | | - def sync_get_event_stream_gen_for_prop_test(*args, **kwargs): |
1395 | | - return event_stream_gen() |
| 1244 | + # Mock current_result as an async property returning events sequentially. |
| 1245 | + async def to_coro(val): |
| 1246 | + return val |
1396 | 1247 |
|
1397 | | - mock_result_aggregator_instance.consume_and_emit = MagicMock( |
1398 | | - side_effect=sync_get_event_stream_gen_for_prop_test |
1399 | | - ) |
1400 | | - |
1401 | | - # Configure current_result on the type of the mock_result_aggregator_instance |
1402 | | - # This makes it behave like a property that returns items from side_effect on access. |
1403 | 1248 | type(mock_result_aggregator_instance).current_result = PropertyMock( |
1404 | | - side_effect=[get_current_result_coro1(), get_current_result_coro2()] |
| 1249 | + side_effect=[to_coro(event1_task_update), to_coro(event2_final_task)] |
1405 | 1250 | ) |
1406 | 1251 |
|
1407 | 1252 | context = create_server_call_context() |
@@ -1991,7 +1836,7 @@ async def test_set_task_push_notification_config_no_notifier(): |
1991 | 1836 | url='http://example.com', |
1992 | 1837 | ) |
1993 | 1838 |
|
1994 | | - with pytest.raises(UnsupportedOperationError): |
| 1839 | + with pytest.raises(PushNotificationNotSupportedError): |
1995 | 1840 | await request_handler.on_create_task_push_notification_config( |
1996 | 1841 | params, create_server_call_context() |
1997 | 1842 | ) |
@@ -2038,7 +1883,7 @@ async def test_get_task_push_notification_config_no_store(): |
2038 | 1883 | id='task_push_notification_config', |
2039 | 1884 | ) |
2040 | 1885 |
|
2041 | | - with pytest.raises(UnsupportedOperationError): |
| 1886 | + with pytest.raises(PushNotificationNotSupportedError): |
2042 | 1887 | await request_handler.on_get_task_push_notification_config( |
2043 | 1888 | params, create_server_call_context() |
2044 | 1889 | ) |
@@ -2269,7 +2114,7 @@ async def test_list_task_push_notification_config_no_store(): |
2269 | 2114 | ) |
2270 | 2115 | params = ListTaskPushNotificationConfigsRequest(task_id='task1') |
2271 | 2116 |
|
2272 | | - with pytest.raises(UnsupportedOperationError): |
| 2117 | + with pytest.raises(PushNotificationNotSupportedError): |
2273 | 2118 | await request_handler.on_list_task_push_notification_configs( |
2274 | 2119 | params, create_server_call_context() |
2275 | 2120 | ) |
@@ -2414,11 +2259,10 @@ async def test_delete_task_push_notification_config_no_store(): |
2414 | 2259 | task_id='task1', id='config1' |
2415 | 2260 | ) |
2416 | 2261 |
|
2417 | | - with pytest.raises(UnsupportedOperationError) as exc_info: |
| 2262 | + with pytest.raises(PushNotificationNotSupportedError): |
2418 | 2263 | await request_handler.on_delete_task_push_notification_config( |
2419 | 2264 | params, create_server_call_context() |
2420 | 2265 | ) |
2421 | | - assert isinstance(exc_info.value, UnsupportedOperationError) |
2422 | 2266 |
|
2423 | 2267 |
|
2424 | 2268 | @pytest.mark.asyncio |
|
0 commit comments