1- use std:: env;
1+ use std:: { env, io } ;
22
3- use assert_fs:: fixture:: { FileWriteStr , PathChild } ;
3+ use assert_fs:: fixture:: { ChildPath , FileWriteStr , PathChild } ;
44use http:: StatusCode ;
55use serde_json:: json;
66use wiremock:: matchers:: method;
77use wiremock:: { Mock , MockServer , ResponseTemplate } ;
88
99use crate :: common:: { TestContext , uv_snapshot} ;
1010
11- /// Check the simple index error message when the server returns HTTP status 500, a retryable error.
12- #[ tokio:: test]
13- async fn simple_http_500 ( ) {
14- let context = TestContext :: new ( "3.12" ) ;
11+ fn connection_reset ( _request : & wiremock:: Request ) -> io:: Error {
12+ io:: Error :: new ( io:: ErrorKind :: ConnectionReset , "Connection reset by peer" )
13+ }
1514
15+ /// Answers with a retryable HTTP status 500.
16+ async fn http_error_server ( ) -> ( MockServer , String ) {
1617 let server = MockServer :: start ( ) . await ;
1718 Mock :: given ( method ( "GET" ) )
1819 . respond_with ( ResponseTemplate :: new ( StatusCode :: INTERNAL_SERVER_ERROR ) )
1920 . mount ( & server)
2021 . await ;
22+
23+ let mock_server_uri = server. uri ( ) ;
24+ ( server, mock_server_uri)
25+ }
26+
27+ /// Answers with a retryable connection reset IO error.
28+ async fn io_error_server ( ) -> ( MockServer , String ) {
29+ let server = MockServer :: start ( ) . await ;
30+ Mock :: given ( method ( "GET" ) )
31+ . respond_with_err ( connection_reset)
32+ . mount ( & server)
33+ . await ;
34+
2135 let mock_server_uri = server. uri ( ) ;
36+ ( server, mock_server_uri)
37+ }
38+
39+ /// Check the simple index error message when the server returns HTTP status 500, a retryable error.
40+ #[ tokio:: test]
41+ async fn simple_http_500 ( ) {
42+ let context = TestContext :: new ( "3.12" ) ;
43+
44+ let ( _server, mock_server_uri) = http_error_server ( ) . await ;
2245
2346 let filters = vec ! [ ( mock_server_uri. as_str( ) , "[SERVER]" ) ] ;
2447 uv_snapshot ! ( filters, context
2548 . pip_install( )
2649 . arg( "tqdm" )
2750 . arg( "--index-url" )
28- . arg( server . uri ( ) ) , @r"
51+ . arg( & mock_server_uri ) , @r"
2952 success: false
3053 exit_code: 2
3154 ----- stdout -----
@@ -36,25 +59,46 @@ async fn simple_http_500() {
3659 " ) ;
3760}
3861
62+ /// Check the simple index error message when the server returns a retryable IO error.
63+ #[ tokio:: test]
64+ async fn simple_io_err ( ) {
65+ let context = TestContext :: new ( "3.12" ) ;
66+
67+ let ( _server, mock_server_uri) = io_error_server ( ) . await ;
68+
69+ let filters = vec ! [ ( mock_server_uri. as_str( ) , "[SERVER]" ) ] ;
70+ uv_snapshot ! ( filters, context
71+ . pip_install( )
72+ . arg( "tqdm" )
73+ . arg( "--index-url" )
74+ . arg( & mock_server_uri) , @r"
75+ success: false
76+ exit_code: 2
77+ ----- stdout -----
78+
79+ ----- stderr -----
80+ error: Failed to fetch: `[SERVER]/tqdm/`
81+ Caused by: Request failed after 3 retries
82+ Caused by: error sending request for url ([SERVER]/tqdm/)
83+ Caused by: client error (SendRequest)
84+ Caused by: connection closed before message completed
85+ " ) ;
86+ }
87+
3988/// Check the find links error message when the server returns HTTP status 500, a retryable error.
4089#[ tokio:: test]
4190async fn find_links_http_500 ( ) {
4291 let context = TestContext :: new ( "3.12" ) ;
4392
44- let server = MockServer :: start ( ) . await ;
45- Mock :: given ( method ( "GET" ) )
46- . respond_with ( ResponseTemplate :: new ( StatusCode :: INTERNAL_SERVER_ERROR ) )
47- . mount ( & server)
48- . await ;
49- let mock_server_uri = server. uri ( ) ;
93+ let ( _server, mock_server_uri) = http_error_server ( ) . await ;
5094
5195 let filters = vec ! [ ( mock_server_uri. as_str( ) , "[SERVER]" ) ] ;
5296 uv_snapshot ! ( filters, context
5397 . pip_install( )
5498 . arg( "tqdm" )
5599 . arg( "--no-index" )
56100 . arg( "--find-links" )
57- . arg( server . uri ( ) ) , @r"
101+ . arg( & mock_server_uri ) , @r"
58102 success: false
59103 exit_code: 2
60104 ----- stdout -----
@@ -66,18 +110,41 @@ async fn find_links_http_500() {
66110 " ) ;
67111}
68112
113+ /// Check the find links error message when the server returns a retryable IO error.
114+ #[ tokio:: test]
115+ async fn find_links_io_error ( ) {
116+ let context = TestContext :: new ( "3.12" ) ;
117+
118+ let ( _server, mock_server_uri) = io_error_server ( ) . await ;
119+
120+ let filters = vec ! [ ( mock_server_uri. as_str( ) , "[SERVER]" ) ] ;
121+ uv_snapshot ! ( filters, context
122+ . pip_install( )
123+ . arg( "tqdm" )
124+ . arg( "--no-index" )
125+ . arg( "--find-links" )
126+ . arg( & mock_server_uri) , @r"
127+ success: false
128+ exit_code: 2
129+ ----- stdout -----
130+
131+ ----- stderr -----
132+ error: Failed to read `--find-links` URL: [SERVER]/
133+ Caused by: Failed to fetch: `[SERVER]/`
134+ Caused by: Request failed after 3 retries
135+ Caused by: error sending request for url ([SERVER]/)
136+ Caused by: client error (SendRequest)
137+ Caused by: connection closed before message completed
138+ " ) ;
139+ }
140+
69141/// Check the direct package URL error message when the server returns HTTP status 500, a retryable
70142/// error.
71143#[ tokio:: test]
72144async fn direct_url_http_500 ( ) {
73145 let context = TestContext :: new ( "3.12" ) ;
74146
75- let server = MockServer :: start ( ) . await ;
76- Mock :: given ( method ( "GET" ) )
77- . respond_with ( ResponseTemplate :: new ( StatusCode :: INTERNAL_SERVER_ERROR ) )
78- . mount ( & server)
79- . await ;
80- let mock_server_uri = server. uri ( ) ;
147+ let ( _server, mock_server_uri) = http_error_server ( ) . await ;
81148
82149 let tqdm_url = format ! (
83150 "{mock_server_uri}/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl"
@@ -97,22 +164,35 @@ async fn direct_url_http_500() {
97164 " ) ;
98165}
99166
100- /// Check the Python install error message when the server returns HTTP status 500, a retryable
101- /// error.
167+ /// Check the direct package URL error message when the server returns a retryable IO error.
102168#[ tokio:: test]
103- async fn python_install_http_500 ( ) {
104- let context = TestContext :: new ( "3.12" )
105- . with_filtered_python_keys ( )
106- . with_filtered_exe_suffix ( )
107- . with_managed_python_dirs ( ) ;
169+ async fn direct_url_io_error ( ) {
170+ let context = TestContext :: new ( "3.12" ) ;
108171
109- let server = MockServer :: start ( ) . await ;
110- Mock :: given ( method ( "GET" ) )
111- . respond_with ( ResponseTemplate :: new ( StatusCode :: INTERNAL_SERVER_ERROR ) )
112- . mount ( & server)
113- . await ;
114- let mock_server_uri = server. uri ( ) ;
172+ let ( _server, mock_server_uri) = io_error_server ( ) . await ;
115173
174+ let tqdm_url = format ! (
175+ "{mock_server_uri}/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl"
176+ ) ;
177+ let filters = vec ! [ ( mock_server_uri. as_str( ) , "[SERVER]" ) ] ;
178+ uv_snapshot ! ( filters, context
179+ . pip_install( )
180+ . arg( format!( "tqdm @ {tqdm_url}" ) ) , @r"
181+ success: false
182+ exit_code: 1
183+ ----- stdout -----
184+
185+ ----- stderr -----
186+ × Failed to download `tqdm @ [SERVER]/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl`
187+ ├─▶ Failed to fetch: `[SERVER]/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl`
188+ ├─▶ Request failed after 3 retries
189+ ├─▶ error sending request for url ([SERVER]/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl)
190+ ├─▶ client error (SendRequest)
191+ ╰─▶ connection closed before message completed
192+ " ) ;
193+ }
194+
195+ fn write_python_downloads_json ( context : & TestContext , mock_server_uri : & String ) -> ChildPath {
116196 let python_downloads_json = context. temp_dir . child ( "python_downloads.json" ) ;
117197 let interpreter = json ! ( {
118198 "cpython-3.10.0-darwin-aarch64-none" : {
@@ -135,6 +215,50 @@ async fn python_install_http_500() {
135215 python_downloads_json
136216 . write_str ( & serde_json:: to_string ( & interpreter) . unwrap ( ) )
137217 . unwrap ( ) ;
218+ python_downloads_json
219+ }
220+
221+ /// Check the Python install error message when the server returns HTTP status 500, a retryable
222+ /// error.
223+ #[ tokio:: test]
224+ async fn python_install_http_500 ( ) {
225+ let context = TestContext :: new ( "3.12" )
226+ . with_filtered_python_keys ( )
227+ . with_filtered_exe_suffix ( )
228+ . with_managed_python_dirs ( ) ;
229+
230+ let ( _server, mock_server_uri) = http_error_server ( ) . await ;
231+
232+ let python_downloads_json = write_python_downloads_json ( & context, & mock_server_uri) ;
233+
234+ let filters = vec ! [ ( mock_server_uri. as_str( ) , "[SERVER]" ) ] ;
235+ uv_snapshot ! ( filters, context
236+ . python_install( )
237+ . arg( "cpython-3.10.0-darwin-aarch64-none" )
238+ . arg( "--python-downloads-json-url" )
239+ . arg( python_downloads_json. path( ) ) , @r"
240+ success: false
241+ exit_code: 1
242+ ----- stdout -----
243+
244+ ----- stderr -----
245+ error: Failed to install cpython-3.10.0-macos-aarch64-none
246+ Caused by: Failed to download [SERVER]/astral-sh/python-build-standalone/releases/download/20211017/cpython-3.10.0-aarch64-apple-darwin-pgo%2Blto-20211017T1616.tar.zst
247+ Caused by: HTTP status server error (500 Internal Server Error) for url ([SERVER]/astral-sh/python-build-standalone/releases/download/20211017/cpython-3.10.0-aarch64-apple-darwin-pgo%2Blto-20211017T1616.tar.zst)
248+ " ) ;
249+ }
250+
251+ /// Check the Python install error message when the server returns a retryable IO error.
252+ #[ tokio:: test]
253+ async fn python_install_io_error ( ) {
254+ let context = TestContext :: new ( "3.12" )
255+ . with_filtered_python_keys ( )
256+ . with_filtered_exe_suffix ( )
257+ . with_managed_python_dirs ( ) ;
258+
259+ let ( _server, mock_server_uri) = http_error_server ( ) . await ;
260+
261+ let python_downloads_json = write_python_downloads_json ( & context, & mock_server_uri) ;
138262
139263 let filters = vec ! [ ( mock_server_uri. as_str( ) , "[SERVER]" ) ] ;
140264 uv_snapshot ! ( filters, context
0 commit comments