mirror of https://github.com/pypa/pip
186 lines
5.6 KiB
Python
186 lines
5.6 KiB
Python
import datetime
|
|
import json
|
|
import logging
|
|
import os
|
|
import sys
|
|
from optparse import Values
|
|
from pathlib import Path
|
|
from typing import Optional
|
|
from unittest.mock import ANY, Mock, patch
|
|
|
|
import pytest
|
|
from freezegun import freeze_time
|
|
from pip._vendor.packaging.version import Version
|
|
|
|
from pip._internal import self_outdated_check
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
["key", "expected"],
|
|
[
|
|
(
|
|
"/hello/world/venv",
|
|
"fcd2d5175dd33d5df759ee7b045264230205ef837bf9f582f7c3ada7",
|
|
),
|
|
(
|
|
"C:\\Users\\User\\Desktop\\venv",
|
|
"902cecc0745b8ecf2509ba473f3556f0ba222fedc6df433acda24aa5",
|
|
),
|
|
],
|
|
)
|
|
def test_get_statefile_name_known_values(key: str, expected: str) -> None:
|
|
assert expected == self_outdated_check._get_statefile_name(key)
|
|
|
|
|
|
@freeze_time("1970-01-02T11:00:00Z")
|
|
@patch("pip._internal.self_outdated_check._self_version_check_logic")
|
|
@patch("pip._internal.self_outdated_check.SelfCheckState")
|
|
def test_pip_self_version_check_calls_underlying_implementation(
|
|
mocked_state: Mock, mocked_function: Mock, tmpdir: Path
|
|
) -> None:
|
|
# GIVEN
|
|
mock_session = Mock()
|
|
fake_options = Values({"cache_dir": str(tmpdir)})
|
|
|
|
# WHEN
|
|
self_outdated_check.pip_self_version_check(mock_session, fake_options)
|
|
|
|
# THEN
|
|
mocked_state.assert_called_once_with(cache_dir=str(tmpdir))
|
|
mocked_function.assert_called_once_with(
|
|
state=mocked_state(cache_dir=str(tmpdir)),
|
|
current_time=datetime.datetime(
|
|
1970, 1, 2, 11, 0, 0, tzinfo=datetime.timezone.utc
|
|
),
|
|
local_version=ANY,
|
|
get_remote_version=ANY,
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
[
|
|
"installed_version",
|
|
"remote_version",
|
|
"stored_version",
|
|
"installed_by_pip",
|
|
"should_show_prompt",
|
|
],
|
|
[
|
|
# A newer version available!
|
|
("1.0", "2.0", None, True, True),
|
|
# A newer version available, and cached value is new too!
|
|
("1.0", "2.0", "2.0", True, True),
|
|
# A newer version available, but was not installed by pip.
|
|
("1.0", "2.0", None, False, False),
|
|
# On the latest version already.
|
|
("2.0", "2.0", None, True, False),
|
|
# On the latest version already, and cached value matches.
|
|
("2.0", "2.0", "2.0", True, False),
|
|
# A newer version available, but cached value is older.
|
|
("1.0", "2.0", "1.0", True, False),
|
|
],
|
|
)
|
|
def test_core_logic(
|
|
installed_version: str,
|
|
remote_version: str,
|
|
stored_version: Optional[str],
|
|
installed_by_pip: bool,
|
|
should_show_prompt: bool,
|
|
caplog: pytest.LogCaptureFixture,
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
# GIVEN
|
|
monkeypatch.setattr(
|
|
self_outdated_check, "was_installed_by_pip", lambda _: installed_by_pip
|
|
)
|
|
mock_state = Mock()
|
|
mock_state.get.return_value = stored_version
|
|
fake_time = datetime.datetime(2000, 1, 1, 0, 0, 0)
|
|
version_that_should_be_checked = stored_version or remote_version
|
|
|
|
# WHEN
|
|
with caplog.at_level(logging.DEBUG):
|
|
return_value = self_outdated_check._self_version_check_logic(
|
|
state=mock_state,
|
|
current_time=fake_time,
|
|
local_version=Version(installed_version),
|
|
get_remote_version=lambda: remote_version,
|
|
)
|
|
|
|
# THEN
|
|
mock_state.get.assert_called_once_with(fake_time)
|
|
assert caplog.messages == [
|
|
f"Remote version of pip: {version_that_should_be_checked}",
|
|
f"Local version of pip: {installed_version}",
|
|
f"Was pip installed by pip? {installed_by_pip}",
|
|
]
|
|
|
|
if stored_version:
|
|
mock_state.set.assert_not_called()
|
|
else:
|
|
mock_state.set.assert_called_once_with(
|
|
version_that_should_be_checked, fake_time
|
|
)
|
|
|
|
if not should_show_prompt:
|
|
assert return_value is None
|
|
return # the remaining assertions are for the other case.
|
|
|
|
assert return_value is not None
|
|
assert return_value.old == installed_version
|
|
assert return_value.new == remote_version
|
|
|
|
|
|
class TestSelfCheckState:
|
|
def test_no_cache(self) -> None:
|
|
# GIVEN / WHEN
|
|
state = self_outdated_check.SelfCheckState(cache_dir="")
|
|
assert state._statefile_path is None
|
|
|
|
def test_reads_expected_statefile(self, tmpdir: Path) -> None:
|
|
# GIVEN
|
|
cache_dir = tmpdir / "cache_dir"
|
|
expected_path = (
|
|
cache_dir
|
|
/ "selfcheck"
|
|
/ self_outdated_check._get_statefile_name(sys.prefix)
|
|
)
|
|
|
|
cache_dir.mkdir()
|
|
(cache_dir / "selfcheck").mkdir()
|
|
expected_path.write_text('{"foo": "bar"}')
|
|
|
|
# WHEN
|
|
state = self_outdated_check.SelfCheckState(cache_dir=str(cache_dir))
|
|
|
|
# THEN
|
|
assert state._statefile_path == os.fspath(expected_path)
|
|
assert state._state == {"foo": "bar"}
|
|
|
|
def test_writes_expected_statefile(self, tmpdir: Path) -> None:
|
|
# GIVEN
|
|
cache_dir = tmpdir / "cache_dir"
|
|
cache_dir.mkdir()
|
|
expected_path = (
|
|
cache_dir
|
|
/ "selfcheck"
|
|
/ self_outdated_check._get_statefile_name(sys.prefix)
|
|
)
|
|
|
|
# WHEN
|
|
state = self_outdated_check.SelfCheckState(cache_dir=str(cache_dir))
|
|
state.set(
|
|
"1.0.0",
|
|
datetime.datetime(2000, 1, 1, 0, 0, 0, tzinfo=datetime.timezone.utc),
|
|
)
|
|
|
|
# THEN
|
|
assert state._statefile_path == os.fspath(expected_path)
|
|
|
|
contents = expected_path.read_text()
|
|
assert json.loads(contents) == {
|
|
"key": sys.prefix,
|
|
"last_check": "2000-01-01T00:00:00+00:00",
|
|
"pypi_version": "1.0.0",
|
|
}
|