diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index af136378d..b6a61e77f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -200,6 +200,48 @@ jobs: fail_ci_if_error: false flags: servertests + test_responses: + name: Test Responses==0.12.0 + runs-on: ubuntu-latest + needs: lint + strategy: + matrix: + python-version: [ 3.7, 3.8 ] + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Get pip cache dir + id: pip-cache + run: | + echo "::set-output name=dir::$(pip cache dir)" + - name: pip cache + uses: actions/cache@v2 + with: + path: ${{ steps.pip-cache.outputs.dir }} + key: pip-${{ matrix.python-version }}-${{ hashFiles('**/setup.py') }}-4 + - name: Install project dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements-dev.txt + pip install pytest-cov + pip install responses==0.12.0 + pip install "coverage<=4.5.4" + - name: Test core-logic with responses==0.12.0 + run: | + pytest -sv --cov=moto --cov-report xml ./tests/test_core + - name: "Upload coverage to Codecov" + if: ${{ github.repository == 'spulec/moto'}} + uses: codecov/codecov-action@v1 + with: + fail_ci_if_error: true + flags: test_responses + terraform: name: Terraform Tests runs-on: ubuntu-latest @@ -263,7 +305,7 @@ jobs: deploy: name: Deploy runs-on: ubuntu-latest - needs: [test, testserver, terraform] + needs: [test, testserver, terraform, test_responses] if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.repository == 'spulec/moto' }} strategy: matrix: diff --git a/codecov.yml b/codecov.yml index 4fe85e33f..750eb3b17 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,7 +1,7 @@ codecov: notify: # Leave a GitHub comment after all builds have passed - after_n_builds: 8 + after_n_builds: 10 coverage: status: project: diff --git a/moto/core/models.py b/moto/core/models.py index 57fb081b9..885a2ad45 100644 --- a/moto/core/models.py +++ b/moto/core/models.py @@ -286,22 +286,47 @@ responses_mock.add_passthru("http") def _find_first_match_legacy(self, request): + matches = [] for i, match in enumerate(self._matches): if match.matches(request): - return match + matches.append(match) + # Look for implemented callbacks first + implemented_matches = [ + m + for m in matches + if type(m) is not CallbackResponse or m.callback != not_implemented_callback + ] + if implemented_matches: + return implemented_matches[0] + elif matches: + # We had matches, but all were of type not_implemented_callback + return matches[0] return None def _find_first_match(self, request): + matches = [] match_failed_reasons = [] for i, match in enumerate(self._matches): match_result, reason = match.matches(request) if match_result: - return match, match_failed_reasons + matches.append(match) else: match_failed_reasons.append(reason) + # Look for implemented callbacks first + implemented_matches = [ + m + for m in matches + if type(m) is not CallbackResponse or m.callback != not_implemented_callback + ] + if implemented_matches: + return implemented_matches[0], [] + elif matches: + # We had matches, but all were of type not_implemented_callback + return matches[0], match_failed_reasons + return None, match_failed_reasons diff --git a/tests/test_core/test_request_mocking.py b/tests/test_core/test_request_mocking.py index 3c56c7242..e08b664b6 100644 --- a/tests/test_core/test_request_mocking.py +++ b/tests/test_core/test_request_mocking.py @@ -3,7 +3,7 @@ import pytest import sure # noqa import boto3 -from moto import mock_sqs, settings +from moto import mock_s3, mock_sts, mock_sqs, settings @mock_sqs @@ -23,3 +23,30 @@ if not settings.TEST_SERVER_MODE: res = requests.get("https://fakeservice.amazonaws.com/foo/bar") assert res.content == b"The method is not implemented" assert res.status_code == 400 + + +@mock_sts +@mock_s3 +def test_decorator_ordering(): + """ + https://github.com/spulec/moto/issues/3790#issuecomment-803979809 + """ + bucket_name = "banana-slugs" + key = "trash-file" + region = "us-east-1" + client = boto3.client("s3", region_name=region) + s3 = boto3.resource("s3", region_name=region) + bucket = s3.Bucket(bucket_name) + bucket.create() + bucket.put_object(Body=b"ABCD", Key=key) + presigned_url = client.generate_presigned_url( + ClientMethod=client.get_object.__name__, + Params={ + "Bucket": bucket_name, + "Key": key, + "ResponseContentDisposition": "attachment;filename=bar", + }, + ) + + resp = requests.get(presigned_url) + resp.status_code.should.equal(200)