mirror of
https://github.com/apache/superset.git
synced 2026-07-18 12:45:44 +00:00
97 lines
3.4 KiB
Python
97 lines
3.4 KiB
Python
# Licensed to the Apache Software Foundation (ASF) under one
|
|
# or more contributor license agreements. See the NOTICE file
|
|
# distributed with this work for additional information
|
|
# regarding copyright ownership. The ASF licenses this file
|
|
# to you under the Apache License, Version 2.0 (the
|
|
# "License"); you may not use this file except in compliance
|
|
# with the License. You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing,
|
|
# software distributed under the License is distributed on an
|
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
# KIND, either express or implied. See the License for the
|
|
# specific language governing permissions and limitations
|
|
# under the License.
|
|
from unittest import mock
|
|
from urllib.error import HTTPError, URLError
|
|
|
|
import pytest
|
|
|
|
from scripts import change_detector
|
|
|
|
|
|
def _make_response(body: bytes) -> mock.MagicMock:
|
|
"""Builds a mock that mimics urlopen's context-manager response."""
|
|
response: mock.MagicMock = mock.MagicMock()
|
|
response.__enter__.return_value.read.return_value = body
|
|
return response
|
|
|
|
|
|
def test_fetch_retries_transient_error_then_succeeds() -> None:
|
|
payload: list[dict[str, str]] = [{"filename": "superset/foo.py"}]
|
|
side_effects: list[object] = [
|
|
HTTPError("http://api", 500, "Server Error", {}, None), # type: ignore
|
|
HTTPError("http://api", 502, "Bad Gateway", {}, None), # type: ignore
|
|
_make_response(b'[{"filename": "superset/foo.py"}]'),
|
|
]
|
|
with (
|
|
mock.patch.object(
|
|
change_detector, "urlopen", side_effect=side_effects
|
|
) as urlopen_mock,
|
|
mock.patch.object(change_detector.time, "sleep") as sleep_mock,
|
|
):
|
|
result = change_detector.fetch_files_github_api("http://api")
|
|
|
|
assert result == payload
|
|
assert urlopen_mock.call_count == 3
|
|
assert sleep_mock.call_count == 2
|
|
|
|
|
|
def test_fetch_retries_rate_limit_then_succeeds() -> None:
|
|
side_effects: list[object] = [
|
|
HTTPError("http://api", 429, "Too Many Requests", {}, None), # type: ignore
|
|
_make_response(b"[]"),
|
|
]
|
|
with (
|
|
mock.patch.object(
|
|
change_detector, "urlopen", side_effect=side_effects
|
|
) as urlopen_mock,
|
|
mock.patch.object(change_detector.time, "sleep") as sleep_mock,
|
|
):
|
|
result = change_detector.fetch_files_github_api("http://api")
|
|
|
|
assert result == []
|
|
assert urlopen_mock.call_count == 2
|
|
assert sleep_mock.call_count == 1
|
|
|
|
|
|
def test_fetch_does_not_retry_client_error() -> None:
|
|
with (
|
|
mock.patch.object(
|
|
change_detector,
|
|
"urlopen",
|
|
side_effect=HTTPError("http://api", 404, "Not Found", {}, None), # type: ignore
|
|
) as urlopen_mock,
|
|
mock.patch.object(change_detector.time, "sleep") as sleep_mock,
|
|
):
|
|
with pytest.raises(HTTPError):
|
|
change_detector.fetch_files_github_api("http://api")
|
|
|
|
assert urlopen_mock.call_count == 1
|
|
assert sleep_mock.call_count == 0
|
|
|
|
|
|
def test_fetch_gives_up_after_max_retries() -> None:
|
|
with (
|
|
mock.patch.object(
|
|
change_detector, "urlopen", side_effect=URLError("connection reset")
|
|
) as urlopen_mock,
|
|
mock.patch.object(change_detector.time, "sleep"),
|
|
):
|
|
with pytest.raises(URLError):
|
|
change_detector.fetch_files_github_api("http://api")
|
|
|
|
assert urlopen_mock.call_count == change_detector.MAX_RETRIES
|