Skip to content

Commit 93bbfd1

Browse files
committed
Add basic network error tests
Add basic tests for error messages on retryable network errors. This test mod is intended to grow to ensure that we handle retryable errors correctly and that we show the appropriate error message if we failed after retrying. The starter tests show some common cases we've seen download errors in: simple and find links indexes, file downloads and Python installs.
1 parent 67bf3eb commit 93bbfd1

File tree

2 files changed

+157
-1
lines changed

2 files changed

+157
-1
lines changed

crates/uv/tests/it/main.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ mod lock_conflict;
3939

4040
mod lock_scenarios;
4141

42-
mod version;
42+
mod network;
4343

4444
mod pip_check;
4545

@@ -119,6 +119,8 @@ mod tree;
119119
#[cfg(feature = "python")]
120120
mod venv;
121121

122+
mod version;
123+
122124
#[cfg(all(feature = "python", feature = "pypi"))]
123125
mod workflow;
124126

crates/uv/tests/it/network.rs

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
use std::env;
2+
3+
use assert_fs::fixture::{FileWriteStr, PathChild};
4+
use http::StatusCode;
5+
use serde_json::json;
6+
use wiremock::matchers::method;
7+
use wiremock::{Mock, MockServer, ResponseTemplate};
8+
9+
use crate::common::{TestContext, uv_snapshot};
10+
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");
15+
16+
let server = MockServer::start().await;
17+
Mock::given(method("GET"))
18+
.respond_with(ResponseTemplate::new(StatusCode::INTERNAL_SERVER_ERROR))
19+
.mount(&server)
20+
.await;
21+
let mock_server_uri = server.uri();
22+
23+
let filters = vec![(mock_server_uri.as_str(), "[SERVER]")];
24+
uv_snapshot!(filters, context
25+
.pip_install()
26+
.arg("tqdm")
27+
.arg("--index-url")
28+
.arg(server.uri()), @r"
29+
success: false
30+
exit_code: 2
31+
----- stdout -----
32+
33+
----- stderr -----
34+
error: Failed to fetch: `[SERVER]/tqdm/`
35+
Caused by: HTTP status server error (500 Internal Server Error) for url ([SERVER]/tqdm/)
36+
");
37+
}
38+
39+
/// Check the find links error message when the server returns HTTP status 500, a retryable error.
40+
#[tokio::test]
41+
async fn find_links_http_500() {
42+
let context = TestContext::new("3.12");
43+
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();
50+
51+
let filters = vec![(mock_server_uri.as_str(), "[SERVER]")];
52+
uv_snapshot!(filters, context
53+
.pip_install()
54+
.arg("tqdm")
55+
.arg("--no-index")
56+
.arg("--find-links")
57+
.arg(server.uri()), @r"
58+
success: false
59+
exit_code: 2
60+
----- stdout -----
61+
62+
----- stderr -----
63+
error: Failed to read `--find-links` URL: [SERVER]/
64+
Caused by: Failed to fetch: `[SERVER]/`
65+
Caused by: HTTP status server error (500 Internal Server Error) for url ([SERVER]/)
66+
");
67+
}
68+
69+
/// Check the direct package URL error message when the server returns HTTP status 500, a retryable
70+
/// error.
71+
#[tokio::test]
72+
async fn direct_url_http_500() {
73+
let context = TestContext::new("3.12");
74+
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();
81+
82+
let tqdm_url = format!(
83+
"{mock_server_uri}/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl"
84+
);
85+
let filters = vec![(mock_server_uri.as_str(), "[SERVER]")];
86+
uv_snapshot!(filters, context
87+
.pip_install()
88+
.arg(format!("tqdm @ {tqdm_url}")), @r"
89+
success: false
90+
exit_code: 1
91+
----- stdout -----
92+
93+
----- stderr -----
94+
× Failed to download `tqdm @ [SERVER]/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl`
95+
├─▶ Failed to fetch: `[SERVER]/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl`
96+
╰─▶ HTTP status server error (500 Internal Server Error) for url ([SERVER]/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl)
97+
");
98+
}
99+
100+
/// Check the Python install error message when the server returns HTTP status 500, a retryable
101+
/// error.
102+
#[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();
108+
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();
115+
116+
let python_downloads_json = context.temp_dir.child("python_downloads.json");
117+
let interpreter = json!({
118+
"cpython-3.10.0-darwin-aarch64-none": {
119+
"arch": {
120+
"family": "aarch64",
121+
"variant": null
122+
},
123+
"libc": "none",
124+
"major": 3,
125+
"minor": 10,
126+
"name": "cpython",
127+
"os": "darwin",
128+
"patch": 0,
129+
"prerelease": "",
130+
"sha256": null,
131+
"url": format!("{mock_server_uri}/astral-sh/python-build-standalone/releases/download/20211017/cpython-3.10.0-aarch64-apple-darwin-pgo%2Blto-20211017T1616.tar.zst"),
132+
"variant": null
133+
}
134+
});
135+
python_downloads_json
136+
.write_str(&serde_json::to_string(&interpreter).unwrap())
137+
.unwrap();
138+
139+
let filters = vec![(mock_server_uri.as_str(), "[SERVER]")];
140+
uv_snapshot!(filters, context
141+
.python_install()
142+
.arg("cpython-3.10.0-darwin-aarch64-none")
143+
.arg("--python-downloads-json-url")
144+
.arg(python_downloads_json.path()), @r"
145+
success: false
146+
exit_code: 1
147+
----- stdout -----
148+
149+
----- stderr -----
150+
error: Failed to install cpython-3.10.0-macos-aarch64-none
151+
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
152+
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)
153+
");
154+
}

0 commit comments

Comments
 (0)