httpie-cli/tests/test_update_warnings.py

238 lines
6.5 KiB
Python
Raw Normal View History

import json
import tempfile
import time
from contextlib import suppress
from datetime import datetime
from pathlib import Path
import pytest
from httpie.internal.daemon_runner import STATUS_FILE
from httpie.internal.daemons import spawn_daemon
from httpie.status import ExitStatus
from .utils import PersistentMockEnvironment, http, httpie
BUILD_CHANNEL = 'test'
BUILD_CHANNEL_2 = 'test2'
UNKNOWN_BUILD_CHANNEL = 'test3'
HIGHEST_VERSION = '999.999.999'
LOWEST_VERSION = '1.1.1'
FIXED_DATE = datetime(1970, 1, 1).isoformat()
MAX_ATTEMPT = 40
MAX_TIMEOUT = 2.0
def check_update_warnings(text):
return 'A new HTTPie release' in text
@pytest.mark.requires_external_processes
def test_daemon_runner():
# We have a pseudo daemon task called 'check_status'
# which creates a temp file called STATUS_FILE under
# user's temp directory. This test simply ensures that
# we create a daemon that successfully performs the
# external task.
status_file = Path(tempfile.gettempdir()) / STATUS_FILE
with suppress(FileNotFoundError):
status_file.unlink()
spawn_daemon('check_status')
for attempt in range(MAX_ATTEMPT):
time.sleep(MAX_TIMEOUT / MAX_ATTEMPT)
if status_file.exists():
break
else:
pytest.fail(
'Maximum number of attempts failed for daemon status check.'
)
assert status_file.exists()
def test_fetch(static_fetch_data, without_warnings):
http('fetch_updates', '--daemon', env=without_warnings)
with open(without_warnings.config.version_info_file) as stream:
version_data = json.load(stream)
assert version_data['last_warned_date'] is None
assert version_data['last_fetched_date'] is not None
assert (
version_data['last_released_versions'][BUILD_CHANNEL]
== HIGHEST_VERSION
)
assert (
version_data['last_released_versions'][BUILD_CHANNEL_2]
== LOWEST_VERSION
)
def test_fetch_dont_override_existing_layout(
static_fetch_data, without_warnings
):
with open(without_warnings.config.version_info_file, 'w') as stream:
existing_layout = {
'last_warned_date': FIXED_DATE,
'last_fetched_date': FIXED_DATE,
'last_released_versions': {BUILD_CHANNEL: LOWEST_VERSION},
}
json.dump(existing_layout, stream)
http('fetch_updates', '--daemon', env=without_warnings)
with open(without_warnings.config.version_info_file) as stream:
version_data = json.load(stream)
# The "last updated at" field should not be modified, but the
# rest need to be updated.
assert version_data['last_warned_date'] == FIXED_DATE
assert version_data['last_fetched_date'] != FIXED_DATE
assert (
version_data['last_released_versions'][BUILD_CHANNEL]
== HIGHEST_VERSION
)
def test_fetch_broken_json(static_fetch_data, without_warnings):
with open(without_warnings.config.version_info_file, 'w') as stream:
stream.write('$$broken$$')
http('fetch_updates', '--daemon', env=without_warnings)
with open(without_warnings.config.version_info_file) as stream:
version_data = json.load(stream)
assert (
version_data['last_released_versions'][BUILD_CHANNEL]
== HIGHEST_VERSION
)
def test_check_updates_disable_warnings(
without_warnings, httpbin, fetch_update_mock
):
r = http(httpbin + '/get', env=without_warnings)
assert not fetch_update_mock.called
assert not check_update_warnings(r.stderr)
def test_check_updates_first_invocation(
with_warnings, httpbin, fetch_update_mock
):
r = http(httpbin + '/get', env=with_warnings)
assert fetch_update_mock.called
assert not check_update_warnings(r.stderr)
@pytest.mark.parametrize(
'should_issue_warning, build_channel',
[
(False, pytest.lazy_fixture('lower_build_channel')),
(True, pytest.lazy_fixture('higher_build_channel')),
],
)
def test_check_updates_first_time_after_data_fetch(
with_warnings,
httpbin,
fetch_update_mock,
static_fetch_data,
should_issue_warning,
build_channel,
):
http('fetch_updates', '--daemon', env=with_warnings)
r = http(httpbin + '/get', env=with_warnings)
assert not fetch_update_mock.called
assert (not should_issue_warning) or check_update_warnings(r.stderr)
def test_check_updates_first_time_after_data_fetch_unknown_build_channel(
with_warnings,
httpbin,
fetch_update_mock,
static_fetch_data,
unknown_build_channel,
):
http('fetch_updates', '--daemon', env=with_warnings)
r = http(httpbin + '/get', env=with_warnings)
assert not fetch_update_mock.called
assert not check_update_warnings(r.stderr)
def test_cli_check_updates(
static_fetch_data, higher_build_channel
):
r = httpie('cli', 'check-updates')
assert r.exit_status == ExitStatus.SUCCESS
assert check_update_warnings(r)
@pytest.mark.parametrize(
"build_channel", [
pytest.lazy_fixture("lower_build_channel"),
pytest.lazy_fixture("unknown_build_channel")
]
)
def test_cli_check_updates_not_shown(
static_fetch_data, build_channel
):
r = httpie('cli', 'check-updates')
assert r.exit_status == ExitStatus.SUCCESS
assert not check_update_warnings(r)
@pytest.fixture
def with_warnings(tmp_path):
env = PersistentMockEnvironment()
env.config['version_info_file'] = tmp_path / 'version.json'
env.config['disable_update_warnings'] = False
return env
@pytest.fixture
def without_warnings(tmp_path):
env = PersistentMockEnvironment()
env.config['version_info_file'] = tmp_path / 'version.json'
env.config['disable_update_warnings'] = True
return env
@pytest.fixture
def fetch_update_mock(mocker):
mock_fetch = mocker.patch('httpie.internal.update_warnings.fetch_updates')
return mock_fetch
@pytest.fixture
def static_fetch_data(mocker):
mock_get = mocker.patch('requests.get')
mock_get.return_value.status_code = 200
mock_get.return_value.json.return_value = {
BUILD_CHANNEL: HIGHEST_VERSION,
BUILD_CHANNEL_2: LOWEST_VERSION,
}
return mock_get
@pytest.fixture
def unknown_build_channel(mocker):
mocker.patch('httpie.internal.update_warnings.BUILD_CHANNEL', UNKNOWN_BUILD_CHANNEL)
@pytest.fixture
def higher_build_channel(mocker):
mocker.patch('httpie.internal.update_warnings.BUILD_CHANNEL', BUILD_CHANNEL)
@pytest.fixture
def lower_build_channel(mocker):
mocker.patch('httpie.internal.update_warnings.BUILD_CHANNEL', BUILD_CHANNEL_2)