From c595f846a84c15fa64b5819b461f20fa3304cf9b Mon Sep 17 00:00:00 2001 From: Marnie Date: Sun, 12 Jul 2026 03:05:49 +0800 Subject: [PATCH 1/2] fix(webhooks): accept 2xx status codes in webhook test endpoint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The webhook test endpoint previously treated any non-200 response as a failure. This is incorrect — HTTP 201 (Created), 202 (Accepted), and 204 (No Content) are all valid webhook responses. Change the condition from \status_code != 200\ to \status_code >= 400\ so that any 2xx response is considered successful. Also update the error message to include the actual status code instead of telling users to return a 200 OK. --- api/tests/unit/webhooks/test_unit_webhooks.py | 4 ++-- api/webhooks/views.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/api/tests/unit/webhooks/test_unit_webhooks.py b/api/tests/unit/webhooks/test_unit_webhooks.py index 9ef69bcd2561..9561bee1f561 100644 --- a/api/tests/unit/webhooks/test_unit_webhooks.py +++ b/api/tests/unit/webhooks/test_unit_webhooks.py @@ -434,10 +434,10 @@ def test_send_test_webhook__various_error_status_codes__returns_correct_response mock_post.assert_called_once() response_json = response.json() assert response_json["status"] == external_api_response_status - assert response_json["detail"] == "Webhook returned invalid status" + assert response_json["detail"] == "Webhook returned error status" assert ( response_json["body"] - == "Please check the webhook endpoint to validate it returns a 200 OK." + == f"Webhook returned HTTP {external_api_response_status}." ) diff --git a/api/webhooks/views.py b/api/webhooks/views.py index cfb756537eb2..743860c2fce9 100644 --- a/api/webhooks/views.py +++ b/api/webhooks/views.py @@ -47,11 +47,11 @@ def test(self, request: Request) -> Response: else WebhookType.ENVIRONMENT ) response = send_test_request_to_webhook(webhook_url, secret, webhook_type) - if response.status_code != 200: + if response.status_code >= 400: return Response( { - "detail": "Webhook returned invalid status", - "body": "Please check the webhook endpoint to validate it returns a 200 OK.", + "detail": "Webhook returned error status", + "body": f"Webhook returned HTTP {response.status_code}.", "status": response.status_code, }, status=status.HTTP_400_BAD_REQUEST, From 9630524f89eebe5dd6b95800bd1c21e4fce625b2 Mon Sep 17 00:00:00 2001 From: Marnie0415 Date: Mon, 13 Jul 2026 19:48:03 +0800 Subject: [PATCH 2/2] fix(webhooks): use response.ok instead of status_code >= 400 and add parametrised 2xx tests - Change condition from esponse.status_code >= 400 to ot response.ok to correctly handle 3xx redirects as failures (Zaimwa9's feedback) - Add parametrised test for 201/202/204 status codes as success cases - This ensures only 2xx responses are treated as successful webhook tests --- api/tests/unit/webhooks/test_unit_webhooks.py | 43 +++++++++++++++++++ api/webhooks/views.py | 2 +- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/api/tests/unit/webhooks/test_unit_webhooks.py b/api/tests/unit/webhooks/test_unit_webhooks.py index 9561bee1f561..b9ede94112e5 100644 --- a/api/tests/unit/webhooks/test_unit_webhooks.py +++ b/api/tests/unit/webhooks/test_unit_webhooks.py @@ -392,6 +392,49 @@ def test_send_test_webhook__200_response_from_webhook__returns_correct_response( assert response_json["detail"] == "Webhook test successful" +@pytest.mark.parametrize( + "external_api_response_status", + [ + 201, + 202, + 204, + ], +) +def test_send_test_webhook__various_2xx_status_codes__returns_success( + mocker: MockerFixture, + admin_client: APIClient, + external_api_response_status: int, + organisation: Organisation, +) -> None: + # Given + webhook_url = "http://test.webhook.com" + mock_post = mocker.patch("requests.post") + mock_response = MagicMock() + mock_response.status_code = external_api_response_status + mock_response.text = "success" + mock_post.return_value = mock_response + + url = reverse("api-v1:webhooks:webhooks-test") + + data = { + "webhook_url": webhook_url, + "secret": "some-secret", + "scope": {"type": "organisation", "id": organisation.id}, + } + + # When + response = admin_client.post( + url, data=json.dumps(data), content_type="application/json" + ) + + # Then + assert response.status_code == 200 + mock_post.assert_called_once() + response_json = response.json() + assert response_json["status"] == external_api_response_status + assert response_json["detail"] == "Webhook test successful" + + @pytest.mark.parametrize( "external_api_response_status, external_api_error_text, expected_final_status", [ diff --git a/api/webhooks/views.py b/api/webhooks/views.py index 743860c2fce9..bf467c467e90 100644 --- a/api/webhooks/views.py +++ b/api/webhooks/views.py @@ -47,7 +47,7 @@ def test(self, request: Request) -> Response: else WebhookType.ENVIRONMENT ) response = send_test_request_to_webhook(webhook_url, secret, webhook_type) - if response.status_code >= 400: + if not response.ok: return Response( { "detail": "Webhook returned error status",