mirror of
https://github.com/pypa/pip
synced 2023-12-13 21:30:23 +01:00
* Remove reference to utcnow This cleans up some of the datetime handling in the self check. Note that this changes the format of the state file, since the datetime now uses ``.isoformat()`` instead of ``.strftime``. Reading an outdated state file will still work on Python 3.11+, but not on earlier versions. * Use aware datetime object in x509.CertificateBuilder
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(dict(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",
|
|
}
|