summaryrefslogtreecommitdiff
path: root/tests/unit/test_link.py
blob: df4957d59749ca0b3ba9f0582d4a899abf00fc80 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
from typing import Optional

import pytest

from pip._internal.models.link import Link, links_equivalent
from pip._internal.utils.hashes import Hashes


class TestLink:
    @pytest.mark.parametrize(
        "url, expected",
        [
            (
                "https://user:password@example.com/path/page.html",
                "<Link https://user:****@example.com/path/page.html>",
            ),
        ],
    )
    def test_repr(self, url: str, expected: str) -> None:
        link = Link(url)
        assert repr(link) == expected

    @pytest.mark.parametrize(
        "url, expected",
        [
            ("http://yo/wheel.whl", "wheel.whl"),
            ("http://yo/wheel", "wheel"),
            ("https://example.com/path/page.html", "page.html"),
            # Test a quoted character.
            ("https://example.com/path/page%231.html", "page#1.html"),
            (
                "http://yo/myproject-1.0%2Bfoobar.0-py2.py3-none-any.whl",
                "myproject-1.0+foobar.0-py2.py3-none-any.whl",
            ),
            # Test a path that ends in a slash.
            ("https://example.com/path/", "path"),
            ("https://example.com/path//", "path"),
            # Test a url with no filename.
            ("https://example.com/", "example.com"),
            # Test a url with no filename and with auth information.
            (
                "https://user:password@example.com/",
                "example.com",
            ),
        ],
    )
    def test_filename(self, url: str, expected: str) -> None:
        link = Link(url)
        assert link.filename == expected

    def test_splitext(self) -> None:
        assert ("wheel", ".whl") == Link("http://yo/wheel.whl").splitext()

    def test_no_ext(self) -> None:
        assert "" == Link("http://yo/wheel").ext

    def test_ext(self) -> None:
        assert ".whl" == Link("http://yo/wheel.whl").ext

    def test_ext_fragment(self) -> None:
        assert ".whl" == Link("http://yo/wheel.whl#frag").ext

    def test_ext_query(self) -> None:
        assert ".whl" == Link("http://yo/wheel.whl?a=b").ext

    def test_is_wheel(self) -> None:
        assert Link("http://yo/wheel.whl").is_wheel

    def test_is_wheel_false(self) -> None:
        assert not Link("http://yo/not_a_wheel").is_wheel

    def test_fragments(self) -> None:
        url = "git+https://example.com/package#egg=eggname"
        assert "eggname" == Link(url).egg_fragment
        assert None is Link(url).subdirectory_fragment
        url = "git+https://example.com/package#egg=eggname&subdirectory=subdir"
        assert "eggname" == Link(url).egg_fragment
        assert "subdir" == Link(url).subdirectory_fragment
        url = "git+https://example.com/package#subdirectory=subdir&egg=eggname"
        assert "eggname" == Link(url).egg_fragment
        assert "subdir" == Link(url).subdirectory_fragment

        # Extras are supported and preserved in the egg fragment,
        # even the empty extras specifier.
        # This behavior is deprecated and will change in pip 25.
        url = "git+https://example.com/package#egg=eggname[extra]"
        assert "eggname[extra]" == Link(url).egg_fragment
        assert None is Link(url).subdirectory_fragment
        url = "git+https://example.com/package#egg=eggname[extra1,extra2]"
        assert "eggname[extra1,extra2]" == Link(url).egg_fragment
        assert None is Link(url).subdirectory_fragment
        url = "git+https://example.com/package#egg=eggname[]"
        assert "eggname[]" == Link(url).egg_fragment
        assert None is Link(url).subdirectory_fragment

    @pytest.mark.xfail(reason="Behavior change scheduled for 25.0", strict=True)
    @pytest.mark.parametrize(
        "fragment",
        [
            # Package names in egg fragments must be in PEP 508 form.
            "~invalid~package~name~",
            # Version specifiers are not valid in egg fragments.
            "eggname==1.2.3",
            "eggname>=1.2.3",
            # The extras specifier must be in PEP 508 form.
            "eggname[!]",
        ],
    )
    def test_invalid_egg_fragments(self, fragment: str) -> None:
        url = f"git+https://example.com/package#egg={fragment}"
        with pytest.raises(Exception):
            Link(url)

    @pytest.mark.parametrize(
        "yanked_reason, expected",
        [
            (None, False),
            ("", True),
            ("there was a mistake", True),
        ],
    )
    def test_is_yanked(self, yanked_reason: Optional[str], expected: bool) -> None:
        link = Link(
            "https://example.com/wheel.whl",
            yanked_reason=yanked_reason,
        )
        assert link.is_yanked == expected

    @pytest.mark.parametrize(
        "hash_name, hex_digest, expected",
        [
            # Test a value that matches but with the wrong hash_name.
            ("sha384", 128 * "a", False),
            # Test matching values, including values other than the first.
            ("sha512", 128 * "a", True),
            ("sha512", 128 * "b", True),
            # Test a matching hash_name with a value that doesn't match.
            ("sha512", 128 * "c", False),
            # Test a link without a hash value.
            ("sha512", "", False),
        ],
    )
    def test_is_hash_allowed(
        self, hash_name: str, hex_digest: str, expected: bool
    ) -> None:
        url = "https://example.com/wheel.whl#{hash_name}={hex_digest}".format(
            hash_name=hash_name,
            hex_digest=hex_digest,
        )
        link = Link(url)
        hashes_data = {
            "sha512": [128 * "a", 128 * "b"],
        }
        hashes = Hashes(hashes_data)
        assert link.is_hash_allowed(hashes) == expected

    def test_is_hash_allowed__no_hash(self) -> None:
        link = Link("https://example.com/wheel.whl")
        hashes_data = {
            "sha512": [128 * "a"],
        }
        hashes = Hashes(hashes_data)
        assert not link.is_hash_allowed(hashes)

    @pytest.mark.parametrize(
        "hashes, expected",
        [
            (None, False),
            # Also test a success case to show the test is correct.
            (Hashes({"sha512": [128 * "a"]}), True),
        ],
    )
    def test_is_hash_allowed__none_hashes(
        self, hashes: Optional[Hashes], expected: bool
    ) -> None:
        url = "https://example.com/wheel.whl#sha512={}".format(128 * "a")
        link = Link(url)
        assert link.is_hash_allowed(hashes) == expected

    @pytest.mark.parametrize(
        "url, expected",
        [
            ("git+https://github.com/org/repo", True),
            ("bzr+http://bzr.myproject.org/MyProject/trunk/#egg=MyProject", True),
            ("hg+file://hg.company.com/repo", True),
            ("https://example.com/some.whl", False),
            ("file://home/foo/some.whl", False),
        ],
    )
    def test_is_vcs(self, url: str, expected: bool) -> None:
        link = Link(url)
        assert link.is_vcs is expected


