mirror of
https://github.com/httpie/cli.git
synced 2024-11-22 15:53:13 +01:00
238 lines
6.5 KiB
Python
238 lines
6.5 KiB
Python
|
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)
|