From 206590acf5215abef67684a0122dc668750f71fa Mon Sep 17 00:00:00 2001 From: Bert Blommers Date: Mon, 28 Feb 2022 21:57:41 -0100 Subject: [PATCH] Fix ordering issue with a user-defined Responses-activation (#4896) --- moto/core/custom_responses_mock.py | 6 ++- moto/core/responses_custom_registry.py | 7 +-- tests/test_core/test_responses_module.py | 63 ++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 5 deletions(-) create mode 100644 tests/test_core/test_responses_module.py diff --git a/moto/core/custom_responses_mock.py b/moto/core/custom_responses_mock.py index 0a23cc05f..b3e901b46 100644 --- a/moto/core/custom_responses_mock.py +++ b/moto/core/custom_responses_mock.py @@ -100,7 +100,8 @@ def not_implemented_callback(request): # - Same request, executed again, will be matched on the subsequent match, which happens to be the catch-all, not-yet-implemented, callback # Fix: Always return the first match def _find_first_match_legacy(self, request): - matches = [match for match in self._matches if match.matches(request)] + all_possibles = self._matches + responses._default_mock._matches + matches = [match for match in all_possibles if match.matches(request)] # Look for implemented callbacks first implemented_matches = [ @@ -120,7 +121,8 @@ def _find_first_match_legacy(self, request): def _find_first_match(self, request): matches = [] match_failed_reasons = [] - for match in self._matches: + all_possibles = self._matches + responses._default_mock._matches + for match in all_possibles: match_result, reason = match.matches(request) if match_result: matches.append(match) diff --git a/moto/core/responses_custom_registry.py b/moto/core/responses_custom_registry.py index 7526c2293..097d14139 100644 --- a/moto/core/responses_custom_registry.py +++ b/moto/core/responses_custom_registry.py @@ -1,9 +1,9 @@ # This will only exist in responses >= 0.17 -from responses import registries +import responses from .custom_responses_mock import CallbackResponse, not_implemented_callback -class CustomRegistry(registries.FirstMatchRegistry): +class CustomRegistry(responses.registries.FirstMatchRegistry): """ Custom Registry that returns requests in an order that makes sense for Moto: - Implemented callbacks take precedence over non-implemented-callbacks @@ -11,9 +11,10 @@ class CustomRegistry(registries.FirstMatchRegistry): """ def find(self, request): + all_possibles = responses._default_mock._registry.registered + self.registered found = [] match_failed_reasons = [] - for response in self.registered: + for response in all_possibles: match_result, reason = response.matches(request) if match_result: found.append(response) diff --git a/tests/test_core/test_responses_module.py b/tests/test_core/test_responses_module.py new file mode 100644 index 000000000..cd89f7126 --- /dev/null +++ b/tests/test_core/test_responses_module.py @@ -0,0 +1,63 @@ +""" +Ensure that the responses module plays nice with our mocks +""" + +import boto3 +import requests +import responses +from moto import mock_s3, settings +from unittest import SkipTest, TestCase + + +class TestResponsesModule(TestCase): + def setUp(self): + if settings.TEST_SERVER_MODE: + raise SkipTest("No point in testing responses-decorator in ServerMode") + + @mock_s3 + @responses.activate + def test_moto_first(self): + + """ + Verify we can activate a user-defined `responses` on top of our Moto mocks + """ + self.moto_responses_compatibility() + + @responses.activate + @mock_s3 + def test_moto_second(self): + """ + Verify we can load Moto after activating a `responses`-mock + """ + self.moto_responses_compatibility() + + def moto_responses_compatibility(self): + responses.add( + responses.GET, url="http://127.0.0.1/lkdsfjlkdsa", json={"a": "4"}, + ) + s3 = boto3.client("s3") + s3.create_bucket(Bucket="mybucket") + s3.put_object(Bucket="mybucket", Key="name", Body="value") + s3.get_object(Bucket="mybucket", Key="name")["Body"].read() + with requests.get("http://127.0.0.1/lkdsfjlkdsa",) as r: + assert r.json() == {"a": "4"} + + @responses.activate + def test_moto_as_late_as_possible(self): + """ + Verify we can load moto after registering a response + """ + responses.add( + responses.GET, url="http://127.0.0.1/lkdsfjlkdsa", json={"a": "4"}, + ) + with mock_s3(): + s3 = boto3.client("s3") + s3.create_bucket(Bucket="mybucket") + s3.put_object(Bucket="mybucket", Key="name", Body="value") + # This mock exists within Moto + with requests.get("http://127.0.0.1/lkdsfjlkdsa",) as r: + assert r.json() == {"a": "4"} + + # And outside of Moto + with requests.get("http://127.0.0.1/lkdsfjlkdsa",) as r: + assert r.json() == {"a": "4"}