diff --git a/airbyte_cdk/test/mock_http/mocker.py b/airbyte_cdk/test/mock_http/mocker.py index 276f0db09..204d43140 100644 --- a/airbyte_cdk/test/mock_http/mocker.py +++ b/airbyte_cdk/test/mock_http/mocker.py @@ -2,9 +2,10 @@ import contextlib import functools +from collections import defaultdict from enum import Enum from types import TracebackType -from typing import Callable, List, Optional, Union +from typing import Callable, Dict, Iterable, List, Optional, Union import requests_mock @@ -40,7 +41,7 @@ class HttpMocker(contextlib.ContextDecorator): def __init__(self) -> None: self._mocker = requests_mock.Mocker() - self._matchers: List[HttpRequestMatcher] = [] + self._matchers: Dict[SupportedHttpMethods, List[HttpRequestMatcher]] = defaultdict(list) def __enter__(self) -> "HttpMocker": self._mocker.__enter__() @@ -55,7 +56,7 @@ def __exit__( self._mocker.__exit__(exc_type, exc_val, exc_tb) def _validate_all_matchers_called(self) -> None: - for matcher in self._matchers: + for matcher in self._get_matchers(): if not matcher.has_expected_match_count(): raise ValueError(f"Invalid number of matches for `{matcher}`") @@ -69,9 +70,9 @@ def _mock_request_method( responses = [responses] matcher = HttpRequestMatcher(request, len(responses)) - if matcher in self._matchers: + if matcher in self._matchers[method]: raise ValueError(f"Request {matcher.request} already mocked") - self._matchers.append(matcher) + self._matchers[method].append(matcher) getattr(self._mocker, method)( requests_mock.ANY, @@ -129,7 +130,7 @@ def matches(requests_mock_request: requests_mock.request._RequestObjectProxy) -> def assert_number_of_calls(self, request: HttpRequest, number_of_calls: int) -> None: corresponding_matchers = list( - filter(lambda matcher: matcher.request == request, self._matchers) + filter(lambda matcher: matcher.request is request, self._get_matchers()) ) if len(corresponding_matchers) != 1: raise ValueError( @@ -150,7 +151,7 @@ def wrapper(*args, **kwargs): # type: ignore # this is a very generic wrapper result = f(*args, **kwargs) except requests_mock.NoMockAddress as no_mock_exception: matchers_as_string = "\n\t".join( - map(lambda matcher: str(matcher.request), self._matchers) + map(lambda matcher: str(matcher.request), self._get_matchers()) ) raise ValueError( f"No matcher matches {no_mock_exception.args[0]} with headers `{no_mock_exception.request.headers}` " @@ -175,6 +176,10 @@ def wrapper(*args, **kwargs): # type: ignore # this is a very generic wrapper return wrapper + def _get_matchers(self) -> Iterable[HttpRequestMatcher]: + for matchers in self._matchers.values(): + yield from matchers + def clear_all_matchers(self) -> None: """Clears all stored matchers by resetting the _matchers list to an empty state.""" - self._matchers = [] + self._matchers = defaultdict(list) diff --git a/unit_tests/test/mock_http/test_mocker.py b/unit_tests/test/mock_http/test_mocker.py index 6a50a30c2..d34827911 100644 --- a/unit_tests/test/mock_http/test_mocker.py +++ b/unit_tests/test/mock_http/test_mocker.py @@ -295,10 +295,17 @@ def decorated_function(http_mocker): with pytest.raises(ValueError): decorated_function() - def test_given_request_already_mocked_when_decorate_then_raise(self): + def test_given_request_already_mocked_when_mock_then_raise(self): with HttpMocker() as http_mocker: a_request = HttpRequest(_A_URL, _SOME_QUERY_PARAMS, _SOME_HEADERS) http_mocker.get(a_request, _A_RESPONSE) with pytest.raises(ValueError): http_mocker.get(a_request, _A_RESPONSE) + + def test_given_same_request_with_different_method_when_mock_then_do_not_raise(self): + with HttpMocker() as http_mocker: + a_request = HttpRequest(_A_URL, _SOME_QUERY_PARAMS, _SOME_HEADERS) + http_mocker.get(a_request, _A_RESPONSE) + + http_mocker.delete(a_request, _A_RESPONSE)