def test_get_applicable_candidates__hashes( self, specifier, expected_versions, ): """ Test a non-None hashes value. """ candidates = [ make_mock_candidate('1.0'), make_mock_candidate('1.1', hex_digest=(64 * 'a')), make_mock_candidate('1.2', hex_digest=(64 * 'b')), ] hashes_data = { 'sha256': [64 * 'b'], } hashes = Hashes(hashes_data) evaluator = CandidateEvaluator.create( 'my-project', specifier=specifier, hashes=hashes, ) actual = evaluator.get_applicable_candidates(candidates) actual_versions = [str(c.version) for c in actual] assert actual_versions == expected_versions
def test_get_applicable_candidates__hashes( self, specifier: SpecifierSet, expected_versions: List[str], ) -> None: """ Test a non-None hashes value. """ candidates = [ make_mock_candidate("1.0"), make_mock_candidate("1.1", hex_digest=(64 * "a")), make_mock_candidate("1.2", hex_digest=(64 * "b")), ] hashes_data = { "sha256": [64 * "b"], } hashes = Hashes(hashes_data) evaluator = CandidateEvaluator.create( "my-project", specifier=specifier, hashes=hashes, ) actual = evaluator.get_applicable_candidates(candidates) actual_versions = [str(c.version) for c in actual] assert actual_versions == expected_versions
def test_sort_best_candidate__all_yanked(self, caplog, monkeypatch): """ Test all candidates yanked. """ # Ignore spurious DEBUG level messages # TODO: Probably better to work out why they are occurring, but IMO the # tests are at fault here for being to dependent on exact output. caplog.set_level(logging.WARNING) candidates = [ make_mock_candidate('1.0', yanked_reason='bad metadata #1'), # Put the best candidate in the middle, to test sorting. make_mock_candidate('3.0', yanked_reason='bad metadata #3'), make_mock_candidate('2.0', yanked_reason='bad metadata #2'), ] ireq = install_req_from_line("pkg") resolver = self._make_test_resolver(monkeypatch, candidates) resolver._populate_link(ireq) assert ireq.link == candidates[1].link # Check the log messages. assert len(caplog.records) == 1 record = caplog.records[0] assert record.levelname == 'WARNING' assert record.message == ( 'The candidate selected for download or install is a yanked ' "version: 'mypackage' candidate " '(version 3.0 at https://example.com/pkg-3.0.tar.gz)\n' 'Reason for being yanked: bad metadata #3')
def test_sort_best_candidate__all_yanked(self, caplog, monkeypatch): """ Test all candidates yanked. """ candidates = [ make_mock_candidate('1.0', yanked_reason='bad metadata #1'), # Put the best candidate in the middle, to test sorting. make_mock_candidate('3.0', yanked_reason='bad metadata #3'), make_mock_candidate('2.0', yanked_reason='bad metadata #2'), ] ireq = install_req_from_line("pkg") resolver = self._make_test_resolver(monkeypatch, candidates) resolver._populate_link(ireq) assert ireq.link == candidates[1].link # Check the log messages. assert len(caplog.records) == 1 record = caplog.records[0] assert record.levelname == 'WARNING' assert record.message == ( 'The candidate selected for download or install is a yanked ' "version: 'mypackage' candidate " '(version 3.0 at https://example.com/pkg-3.0.tar.gz)\n' 'Reason for being yanked: bad metadata #3' )
def test_filter_unallowed_hashes__log_message_with_no_match( caplog: pytest.LogCaptureFixture, ) -> None: caplog.set_level(logging.DEBUG) candidates = [ make_mock_candidate("1.0"), make_mock_candidate("1.1", hex_digest=(64 * "b")), make_mock_candidate("1.2", hex_digest=(64 * "c")), ] hashes_data = { "sha256": [64 * "a", 64 * "d"], } hashes = Hashes(hashes_data) actual = filter_unallowed_hashes( candidates, hashes=hashes, project_name="my-project", ) assert len(actual) == 3 expected_message = ( "Checked 3 links for project 'my-project' against 2 hashes " "(0 matches, 1 no digest): discarding no candidates" ) check_caplog(caplog, "DEBUG", expected_message)
def test_sort_best_candidate__has_non_yanked(self, caplog, monkeypatch): """ Test unyanked candidate preferred over yanked. """ candidates = [ make_mock_candidate('1.0'), make_mock_candidate('2.0', yanked_reason='bad metadata #2'), ] ireq = install_req_from_line("pkg") resolver = self._make_test_resolver(monkeypatch, candidates) resolver._populate_link(ireq) assert ireq.link == candidates[0].link assert len(caplog.records) == 0
def test_sort_best_candidate__yanked_reason( self, caplog, monkeypatch, yanked_reason, expected_reason, ): """ Test the log message with various reason strings. """ # Ignore spurious DEBUG level messages # TODO: Probably better to work out why they are occurring, but IMO the # tests are at fault here for being to dependent on exact output. caplog.set_level(logging.WARNING) candidates = [ make_mock_candidate('1.0', yanked_reason=yanked_reason), ] ireq = install_req_from_line("pkg") resolver = self._make_test_resolver(monkeypatch, candidates) resolver._populate_link(ireq) assert ireq.link == candidates[0].link assert len(caplog.records) == 1 record = caplog.records[0] assert record.levelname == 'WARNING' expected_message = ( 'The candidate selected for download or install is a yanked ' "version: 'mypackage' candidate " '(version 1.0 at https://example.com/pkg-1.0.tar.gz)\n' 'Reason for being yanked: ') + expected_reason assert record.message == expected_message
def test_sort_best_candidate__yanked_reason( self, caplog, monkeypatch, yanked_reason, expected_reason, ): """ Test the log message with various reason strings. """ candidates = [ make_mock_candidate('1.0', yanked_reason=yanked_reason), ] ireq = install_req_from_line("pkg") resolver = self._make_test_resolver(monkeypatch, candidates) resolver._populate_link(ireq) assert ireq.link == candidates[0].link assert len(caplog.records) == 1 record = caplog.records[0] assert record.levelname == 'WARNING' expected_message = ( 'The candidate selected for download or install is a yanked ' "version: 'mypackage' candidate " '(version 1.0 at https://example.com/pkg-1.0.tar.gz)\n' 'Reason for being yanked: ' ) + expected_reason assert record.message == expected_message
def test_filter_unallowed_hashes(hex_digest, expected_versions): candidates = [ make_mock_candidate('1.0'), make_mock_candidate('1.1', hex_digest=(64 * 'a')), make_mock_candidate('1.2', hex_digest=(64 * 'b')), ] hashes_data = { 'sha256': [hex_digest], } hashes = Hashes(hashes_data) actual = filter_unallowed_hashes( candidates, hashes=hashes, project_name='my-project', ) actual_versions = [str(candidate.version) for candidate in actual] assert actual_versions == expected_versions # Check that the return value is always different from the given value. assert actual is not candidates
def test_sort_key__is_yanked(self, yanked_reason, expected): """ Test the effect of is_yanked on _sort_key()'s return value. """ candidate = make_mock_candidate('1.0', yanked_reason=yanked_reason) evaluator = CandidateEvaluator.create('my-project') sort_value = evaluator._sort_key(candidate) # Yanked / non-yanked is reflected in the second element of the tuple. actual = sort_value[1] assert actual == expected
def test_sort_best_candidate__has_non_yanked(self, caplog, monkeypatch): """ Test unyanked candidate preferred over yanked. """ # Ignore spurious DEBUG level messages # TODO: Probably better to work out why they are occurring, but IMO the # tests are at fault here for being to dependent on exact output. caplog.set_level(logging.WARNING) candidates = [ make_mock_candidate('1.0'), make_mock_candidate('2.0', yanked_reason='bad metadata #2'), ] ireq = install_req_from_line("pkg") resolver = self._make_test_resolver(monkeypatch, candidates) resolver._populate_link(ireq) assert ireq.link == candidates[0].link assert len(caplog.records) == 0
def test_filter_unallowed_hashes(hex_digest: str, expected_versions: List[str]) -> None: candidates = [ make_mock_candidate("1.0"), make_mock_candidate("1.1", hex_digest=(64 * "a")), make_mock_candidate("1.2", hex_digest=(64 * "b")), ] hashes_data = { "sha256": [hex_digest], } hashes = Hashes(hashes_data) actual = filter_unallowed_hashes( candidates, hashes=hashes, project_name="my-project", ) actual_versions = [str(candidate.version) for candidate in actual] assert actual_versions == expected_versions # Check that the return value is always different from the given value. assert actual is not candidates
def test_filter_unallowed_hashes__no_hashes(caplog): caplog.set_level(logging.DEBUG) candidates = [ make_mock_candidate('1.0'), make_mock_candidate('1.1'), ] actual = filter_unallowed_hashes( candidates, hashes=Hashes(), project_name='my-project', ) # Check that the return value is a copy. assert actual == candidates assert actual is not candidates expected_message = ( "Given no hashes to check 2 links for project 'my-project': " "discarding no candidates" ) check_caplog(caplog, 'DEBUG', expected_message)
def test_sort_key__is_yanked(self, yanked_reason: Optional[str], expected: int) -> None: """ Test the effect of is_yanked on _sort_key()'s return value. """ candidate = make_mock_candidate("1.0", yanked_reason=yanked_reason) evaluator = CandidateEvaluator.create("my-project") sort_value = evaluator._sort_key(candidate) # Yanked / non-yanked is reflected in the second element of the tuple. actual = sort_value[1] assert actual == expected
def test_filter_unallowed_hashes__log_message_with_match( caplog: pytest.LogCaptureFixture, ) -> None: caplog.set_level(logging.DEBUG) # Test 1 match, 2 non-matches, 3 no hashes so all 3 values will be # different. candidates = [ make_mock_candidate("1.0"), make_mock_candidate("1.1", ), make_mock_candidate("1.2", ), make_mock_candidate("1.3", hex_digest=(64 * "a")), make_mock_candidate("1.4", hex_digest=(64 * "b")), make_mock_candidate("1.5", hex_digest=(64 * "c")), ] hashes_data = { "sha256": [64 * "a", 64 * "d"], } hashes = Hashes(hashes_data) actual = filter_unallowed_hashes( candidates, hashes=hashes, project_name="my-project", ) assert len(actual) == 4 expected_message = ( "Checked 6 links for project 'my-project' against 2 hashes " "(1 matches, 3 no digest): discarding 2 non-matches:\n" " https://example.com/pkg-1.4.tar.gz#sha256=" "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n" " https://example.com/pkg-1.5.tar.gz#sha256=" "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc") check_caplog(caplog, "DEBUG", expected_message)
def test_filter_unallowed_hashes__log_message_with_match(caplog): caplog.set_level(logging.DEBUG) # Test 1 match, 2 non-matches, 3 no hashes so all 3 values will be # different. candidates = [ make_mock_candidate('1.0'), make_mock_candidate('1.1',), make_mock_candidate('1.2',), make_mock_candidate('1.3', hex_digest=(64 * 'a')), make_mock_candidate('1.4', hex_digest=(64 * 'b')), make_mock_candidate('1.5', hex_digest=(64 * 'c')), ] hashes_data = { 'sha256': [64 * 'a', 64 * 'd'], } hashes = Hashes(hashes_data) actual = filter_unallowed_hashes( candidates, hashes=hashes, project_name='my-project', ) assert len(actual) == 4 expected_message = ( "Checked 6 links for project 'my-project' against 2 hashes " "(1 matches, 3 no digest): discarding 2 non-matches:\n" " https://example.com/pkg-1.4.tar.gz#sha256=" "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n" " https://example.com/pkg-1.5.tar.gz#sha256=" "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc" ) check_caplog(caplog, 'DEBUG', expected_message)
def test_filter_unallowed_hashes__no_hashes( caplog: pytest.LogCaptureFixture) -> None: caplog.set_level(logging.DEBUG) candidates = [ make_mock_candidate("1.0"), make_mock_candidate("1.1"), ] actual = filter_unallowed_hashes( candidates, hashes=Hashes(), project_name="my-project", ) # Check that the return value is a copy. assert actual == candidates assert actual is not candidates expected_message = ( "Given no hashes to check 2 links for project 'my-project': " "discarding no candidates") check_caplog(caplog, "DEBUG", expected_message)
def test_sort_best_candidate__best_yanked_but_not_all( self, caplog, ): """ Test the best candidates being yanked, but not all. """ caplog.set_level(logging.INFO) candidates = [ make_mock_candidate('4.0', yanked_reason='bad metadata #4'), # Put the best candidate in the middle, to test sorting. make_mock_candidate('2.0'), make_mock_candidate('3.0', yanked_reason='bad metadata #3'), make_mock_candidate('1.0'), ] expected_best = candidates[1] evaluator = CandidateEvaluator.create('my-project') actual = evaluator.sort_best_candidate(candidates) assert actual is expected_best assert str(actual.version) == '2.0' # Check the log messages. assert len(caplog.records) == 0
def test_filter_unallowed_hashes__log_message_with_no_match(caplog): caplog.set_level(logging.DEBUG) candidates = [ make_mock_candidate('1.0'), make_mock_candidate('1.1', hex_digest=(64 * 'b')), make_mock_candidate('1.2', hex_digest=(64 * 'c')), ] hashes_data = { 'sha256': [64 * 'a', 64 * 'd'], } hashes = Hashes(hashes_data) actual = filter_unallowed_hashes( candidates, hashes=hashes, project_name='my-project', ) assert len(actual) == 3 expected_message = ( "Checked 3 links for project 'my-project' against 2 hashes " "(0 matches, 1 no digest): discarding no candidates" ) check_caplog(caplog, 'DEBUG', expected_message)
def test_sort_best_candidate__best_yanked_but_not_all( self, caplog: pytest.LogCaptureFixture, ) -> None: """ Test the best candidates being yanked, but not all. """ caplog.set_level(logging.INFO) candidates = [ make_mock_candidate("4.0", yanked_reason="bad metadata #4"), # Put the best candidate in the middle, to test sorting. make_mock_candidate("2.0"), make_mock_candidate("3.0", yanked_reason="bad metadata #3"), make_mock_candidate("1.0"), ] expected_best = candidates[1] evaluator = CandidateEvaluator.create("my-project") actual = evaluator.sort_best_candidate(candidates) assert actual is expected_best assert str(actual.version) == "2.0" # Check the log messages. assert len(caplog.records) == 0
def test_sort_key__hash(self, hex_digest: Optional[str], expected: int) -> None: """ Test the effect of the link's hash on _sort_key()'s return value. """ candidate = make_mock_candidate("1.0", hex_digest=hex_digest) hashes_data = { "sha256": [64 * "a"], } hashes = Hashes(hashes_data) evaluator = CandidateEvaluator.create("my-project", hashes=hashes) sort_value = evaluator._sort_key(candidate) # The hash is reflected in the first element of the tuple. actual = sort_value[0] assert actual == expected
def test_sort_key__hash(self, hex_digest, expected): """ Test the effect of the link's hash on _sort_key()'s return value. """ candidate = make_mock_candidate('1.0', hex_digest=hex_digest) hashes_data = { 'sha256': [64 * 'a'], } hashes = Hashes(hashes_data) evaluator = CandidateEvaluator.create('my-project', hashes=hashes) sort_value = evaluator._sort_key(candidate) # The hash is reflected in the first element of the tuple. actual = sort_value[0] assert actual == expected
def test_get_applicable_candidates(self): specifier = SpecifierSet('<= 1.11') versions = ['1.10', '1.11', '1.12'] candidates = [make_mock_candidate(version) for version in versions] evaluator = CandidateEvaluator.create( 'my-project', specifier=specifier, ) actual = evaluator.get_applicable_candidates(candidates) expected_applicable = candidates[:2] assert [str(c.version) for c in expected_applicable] == [ '1.10', '1.11', ] assert actual == expected_applicable
def test_get_applicable_candidates(self) -> None: specifier = SpecifierSet("<= 1.11") versions = ["1.10", "1.11", "1.12"] candidates = [make_mock_candidate(version) for version in versions] evaluator = CandidateEvaluator.create( "my-project", specifier=specifier, ) actual = evaluator.get_applicable_candidates(candidates) expected_applicable = candidates[:2] assert [str(c.version) for c in expected_applicable] == [ "1.10", "1.11", ] assert actual == expected_applicable
def test_compute_best_candidate__none_best(self): """ Test returning a None best candidate. """ specifier = SpecifierSet('<= 1.10') versions = ['1.11', '1.12'] candidates = [make_mock_candidate(version) for version in versions] evaluator = CandidateEvaluator.create( 'my-project', specifier=specifier, ) result = evaluator.compute_best_candidate(candidates) assert result._candidates == candidates assert result._applicable_candidates == [] assert result.best_candidate is None
def test_compute_best_candidate__none_best(self) -> None: """ Test returning a None best candidate. """ specifier = SpecifierSet("<= 1.10") versions = ["1.11", "1.12"] candidates = [make_mock_candidate(version) for version in versions] evaluator = CandidateEvaluator.create( "my-project", specifier=specifier, ) result = evaluator.compute_best_candidate(candidates) assert result._candidates == candidates assert result._applicable_candidates == [] assert result.best_candidate is None
def test_compute_best_candidate(self): specifier = SpecifierSet('<= 1.11') versions = ['1.10', '1.11', '1.12'] candidates = [make_mock_candidate(version) for version in versions] evaluator = CandidateEvaluator.create( 'my-project', specifier=specifier, ) result = evaluator.compute_best_candidate(candidates) assert result._candidates == candidates expected_applicable = candidates[:2] assert [str(c.version) for c in expected_applicable] == [ '1.10', '1.11', ] assert result._applicable_candidates == expected_applicable assert result.best_candidate is expected_applicable[1]
def test_compute_best_candidate(self) -> None: specifier = SpecifierSet("<= 1.11") versions = ["1.10", "1.11", "1.12"] candidates = [make_mock_candidate(version) for version in versions] evaluator = CandidateEvaluator.create( "my-project", specifier=specifier, ) result = evaluator.compute_best_candidate(candidates) assert result._candidates == candidates expected_applicable = candidates[:2] assert [str(c.version) for c in expected_applicable] == [ "1.10", "1.11", ] assert result._applicable_candidates == expected_applicable assert result.best_candidate is expected_applicable[1]