@@ -67,6 +67,88 @@ def test_url_no_authority() -> None:
6767 assert url .path == "/"
6868
6969
70+ def test_origin () -> None :
71+ url = httpx2 .URL ("https://user:password@example.com/path?query#fragment" )
72+
73+ assert url .origin == httpx2 .Origin ("https://example.com" )
74+ assert url .origin .scheme == "https"
75+ assert url .origin .host == "example.com"
76+ assert url .origin .port == 443
77+ assert repr (url .origin ) == "Origin(scheme='https', host='example.com', port=443)"
78+
79+
80+ def test_origin_is_immutable_and_hashable () -> None :
81+ origin = httpx2 .Origin ("https://example.com" )
82+
83+ with pytest .raises (AttributeError ):
84+ origin .host = "other.example.com" # type: ignore[misc]
85+
86+ assert {origin , httpx2 .Origin ("HTTPS://EXAMPLE.COM:443" )} == {origin }
87+
88+
89+ @pytest .mark .parametrize (
90+ "url,scheme,port" ,
91+ [
92+ ("ftp://example.com" , "ftp" , 21 ),
93+ ("http://example.com" , "http" , 80 ),
94+ ("https://example.com" , "https" , 443 ),
95+ ("ws://example.com/socket" , "ws" , 80 ),
96+ ("wss://example.com/socket" , "wss" , 443 ),
97+ ("custom://example.com" , "custom" , None ),
98+ ("custom://example.com:1234" , "custom" , 1234 ),
99+ ],
100+ )
101+ def test_origin_effective_port (url : str , scheme : str , port : int | None ) -> None :
102+ origin = httpx2 .Origin (url )
103+
104+ assert origin .scheme == scheme
105+ assert origin .port == port
106+
107+
108+ @pytest .mark .parametrize (
109+ "left,right" ,
110+ [
111+ ("https://example.com" , "HTTPS://EXAMPLE.COM:443" ),
112+ ("https://中国.icom.museum" , "https://xn--fiqs8s.icom.museum:443" ),
113+ ("ws://example.com/socket" , "ws://example.com:80/other" ),
114+ ("wss://example.com/socket" , "wss://example.com:443/other" ),
115+ ("https://[::1]" , "https://[0:0:0:0:0:0:0:1]" ),
116+ ("https://[2001:db8::1]" , "https://[2001:0db8:0:0:0:0:0:1]" ),
117+ ("https://[::ffff:192.168.0.1]" , "https://[::ffff:c0a8:1]" ),
118+ ("https://[fe80::1%25eth0]" , "https://[fe80:0:0:0:0:0:0:1%25eth0]" ),
119+ ],
120+ )
121+ def test_equivalent_origins (left : str , right : str ) -> None :
122+ assert httpx2 .URL (left ).origin == httpx2 .URL (right ).origin
123+
124+
125+ @pytest .mark .parametrize (
126+ "left,right" ,
127+ [
128+ ("http://example.com" , "https://example.com" ),
129+ ("http://example.com" , "ws://example.com" ),
130+ ("https://example.com" , "wss://example.com" ),
131+ ("https://example.com" , "https://example.com:444" ),
132+ ("https://[::1]" , "https://[::2]" ),
133+ ("https://[fe80::1%25eth0]" , "https://[fe80::1%25eth1]" ),
134+ ("https://[fe80::1%eth0]" , "https://[fe80::1%25eth0]" ),
135+ ("https://[2001:db8::1%30]" , "https://[2001:db8::10]" ),
136+ ],
137+ )
138+ def test_distinct_origins (left : str , right : str ) -> None :
139+ assert httpx2 .URL (left ).origin != httpx2 .URL (right ).origin
140+
141+
142+ @pytest .mark .parametrize ("url" , ["" , "/path" , "mailto:user@example.com" , "file:///tmp/example" ])
143+ def test_relative_url_does_not_have_origin (url : str ) -> None :
144+ with pytest .raises (ValueError , match = "URL must be absolute to have an origin" ):
145+ _ = httpx2 .URL (url ).origin
146+
147+
148+ def test_origin_preserves_percent_escaped_ipv6_scope () -> None :
149+ assert httpx2 .Origin ("https://[2001:db8::1%2e]" ).host == "2001:db8::1%2e"
150+
151+
70152# Tests for percent encoding across path, query, and fragment...
71153
72154
0 commit comments