From 98ea40f049a4fc2f3958e245d087d0022bf3bdd0 Mon Sep 17 00:00:00 2001 From: maxi297 Date: Mon, 17 Feb 2025 10:12:29 -0500 Subject: [PATCH 1/3] HttpMocker - adding support for PUT requests and bytes responses --- airbyte_cdk/test/mock_http/mocker.py | 8 ++++++- airbyte_cdk/test/mock_http/response.py | 6 ++--- unit_tests/test/mock_http/test_mocker.py | 30 +++++++++++++++++++++++- 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/airbyte_cdk/test/mock_http/mocker.py b/airbyte_cdk/test/mock_http/mocker.py index cd1b1f9a7..038e998b5 100644 --- a/airbyte_cdk/test/mock_http/mocker.py +++ b/airbyte_cdk/test/mock_http/mocker.py @@ -17,6 +17,7 @@ class SupportedHttpMethods(str, Enum): GET = "get" PATCH = "patch" POST = "post" + PUT = "put" DELETE = "delete" @@ -77,7 +78,7 @@ def _mock_request_method( additional_matcher=self._matches_wrapper(matcher), response_list=[ { - "text": response.body, + "text" if isinstance(response.body, str) else "content": response.body, "status_code": response.status_code, "headers": response.headers, } @@ -98,6 +99,11 @@ def post( ) -> None: self._mock_request_method(SupportedHttpMethods.POST, request, responses) + def put( + self, request: HttpRequest, responses: Union[HttpResponse, List[HttpResponse]] + ) -> None: + self._mock_request_method(SupportedHttpMethods.PUT, request, responses) + def delete( self, request: HttpRequest, responses: Union[HttpResponse, List[HttpResponse]] ) -> None: diff --git a/airbyte_cdk/test/mock_http/response.py b/airbyte_cdk/test/mock_http/response.py index 848be55a0..204362546 100644 --- a/airbyte_cdk/test/mock_http/response.py +++ b/airbyte_cdk/test/mock_http/response.py @@ -1,19 +1,19 @@ # Copyright (c) 2023 Airbyte, Inc., all rights reserved. from types import MappingProxyType -from typing import Mapping +from typing import Mapping, Union class HttpResponse: def __init__( - self, body: str, status_code: int = 200, headers: Mapping[str, str] = MappingProxyType({}) + self, body: Union[str, bytes], status_code: int = 200, headers: Mapping[str, str] = MappingProxyType({}) ): self._body = body self._status_code = status_code self._headers = headers @property - def body(self) -> str: + def body(self) -> Union[str, bytes]: return self._body @property diff --git a/unit_tests/test/mock_http/test_mocker.py b/unit_tests/test/mock_http/test_mocker.py index 4ff5afe01..6a50a30c2 100644 --- a/unit_tests/test/mock_http/test_mocker.py +++ b/unit_tests/test/mock_http/test_mocker.py @@ -1,5 +1,5 @@ # Copyright (c) 2023 Airbyte, Inc., all rights reserved. - +import gzip from unittest import TestCase import pytest @@ -75,6 +75,34 @@ def test_given_post_request_match_when_decorate_then_return_response(self, http_ assert response.text == _A_RESPONSE_BODY assert response.status_code == 474 + @HttpMocker() + def test_given_put_request_match_when_decorate_then_return_response(self, http_mocker): + http_mocker.put( + HttpRequest(_A_URL, _SOME_QUERY_PARAMS, _SOME_HEADERS, _SOME_REQUEST_BODY_STR), + HttpResponse(_A_RESPONSE_BODY, 474), + ) + + response = requests.put( + _A_URL, params=_SOME_QUERY_PARAMS, headers=_SOME_HEADERS, data=_SOME_REQUEST_BODY_STR + ) + + assert response.text == _A_RESPONSE_BODY + assert response.status_code == 474 + + @HttpMocker() + def test_given_response_in_bytes_when_decorate_then_match(self, http_mocker): + response_content = gzip.compress(bytes("This is the response within the gzip", "utf-8")) + http_mocker.put( + HttpRequest(_A_URL, _SOME_QUERY_PARAMS, _SOME_HEADERS, _SOME_REQUEST_BODY_STR), + HttpResponse(response_content, 474), + ) + + response = requests.put( + _A_URL, params=_SOME_QUERY_PARAMS, headers=_SOME_HEADERS, data=_SOME_REQUEST_BODY_STR + ) + + assert response.content == response_content + @HttpMocker() def test_given_multiple_responses_when_decorate_get_request_then_return_response( self, http_mocker From 766aef6025ee406acd24ecdd16d63ca75b70bfce Mon Sep 17 00:00:00 2001 From: octavia-squidington-iii Date: Mon, 17 Feb 2025 15:16:00 +0000 Subject: [PATCH 2/3] Auto-fix lint and format issues --- airbyte_cdk/test/mock_http/mocker.py | 4 +--- airbyte_cdk/test/mock_http/response.py | 5 ++++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/airbyte_cdk/test/mock_http/mocker.py b/airbyte_cdk/test/mock_http/mocker.py index 038e998b5..509f90053 100644 --- a/airbyte_cdk/test/mock_http/mocker.py +++ b/airbyte_cdk/test/mock_http/mocker.py @@ -99,9 +99,7 @@ def post( ) -> None: self._mock_request_method(SupportedHttpMethods.POST, request, responses) - def put( - self, request: HttpRequest, responses: Union[HttpResponse, List[HttpResponse]] - ) -> None: + def put(self, request: HttpRequest, responses: Union[HttpResponse, List[HttpResponse]]) -> None: self._mock_request_method(SupportedHttpMethods.PUT, request, responses) def delete( diff --git a/airbyte_cdk/test/mock_http/response.py b/airbyte_cdk/test/mock_http/response.py index 204362546..fefe762e9 100644 --- a/airbyte_cdk/test/mock_http/response.py +++ b/airbyte_cdk/test/mock_http/response.py @@ -6,7 +6,10 @@ class HttpResponse: def __init__( - self, body: Union[str, bytes], status_code: int = 200, headers: Mapping[str, str] = MappingProxyType({}) + self, + body: Union[str, bytes], + status_code: int = 200, + headers: Mapping[str, str] = MappingProxyType({}), ): self._body = body self._status_code = status_code From c188a1d70b7b28cf7dacd1297eecdd890ddaa799 Mon Sep 17 00:00:00 2001 From: maxi297 Date: Mon, 17 Feb 2025 10:20:19 -0500 Subject: [PATCH 3/3] code review --- airbyte_cdk/test/mock_http/mocker.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/airbyte_cdk/test/mock_http/mocker.py b/airbyte_cdk/test/mock_http/mocker.py index 509f90053..276f0db09 100644 --- a/airbyte_cdk/test/mock_http/mocker.py +++ b/airbyte_cdk/test/mock_http/mocker.py @@ -78,7 +78,7 @@ def _mock_request_method( additional_matcher=self._matches_wrapper(matcher), response_list=[ { - "text" if isinstance(response.body, str) else "content": response.body, + self._get_body_field(response): response.body, "status_code": response.status_code, "headers": response.headers, } @@ -86,6 +86,10 @@ def _mock_request_method( ], ) + @staticmethod + def _get_body_field(response: HttpResponse) -> str: + return "text" if isinstance(response.body, str) else "content" + def get(self, request: HttpRequest, responses: Union[HttpResponse, List[HttpResponse]]) -> None: self._mock_request_method(SupportedHttpMethods.GET, request, responses)