@pytest.mark.parametrize(
    "url1, url2",
    [
        pytest.param(
            "https://example.com/foo#egg=foo",
            "https://example.com/foo",
            id="drop-egg",
        ),
        pytest.param(
            "https://example.com/foo#subdirectory=bar&egg=foo",
            "https://example.com/foo#subdirectory=bar&egg=bar",
            id="drop-egg-only",
        ),
        pytest.param(
            "https://example.com/foo#subdirectory=bar&egg=foo",
            "https://example.com/foo#egg=foo&subdirectory=bar",
            id="fragment-ordering",
        ),
        pytest.param(
            "https://example.com/foo?a=1&b=2",
            "https://example.com/foo?b=2&a=1",
            id="query-opordering",
        ),
    ],
)
def test_links_equivalent(url1: str, url2: str) -> None:
    assert links_equivalent(Link(url1), Link(url2))


@pytest.mark.parametrize(
    "url1, url2",
    [
        pytest.param(
            "https://example.com/foo#sha512=1234567890abcdef",
            "https://example.com/foo#sha512=abcdef1234567890",
            id="different-keys",
        ),
        pytest.param(
            "https://example.com/foo#sha512=1234567890abcdef",
            "https://example.com/foo#md5=1234567890abcdef",
            id="different-values",
        ),
        pytest.param(
            "https://example.com/foo#subdirectory=bar&egg=foo",
            "https://example.com/foo#subdirectory=rex",
            id="drop-egg-still-different",
        ),
    ],
)
def test_links_equivalent_false(url1: str, url2: str) -> None:
    assert not links_equivalent(Link(url1), Link(url2))