1"""Tests for `chives.urls`."""
3from pathlib import Path
6from vcr.cassette import Cassette
8from chives.urls import (
12 parse_mastodon_post_url,
13 parse_tumblr_post_url,
17@pytest.mark.parametrize(
21 "https://www.youtube.com/watch?v=2OHPPSew2nY&list=WL&index=6&t=193s",
22 "https://www.youtube.com/watch?v=2OHPPSew2nY",
25 "https://www.youtube.com/watch?v=2OHPPSew2nY",
26 "https://www.youtube.com/watch?v=2OHPPSew2nY",
29 "https://www.youtube.com/watch?v=WiIi7STG3e0&start_radio=1",
30 "https://www.youtube.com/watch?v=WiIi7STG3e0",
33 "https://www.youtube.com/watch?app=desktop&v=vQ4ATsyD4hA",
34 "https://www.youtube.com/watch?v=vQ4ATsyD4hA",
38def test_clean_youtube_url(url: str, cleaned_url: str) -> None:
40 All the query parameters get stripped from YouTube URLs correctly.
42 assert clean_youtube_url(url) == cleaned_url
45@pytest.mark.parametrize(
46 "url, server, acct, post_id",
49 "https://iconfactory.world/@Iconfactory/115650922400392083",
56 "https://social.alexwlchan.net/@chris__martin@functional.cafe/113369395383537892",
60 id="alexwlchan_redirect",
63 "https://social.alexwlchan.net/@alex/116300317590482708",
64 "social.alexwlchan.net",
71def test_parse_mastodon_post_url(
72 vcr_cassette: Cassette, url: str, server: str, acct: str, post_id: str
75 Mastodon post URLs are parsed correctly.
77 assert parse_mastodon_post_url(url) == (server, acct, post_id)
80@pytest.mark.parametrize(
84 "https://mastodon.social/", "Cannot parse Mastodon URL", id="no_path"
87 "https://mastodon.social/about",
88 "Cannot parse Mastodon URL",
92 "https://mastodon.social/about/subdir", "Cannot find `acct`", id="no_acct"
95 "https://mastodon.social/@example/about",
96 "Mastodon post ID is not numeric",
97 id="non_numeric_post_id",
100 "https://social.alexwlchan.net/@does@not.exist/123",
101 "Cannot parse Mastodon URL",
102 id="alexwlchan_does_not_exist",
106def test_parse_mastodon_post_url_errors(
107 vcr_cassette: Cassette, url: str, error: str
110 parse_mastodon_post_url returns a useful error if it can't parse the URL.
112 with pytest.raises(ValueError, match=error):
113 parse_mastodon_post_url(url)
116@pytest.mark.parametrize(
117 "url, blog_identifier, post_id",
120 "https://www.tumblr.com/kynvillingur/792473255236796416/",
122 "792473255236796416",
125 "https://cut3panda.tumblr.com/post/94093772689/for-some-people-the-more-you-get-to-know-them",
131def test_parse_tumblr_post_url(url: str, blog_identifier: str, post_id: str) -> None:
133 Tumblr URLs are parsed correctly.
135 assert parse_tumblr_post_url(url) == (blog_identifier, post_id)
138@pytest.mark.parametrize(
141 "https://www.tumblr.com/",
142 "https://www.tumblr.com/staff/",
143 "https://staff.tumblr.com/",
144 "https://www.example.com/",
147def test_parse_bad_tumblr_url(url: str) -> None:
149 Parsing a non-Tumblr URL throws a ValueError.
151 with pytest.raises(ValueError, match="Cannot parse Tumblr URL"):
152 parse_tumblr_post_url(url)
155class TestIsMastodonHost:
157 Tests for `is_mastodon_host`.
160 @pytest.mark.parametrize(
161 "host", ["mastodon.social", "hachyderm.io", "social.jvns.ca"]
163 def test_mastodon_servers(self, host: str, vcr_cassette: Cassette) -> None:
165 It correctly identifies real Mastodon servers.
167 assert is_mastodon_host(host)
169 @pytest.mark.parametrize(
172 # These are regular Internet websites which don't expose
173 # the /.well-known/nodeinfo endpoint
177 # PeerTube exposes /.well-known/nodeinfo, but it's running
178 # different software.
181 # A website with a known bad SSL certificate, which is assumed
182 # not to be a Mastodon host because we can't connect to it.
183 "expired.badssl.com",
186 def test_non_mastodon_servers(self, host: str, vcr_cassette: Cassette) -> None:
188 Other websites are not Mastodon servers.
190 assert not is_mastodon_host(host)
195 Tests for `is_url_safe`.
198 @pytest.mark.parametrize("path", ["example.txt", Path("a/b/cat.jpg")])
199 def test_safe(self, path: str | Path) -> None:
200 """Paths which are URL safe."""
201 assert is_url_safe(path)
203 @pytest.mark.parametrize("path", ["is it?", Path("cat%c.jpg"), "a#b"])
204 def test_unsafe(self, path: str | Path) -> None:
205 """Paths which are not URL safe."""
206 assert not is_url_safe(path)