|
7 | 7 | from aiohttp import web, ClientSession
|
8 | 8 | from aiohttp.client import ServerDisconnectedError
|
9 | 9 | from aiohttp.web_request import Request
|
| 10 | +from aiohttp.web_exceptions import ( |
| 11 | + HTTPInternalServerError, |
| 12 | + HTTPNetworkAuthenticationRequired, |
| 13 | + HTTPBadRequest, |
| 14 | + HTTPNotFound, |
| 15 | + HTTPUnavailableForLegalReasons, |
| 16 | +) |
10 | 17 |
|
11 | 18 | from sentry_sdk import capture_message, start_transaction
|
12 | 19 | from sentry_sdk.integrations.aiohttp import AioHttpIntegration
|
@@ -617,3 +624,118 @@ async def handler(_):
|
617 | 624 | # Important to note that the ServerDisconnectedError indicates we have no error server-side.
|
618 | 625 | with pytest.raises(ServerDisconnectedError):
|
619 | 626 | await client.get("/")
|
| 627 | + |
| 628 | + |
| 629 | +@pytest.mark.parametrize( |
| 630 | + ("integration_kwargs", "exception_to_raise", "should_capture"), |
| 631 | + ( |
| 632 | + ({}, None, False), |
| 633 | + ({}, HTTPBadRequest, False), |
| 634 | + ( |
| 635 | + {}, |
| 636 | + HTTPUnavailableForLegalReasons(None), |
| 637 | + False, |
| 638 | + ), # Highest 4xx status code (451) |
| 639 | + ({}, HTTPInternalServerError, True), |
| 640 | + ({}, HTTPNetworkAuthenticationRequired, True), # Highest 5xx status code (511) |
| 641 | + ({"failed_request_status_codes": set()}, HTTPInternalServerError, False), |
| 642 | + ( |
| 643 | + {"failed_request_status_codes": set()}, |
| 644 | + HTTPNetworkAuthenticationRequired, |
| 645 | + False, |
| 646 | + ), |
| 647 | + ({"failed_request_status_codes": {404, *range(500, 600)}}, HTTPNotFound, True), |
| 648 | + ( |
| 649 | + {"failed_request_status_codes": {404, *range(500, 600)}}, |
| 650 | + HTTPInternalServerError, |
| 651 | + True, |
| 652 | + ), |
| 653 | + ( |
| 654 | + {"failed_request_status_codes": {404, *range(500, 600)}}, |
| 655 | + HTTPBadRequest, |
| 656 | + False, |
| 657 | + ), |
| 658 | + ), |
| 659 | +) |
| 660 | +@pytest.mark.asyncio |
| 661 | +async def test_failed_request_status_codes( |
| 662 | + sentry_init, |
| 663 | + aiohttp_client, |
| 664 | + capture_events, |
| 665 | + integration_kwargs, |
| 666 | + exception_to_raise, |
| 667 | + should_capture, |
| 668 | +): |
| 669 | + sentry_init(integrations=[AioHttpIntegration(**integration_kwargs)]) |
| 670 | + events = capture_events() |
| 671 | + |
| 672 | + async def handle(_): |
| 673 | + if exception_to_raise is not None: |
| 674 | + raise exception_to_raise |
| 675 | + else: |
| 676 | + return web.Response(status=200) |
| 677 | + |
| 678 | + app = web.Application() |
| 679 | + app.router.add_get("/", handle) |
| 680 | + |
| 681 | + client = await aiohttp_client(app) |
| 682 | + resp = await client.get("/") |
| 683 | + |
| 684 | + expected_status = ( |
| 685 | + 200 if exception_to_raise is None else exception_to_raise.status_code |
| 686 | + ) |
| 687 | + assert resp.status == expected_status |
| 688 | + |
| 689 | + if should_capture: |
| 690 | + (event,) = events |
| 691 | + assert event["exception"]["values"][0]["type"] == exception_to_raise.__name__ |
| 692 | + else: |
| 693 | + assert not events |
| 694 | + |
| 695 | + |
| 696 | +@pytest.mark.asyncio |
| 697 | +async def test_failed_request_status_codes_with_returned_status( |
| 698 | + sentry_init, aiohttp_client, capture_events |
| 699 | +): |
| 700 | + """ |
| 701 | + Returning a web.Response with a failed_request_status_code should not be reported to Sentry. |
| 702 | + """ |
| 703 | + sentry_init(integrations=[AioHttpIntegration(failed_request_status_codes={500})]) |
| 704 | + events = capture_events() |
| 705 | + |
| 706 | + async def handle(_): |
| 707 | + return web.Response(status=500) |
| 708 | + |
| 709 | + app = web.Application() |
| 710 | + app.router.add_get("/", handle) |
| 711 | + |
| 712 | + client = await aiohttp_client(app) |
| 713 | + resp = await client.get("/") |
| 714 | + |
| 715 | + assert resp.status == 500 |
| 716 | + assert not events |
| 717 | + |
| 718 | + |
| 719 | +@pytest.mark.asyncio |
| 720 | +async def test_failed_request_status_codes_non_http_exception( |
| 721 | + sentry_init, aiohttp_client, capture_events |
| 722 | +): |
| 723 | + """ |
| 724 | + If an exception, which is not an instance of HTTPException, is raised, it should be captured, even if |
| 725 | + failed_request_status_codes is empty. |
| 726 | + """ |
| 727 | + sentry_init(integrations=[AioHttpIntegration(failed_request_status_codes=set())]) |
| 728 | + events = capture_events() |
| 729 | + |
| 730 | + async def handle(_): |
| 731 | + 1 / 0 |
| 732 | + |
| 733 | + app = web.Application() |
| 734 | + app.router.add_get("/", handle) |
| 735 | + |
| 736 | + client = await aiohttp_client(app) |
| 737 | + resp = await client.get("/") |
| 738 | + assert resp.status == 500 |
| 739 | + |
| 740 | + (event,) = events |
| 741 | + assert event["exception"]["values"][0]["type"] == "ZeroDivisionError" |
0 commit comments