mirror of
https://github.com/astral-sh/uv.git
synced 2025-08-04 19:08:04 +00:00
Remove separate test files in favor of same-file mod tests
(#9199)
## Summary These were moved as part of a broader refactor to create a single integration test module. That "single integration test module" did indeed have a big impact on compile times, which is great! But we aren't seeing any benefit from moving these tests into their own files (despite the claim in [this blog post](https://matklad.github.io/2021/02/27/delete-cargo-integration-tests.html), I see the same compilation pattern regardless of where the tests are located). Plus, we don't have many of these, and same-file tests is such a strong Rust convention.
This commit is contained in:
parent
747d69dc96
commit
d08bfee718
114 changed files with 15321 additions and 15344 deletions
File diff suppressed because it is too large
Load diff
|
@ -1,995 +0,0 @@
|
|||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn parse_sha256() {
|
||||
let text = r#"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<h1>Links for jinja2</h1>
|
||||
<a href="/whl/Jinja2-3.1.2-py3-none-any.whl#sha256=6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61">Jinja2-3.1.2-py3-none-any.whl</a><br/>
|
||||
</body>
|
||||
</html>
|
||||
<!--TIMESTAMP 1703347410-->
|
||||
"#;
|
||||
let base = Url::parse("https://download.pytorch.org/whl/jinja2/").unwrap();
|
||||
let result = SimpleHtml::parse(text, &base).unwrap();
|
||||
insta::assert_debug_snapshot!(result, @r###"
|
||||
SimpleHtml {
|
||||
base: BaseUrl(
|
||||
Url {
|
||||
scheme: "https",
|
||||
cannot_be_a_base: false,
|
||||
username: "",
|
||||
password: None,
|
||||
host: Some(
|
||||
Domain(
|
||||
"download.pytorch.org",
|
||||
),
|
||||
),
|
||||
port: None,
|
||||
path: "/whl/jinja2/",
|
||||
query: None,
|
||||
fragment: None,
|
||||
},
|
||||
),
|
||||
files: [
|
||||
File {
|
||||
core_metadata: None,
|
||||
dist_info_metadata: None,
|
||||
data_dist_info_metadata: None,
|
||||
filename: "Jinja2-3.1.2-py3-none-any.whl",
|
||||
hashes: Hashes {
|
||||
md5: None,
|
||||
sha256: Some(
|
||||
"6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61",
|
||||
),
|
||||
sha384: None,
|
||||
sha512: None,
|
||||
},
|
||||
requires_python: None,
|
||||
size: None,
|
||||
upload_time: None,
|
||||
url: "/whl/Jinja2-3.1.2-py3-none-any.whl#sha256=6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61",
|
||||
yanked: None,
|
||||
},
|
||||
],
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_md5() {
|
||||
let text = r#"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<h1>Links for jinja2</h1>
|
||||
<a href="/whl/Jinja2-3.1.2-py3-none-any.whl#md5=6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61">Jinja2-3.1.2-py3-none-any.whl</a><br/>
|
||||
</body>
|
||||
</html>
|
||||
<!--TIMESTAMP 1703347410-->
|
||||
"#;
|
||||
let base = Url::parse("https://download.pytorch.org/whl/jinja2/").unwrap();
|
||||
let result = SimpleHtml::parse(text, &base).unwrap();
|
||||
insta::assert_debug_snapshot!(result, @r###"
|
||||
SimpleHtml {
|
||||
base: BaseUrl(
|
||||
Url {
|
||||
scheme: "https",
|
||||
cannot_be_a_base: false,
|
||||
username: "",
|
||||
password: None,
|
||||
host: Some(
|
||||
Domain(
|
||||
"download.pytorch.org",
|
||||
),
|
||||
),
|
||||
port: None,
|
||||
path: "/whl/jinja2/",
|
||||
query: None,
|
||||
fragment: None,
|
||||
},
|
||||
),
|
||||
files: [
|
||||
File {
|
||||
core_metadata: None,
|
||||
dist_info_metadata: None,
|
||||
data_dist_info_metadata: None,
|
||||
filename: "Jinja2-3.1.2-py3-none-any.whl",
|
||||
hashes: Hashes {
|
||||
md5: Some(
|
||||
"6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61",
|
||||
),
|
||||
sha256: None,
|
||||
sha384: None,
|
||||
sha512: None,
|
||||
},
|
||||
requires_python: None,
|
||||
size: None,
|
||||
upload_time: None,
|
||||
url: "/whl/Jinja2-3.1.2-py3-none-any.whl#md5=6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61",
|
||||
yanked: None,
|
||||
},
|
||||
],
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_base() {
|
||||
let text = r#"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<base href="https://index.python.org/">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Links for jinja2</h1>
|
||||
<a href="/whl/Jinja2-3.1.2-py3-none-any.whl#sha256=6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61">Jinja2-3.1.2-py3-none-any.whl</a><br/>
|
||||
</body>
|
||||
</html>
|
||||
<!--TIMESTAMP 1703347410-->
|
||||
"#;
|
||||
let base = Url::parse("https://download.pytorch.org/whl/jinja2/").unwrap();
|
||||
let result = SimpleHtml::parse(text, &base).unwrap();
|
||||
insta::assert_debug_snapshot!(result, @r###"
|
||||
SimpleHtml {
|
||||
base: BaseUrl(
|
||||
Url {
|
||||
scheme: "https",
|
||||
cannot_be_a_base: false,
|
||||
username: "",
|
||||
password: None,
|
||||
host: Some(
|
||||
Domain(
|
||||
"index.python.org",
|
||||
),
|
||||
),
|
||||
port: None,
|
||||
path: "/",
|
||||
query: None,
|
||||
fragment: None,
|
||||
},
|
||||
),
|
||||
files: [
|
||||
File {
|
||||
core_metadata: None,
|
||||
dist_info_metadata: None,
|
||||
data_dist_info_metadata: None,
|
||||
filename: "Jinja2-3.1.2-py3-none-any.whl",
|
||||
hashes: Hashes {
|
||||
md5: None,
|
||||
sha256: Some(
|
||||
"6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61",
|
||||
),
|
||||
sha384: None,
|
||||
sha512: None,
|
||||
},
|
||||
requires_python: None,
|
||||
size: None,
|
||||
upload_time: None,
|
||||
url: "/whl/Jinja2-3.1.2-py3-none-any.whl#sha256=6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61",
|
||||
yanked: None,
|
||||
},
|
||||
],
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_escaped_fragment() {
|
||||
let text = r#"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<h1>Links for jinja2</h1>
|
||||
<a href="/whl/Jinja2-3.1.2+233fca715f49-py3-none-any.whl#sha256=6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61">Jinja2-3.1.2+233fca715f49-py3-none-any.whl</a><br/>
|
||||
</body>
|
||||
</html>
|
||||
<!--TIMESTAMP 1703347410-->
|
||||
"#;
|
||||
let base = Url::parse("https://download.pytorch.org/whl/jinja2/").unwrap();
|
||||
let result = SimpleHtml::parse(text, &base).unwrap();
|
||||
insta::assert_debug_snapshot!(result, @r###"
|
||||
SimpleHtml {
|
||||
base: BaseUrl(
|
||||
Url {
|
||||
scheme: "https",
|
||||
cannot_be_a_base: false,
|
||||
username: "",
|
||||
password: None,
|
||||
host: Some(
|
||||
Domain(
|
||||
"download.pytorch.org",
|
||||
),
|
||||
),
|
||||
port: None,
|
||||
path: "/whl/jinja2/",
|
||||
query: None,
|
||||
fragment: None,
|
||||
},
|
||||
),
|
||||
files: [
|
||||
File {
|
||||
core_metadata: None,
|
||||
dist_info_metadata: None,
|
||||
data_dist_info_metadata: None,
|
||||
filename: "Jinja2-3.1.2+233fca715f49-py3-none-any.whl",
|
||||
hashes: Hashes {
|
||||
md5: None,
|
||||
sha256: Some(
|
||||
"6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61",
|
||||
),
|
||||
sha384: None,
|
||||
sha512: None,
|
||||
},
|
||||
requires_python: None,
|
||||
size: None,
|
||||
upload_time: None,
|
||||
url: "/whl/Jinja2-3.1.2+233fca715f49-py3-none-any.whl#sha256=6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61",
|
||||
yanked: None,
|
||||
},
|
||||
],
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_encoded_fragment() {
|
||||
let text = r#"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<h1>Links for jinja2</h1>
|
||||
<a href="/whl/Jinja2-3.1.2-py3-none-any.whl#sha256%3D4095ada29e51070f7d199a0a5bdf5c8d8e238e03f0bf4dcc02571e78c9ae800d">Jinja2-3.1.2-py3-none-any.whl</a><br/>
|
||||
</body>
|
||||
</html>
|
||||
<!--TIMESTAMP 1703347410-->
|
||||
"#;
|
||||
let base = Url::parse("https://download.pytorch.org/whl/jinja2/").unwrap();
|
||||
let result = SimpleHtml::parse(text, &base).unwrap();
|
||||
insta::assert_debug_snapshot!(result, @r###"
|
||||
SimpleHtml {
|
||||
base: BaseUrl(
|
||||
Url {
|
||||
scheme: "https",
|
||||
cannot_be_a_base: false,
|
||||
username: "",
|
||||
password: None,
|
||||
host: Some(
|
||||
Domain(
|
||||
"download.pytorch.org",
|
||||
),
|
||||
),
|
||||
port: None,
|
||||
path: "/whl/jinja2/",
|
||||
query: None,
|
||||
fragment: None,
|
||||
},
|
||||
),
|
||||
files: [
|
||||
File {
|
||||
core_metadata: None,
|
||||
dist_info_metadata: None,
|
||||
data_dist_info_metadata: None,
|
||||
filename: "Jinja2-3.1.2-py3-none-any.whl",
|
||||
hashes: Hashes {
|
||||
md5: None,
|
||||
sha256: Some(
|
||||
"4095ada29e51070f7d199a0a5bdf5c8d8e238e03f0bf4dcc02571e78c9ae800d",
|
||||
),
|
||||
sha384: None,
|
||||
sha512: None,
|
||||
},
|
||||
requires_python: None,
|
||||
size: None,
|
||||
upload_time: None,
|
||||
url: "/whl/Jinja2-3.1.2-py3-none-any.whl#sha256%3D4095ada29e51070f7d199a0a5bdf5c8d8e238e03f0bf4dcc02571e78c9ae800d",
|
||||
yanked: None,
|
||||
},
|
||||
],
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_quoted_filepath() {
|
||||
let text = r#"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<h1>Links for jinja2</h1>
|
||||
<a href="cpu/torchtext-0.17.0%2Bcpu-cp39-cp39-win_amd64.whl">cpu/torchtext-0.17.0%2Bcpu-cp39-cp39-win_amd64.whl</a><br/>
|
||||
</body>
|
||||
</html>
|
||||
<!--TIMESTAMP 1703347410-->
|
||||
"#;
|
||||
let base = Url::parse("https://download.pytorch.org/whl/jinja2/").unwrap();
|
||||
let result = SimpleHtml::parse(text, &base).unwrap();
|
||||
insta::assert_debug_snapshot!(result, @r###"
|
||||
SimpleHtml {
|
||||
base: BaseUrl(
|
||||
Url {
|
||||
scheme: "https",
|
||||
cannot_be_a_base: false,
|
||||
username: "",
|
||||
password: None,
|
||||
host: Some(
|
||||
Domain(
|
||||
"download.pytorch.org",
|
||||
),
|
||||
),
|
||||
port: None,
|
||||
path: "/whl/jinja2/",
|
||||
query: None,
|
||||
fragment: None,
|
||||
},
|
||||
),
|
||||
files: [
|
||||
File {
|
||||
core_metadata: None,
|
||||
dist_info_metadata: None,
|
||||
data_dist_info_metadata: None,
|
||||
filename: "torchtext-0.17.0+cpu-cp39-cp39-win_amd64.whl",
|
||||
hashes: Hashes {
|
||||
md5: None,
|
||||
sha256: None,
|
||||
sha384: None,
|
||||
sha512: None,
|
||||
},
|
||||
requires_python: None,
|
||||
size: None,
|
||||
upload_time: None,
|
||||
url: "cpu/torchtext-0.17.0%2Bcpu-cp39-cp39-win_amd64.whl",
|
||||
yanked: None,
|
||||
},
|
||||
],
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_missing_hash() {
|
||||
let text = r#"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<h1>Links for jinja2</h1>
|
||||
<a href="/whl/Jinja2-3.1.2-py3-none-any.whl">Jinja2-3.1.2-py3-none-any.whl</a><br/>
|
||||
</body>
|
||||
</html>
|
||||
<!--TIMESTAMP 1703347410-->
|
||||
"#;
|
||||
let base = Url::parse("https://download.pytorch.org/whl/jinja2/").unwrap();
|
||||
let result = SimpleHtml::parse(text, &base).unwrap();
|
||||
insta::assert_debug_snapshot!(result, @r###"
|
||||
SimpleHtml {
|
||||
base: BaseUrl(
|
||||
Url {
|
||||
scheme: "https",
|
||||
cannot_be_a_base: false,
|
||||
username: "",
|
||||
password: None,
|
||||
host: Some(
|
||||
Domain(
|
||||
"download.pytorch.org",
|
||||
),
|
||||
),
|
||||
port: None,
|
||||
path: "/whl/jinja2/",
|
||||
query: None,
|
||||
fragment: None,
|
||||
},
|
||||
),
|
||||
files: [
|
||||
File {
|
||||
core_metadata: None,
|
||||
dist_info_metadata: None,
|
||||
data_dist_info_metadata: None,
|
||||
filename: "Jinja2-3.1.2-py3-none-any.whl",
|
||||
hashes: Hashes {
|
||||
md5: None,
|
||||
sha256: None,
|
||||
sha384: None,
|
||||
sha512: None,
|
||||
},
|
||||
requires_python: None,
|
||||
size: None,
|
||||
upload_time: None,
|
||||
url: "/whl/Jinja2-3.1.2-py3-none-any.whl",
|
||||
yanked: None,
|
||||
},
|
||||
],
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_missing_href() {
|
||||
let text = r"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<h1>Links for jinja2</h1>
|
||||
<a>Jinja2-3.1.2-py3-none-any.whl</a><br/>
|
||||
</body>
|
||||
</html>
|
||||
<!--TIMESTAMP 1703347410-->
|
||||
";
|
||||
let base = Url::parse("https://download.pytorch.org/whl/jinja2/").unwrap();
|
||||
let result = SimpleHtml::parse(text, &base).unwrap_err();
|
||||
insta::assert_snapshot!(result, @"Missing href attribute on anchor link: `Jinja2-3.1.2-py3-none-any.whl`");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_empty_href() {
|
||||
let text = r#"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<h1>Links for jinja2</h1>
|
||||
<a href="">Jinja2-3.1.2-py3-none-any.whl</a><br/>
|
||||
</body>
|
||||
</html>
|
||||
<!--TIMESTAMP 1703347410-->
|
||||
"#;
|
||||
let base = Url::parse("https://download.pytorch.org/whl/jinja2/").unwrap();
|
||||
let result = SimpleHtml::parse(text, &base).unwrap_err();
|
||||
insta::assert_snapshot!(result, @"Missing href attribute on anchor link: `Jinja2-3.1.2-py3-none-any.whl`");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_empty_fragment() {
|
||||
let text = r#"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<h1>Links for jinja2</h1>
|
||||
<a href="/whl/Jinja2-3.1.2-py3-none-any.whl#">Jinja2-3.1.2-py3-none-any.whl</a><br/>
|
||||
</body>
|
||||
</html>
|
||||
<!--TIMESTAMP 1703347410-->
|
||||
"#;
|
||||
let base = Url::parse("https://download.pytorch.org/whl/jinja2/").unwrap();
|
||||
let result = SimpleHtml::parse(text, &base).unwrap();
|
||||
insta::assert_debug_snapshot!(result, @r###"
|
||||
SimpleHtml {
|
||||
base: BaseUrl(
|
||||
Url {
|
||||
scheme: "https",
|
||||
cannot_be_a_base: false,
|
||||
username: "",
|
||||
password: None,
|
||||
host: Some(
|
||||
Domain(
|
||||
"download.pytorch.org",
|
||||
),
|
||||
),
|
||||
port: None,
|
||||
path: "/whl/jinja2/",
|
||||
query: None,
|
||||
fragment: None,
|
||||
},
|
||||
),
|
||||
files: [
|
||||
File {
|
||||
core_metadata: None,
|
||||
dist_info_metadata: None,
|
||||
data_dist_info_metadata: None,
|
||||
filename: "Jinja2-3.1.2-py3-none-any.whl",
|
||||
hashes: Hashes {
|
||||
md5: None,
|
||||
sha256: None,
|
||||
sha384: None,
|
||||
sha512: None,
|
||||
},
|
||||
requires_python: None,
|
||||
size: None,
|
||||
upload_time: None,
|
||||
url: "/whl/Jinja2-3.1.2-py3-none-any.whl#",
|
||||
yanked: None,
|
||||
},
|
||||
],
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_query_string() {
|
||||
let text = r#"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<h1>Links for jinja2</h1>
|
||||
<a href="/whl/Jinja2-3.1.2-py3-none-any.whl?project=legacy">Jinja2-3.1.2-py3-none-any.whl</a><br/>
|
||||
</body>
|
||||
</html>
|
||||
<!--TIMESTAMP 1703347410-->
|
||||
"#;
|
||||
let base = Url::parse("https://download.pytorch.org/whl/jinja2/").unwrap();
|
||||
let result = SimpleHtml::parse(text, &base).unwrap();
|
||||
insta::assert_debug_snapshot!(result, @r###"
|
||||
SimpleHtml {
|
||||
base: BaseUrl(
|
||||
Url {
|
||||
scheme: "https",
|
||||
cannot_be_a_base: false,
|
||||
username: "",
|
||||
password: None,
|
||||
host: Some(
|
||||
Domain(
|
||||
"download.pytorch.org",
|
||||
),
|
||||
),
|
||||
port: None,
|
||||
path: "/whl/jinja2/",
|
||||
query: None,
|
||||
fragment: None,
|
||||
},
|
||||
),
|
||||
files: [
|
||||
File {
|
||||
core_metadata: None,
|
||||
dist_info_metadata: None,
|
||||
data_dist_info_metadata: None,
|
||||
filename: "Jinja2-3.1.2-py3-none-any.whl",
|
||||
hashes: Hashes {
|
||||
md5: None,
|
||||
sha256: None,
|
||||
sha384: None,
|
||||
sha512: None,
|
||||
},
|
||||
requires_python: None,
|
||||
size: None,
|
||||
upload_time: None,
|
||||
url: "/whl/Jinja2-3.1.2-py3-none-any.whl?project=legacy",
|
||||
yanked: None,
|
||||
},
|
||||
],
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_missing_hash_value() {
|
||||
let text = r#"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<h1>Links for jinja2</h1>
|
||||
<a href="/whl/Jinja2-3.1.2-py3-none-any.whl#sha256">Jinja2-3.1.2-py3-none-any.whl</a><br/>
|
||||
</body>
|
||||
</html>
|
||||
<!--TIMESTAMP 1703347410-->
|
||||
"#;
|
||||
let base = Url::parse("https://download.pytorch.org/whl/jinja2/").unwrap();
|
||||
let result = SimpleHtml::parse(text, &base).unwrap_err();
|
||||
insta::assert_snapshot!(result, @"Unexpected fragment (expected `#sha256=...` or similar) on URL: sha256");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_unknown_hash() {
|
||||
let text = r#"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<h1>Links for jinja2</h1>
|
||||
<a href="/whl/Jinja2-3.1.2-py3-none-any.whl#blake2=6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61">Jinja2-3.1.2-py3-none-any.whl</a><br/>
|
||||
</body>
|
||||
</html>
|
||||
<!--TIMESTAMP 1703347410-->
|
||||
"#;
|
||||
let base = Url::parse("https://download.pytorch.org/whl/jinja2/").unwrap();
|
||||
let result = SimpleHtml::parse(text, &base).unwrap_err();
|
||||
insta::assert_snapshot!(result, @"Unsupported hash algorithm (expected one of: `md5`, `sha256`, `sha384`, or `sha512`) on: `blake2=6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61`");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_flat_index_html() {
|
||||
let text = r#"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head>
|
||||
<body>
|
||||
<a href="https://storage.googleapis.com/jax-releases/cuda100/jaxlib-0.1.52+cuda100-cp36-none-manylinux2010_x86_64.whl">cuda100/jaxlib-0.1.52+cuda100-cp36-none-manylinux2010_x86_64.whl</a><br>
|
||||
<a href="https://storage.googleapis.com/jax-releases/cuda100/jaxlib-0.1.52+cuda100-cp37-none-manylinux2010_x86_64.whl">cuda100/jaxlib-0.1.52+cuda100-cp37-none-manylinux2010_x86_64.whl</a><br>
|
||||
</body>
|
||||
</html>
|
||||
"#;
|
||||
let base =
|
||||
Url::parse("https://storage.googleapis.com/jax-releases/jax_cuda_releases.html").unwrap();
|
||||
let result = SimpleHtml::parse(text, &base).unwrap();
|
||||
insta::assert_debug_snapshot!(result, @r###"
|
||||
SimpleHtml {
|
||||
base: BaseUrl(
|
||||
Url {
|
||||
scheme: "https",
|
||||
cannot_be_a_base: false,
|
||||
username: "",
|
||||
password: None,
|
||||
host: Some(
|
||||
Domain(
|
||||
"storage.googleapis.com",
|
||||
),
|
||||
),
|
||||
port: None,
|
||||
path: "/jax-releases/jax_cuda_releases.html",
|
||||
query: None,
|
||||
fragment: None,
|
||||
},
|
||||
),
|
||||
files: [
|
||||
File {
|
||||
core_metadata: None,
|
||||
dist_info_metadata: None,
|
||||
data_dist_info_metadata: None,
|
||||
filename: "jaxlib-0.1.52+cuda100-cp36-none-manylinux2010_x86_64.whl",
|
||||
hashes: Hashes {
|
||||
md5: None,
|
||||
sha256: None,
|
||||
sha384: None,
|
||||
sha512: None,
|
||||
},
|
||||
requires_python: None,
|
||||
size: None,
|
||||
upload_time: None,
|
||||
url: "https://storage.googleapis.com/jax-releases/cuda100/jaxlib-0.1.52+cuda100-cp36-none-manylinux2010_x86_64.whl",
|
||||
yanked: None,
|
||||
},
|
||||
File {
|
||||
core_metadata: None,
|
||||
dist_info_metadata: None,
|
||||
data_dist_info_metadata: None,
|
||||
filename: "jaxlib-0.1.52+cuda100-cp37-none-manylinux2010_x86_64.whl",
|
||||
hashes: Hashes {
|
||||
md5: None,
|
||||
sha256: None,
|
||||
sha384: None,
|
||||
sha512: None,
|
||||
},
|
||||
requires_python: None,
|
||||
size: None,
|
||||
upload_time: None,
|
||||
url: "https://storage.googleapis.com/jax-releases/cuda100/jaxlib-0.1.52+cuda100-cp37-none-manylinux2010_x86_64.whl",
|
||||
yanked: None,
|
||||
},
|
||||
],
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
/// Test for AWS Code Artifact
|
||||
///
|
||||
/// See: <https://github.com/astral-sh/uv/issues/1388#issuecomment-1947659088>
|
||||
#[test]
|
||||
fn parse_code_artifact_index_html() {
|
||||
let text = r#"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Links for flask</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Links for flask</h1>
|
||||
<a href="0.1/Flask-0.1.tar.gz#sha256=9da884457e910bf0847d396cb4b778ad9f3c3d17db1c5997cb861937bd284237" data-gpg-sig="false" >Flask-0.1.tar.gz</a>
|
||||
<br/>
|
||||
<a href="0.10.1/Flask-0.10.1.tar.gz#sha256=4c83829ff83d408b5e1d4995472265411d2c414112298f2eb4b359d9e4563373" data-gpg-sig="false" >Flask-0.10.1.tar.gz</a>
|
||||
<br/>
|
||||
<a href="3.0.1/flask-3.0.1.tar.gz#sha256=6489f51bb3666def6f314e15f19d50a1869a19ae0e8c9a3641ffe66c77d42403" data-requires-python=">=3.8" data-gpg-sig="false" >flask-3.0.1.tar.gz</a>
|
||||
<br/>
|
||||
</body>
|
||||
</html>
|
||||
"#;
|
||||
let base = Url::parse("https://account.d.codeartifact.us-west-2.amazonaws.com/pypi/shared-packages-pypi/simple/flask/")
|
||||
.unwrap();
|
||||
let result = SimpleHtml::parse(text, &base).unwrap();
|
||||
insta::assert_debug_snapshot!(result, @r###"
|
||||
SimpleHtml {
|
||||
base: BaseUrl(
|
||||
Url {
|
||||
scheme: "https",
|
||||
cannot_be_a_base: false,
|
||||
username: "",
|
||||
password: None,
|
||||
host: Some(
|
||||
Domain(
|
||||
"account.d.codeartifact.us-west-2.amazonaws.com",
|
||||
),
|
||||
),
|
||||
port: None,
|
||||
path: "/pypi/shared-packages-pypi/simple/flask/",
|
||||
query: None,
|
||||
fragment: None,
|
||||
},
|
||||
),
|
||||
files: [
|
||||
File {
|
||||
core_metadata: None,
|
||||
dist_info_metadata: None,
|
||||
data_dist_info_metadata: None,
|
||||
filename: "Flask-0.1.tar.gz",
|
||||
hashes: Hashes {
|
||||
md5: None,
|
||||
sha256: Some(
|
||||
"9da884457e910bf0847d396cb4b778ad9f3c3d17db1c5997cb861937bd284237",
|
||||
),
|
||||
sha384: None,
|
||||
sha512: None,
|
||||
},
|
||||
requires_python: None,
|
||||
size: None,
|
||||
upload_time: None,
|
||||
url: "0.1/Flask-0.1.tar.gz#sha256=9da884457e910bf0847d396cb4b778ad9f3c3d17db1c5997cb861937bd284237",
|
||||
yanked: None,
|
||||
},
|
||||
File {
|
||||
core_metadata: None,
|
||||
dist_info_metadata: None,
|
||||
data_dist_info_metadata: None,
|
||||
filename: "Flask-0.10.1.tar.gz",
|
||||
hashes: Hashes {
|
||||
md5: None,
|
||||
sha256: Some(
|
||||
"4c83829ff83d408b5e1d4995472265411d2c414112298f2eb4b359d9e4563373",
|
||||
),
|
||||
sha384: None,
|
||||
sha512: None,
|
||||
},
|
||||
requires_python: None,
|
||||
size: None,
|
||||
upload_time: None,
|
||||
url: "0.10.1/Flask-0.10.1.tar.gz#sha256=4c83829ff83d408b5e1d4995472265411d2c414112298f2eb4b359d9e4563373",
|
||||
yanked: None,
|
||||
},
|
||||
File {
|
||||
core_metadata: None,
|
||||
dist_info_metadata: None,
|
||||
data_dist_info_metadata: None,
|
||||
filename: "flask-3.0.1.tar.gz",
|
||||
hashes: Hashes {
|
||||
md5: None,
|
||||
sha256: Some(
|
||||
"6489f51bb3666def6f314e15f19d50a1869a19ae0e8c9a3641ffe66c77d42403",
|
||||
),
|
||||
sha384: None,
|
||||
sha512: None,
|
||||
},
|
||||
requires_python: Some(
|
||||
Ok(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: GreaterThanEqual,
|
||||
version: "3.8",
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
size: None,
|
||||
upload_time: None,
|
||||
url: "3.0.1/flask-3.0.1.tar.gz#sha256=6489f51bb3666def6f314e15f19d50a1869a19ae0e8c9a3641ffe66c77d42403",
|
||||
yanked: None,
|
||||
},
|
||||
],
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_file_requires_python_trailing_comma() {
|
||||
let text = r#"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<h1>Links for jinja2</h1>
|
||||
<a href="/whl/Jinja2-3.1.2-py3-none-any.whl#sha256=6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61" data-requires-python=">=3.8,">Jinja2-3.1.2-py3-none-any.whl</a><br/>
|
||||
</body>
|
||||
</html>
|
||||
"#;
|
||||
let base = Url::parse("https://download.pytorch.org/whl/jinja2/").unwrap();
|
||||
let result = SimpleHtml::parse(text, &base).unwrap();
|
||||
insta::assert_debug_snapshot!(result, @r###"
|
||||
SimpleHtml {
|
||||
base: BaseUrl(
|
||||
Url {
|
||||
scheme: "https",
|
||||
cannot_be_a_base: false,
|
||||
username: "",
|
||||
password: None,
|
||||
host: Some(
|
||||
Domain(
|
||||
"download.pytorch.org",
|
||||
),
|
||||
),
|
||||
port: None,
|
||||
path: "/whl/jinja2/",
|
||||
query: None,
|
||||
fragment: None,
|
||||
},
|
||||
),
|
||||
files: [
|
||||
File {
|
||||
core_metadata: None,
|
||||
dist_info_metadata: None,
|
||||
data_dist_info_metadata: None,
|
||||
filename: "Jinja2-3.1.2-py3-none-any.whl",
|
||||
hashes: Hashes {
|
||||
md5: None,
|
||||
sha256: Some(
|
||||
"6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61",
|
||||
),
|
||||
sha384: None,
|
||||
sha512: None,
|
||||
},
|
||||
requires_python: Some(
|
||||
Ok(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: GreaterThanEqual,
|
||||
version: "3.8",
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
size: None,
|
||||
upload_time: None,
|
||||
url: "/whl/Jinja2-3.1.2-py3-none-any.whl#sha256=6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61",
|
||||
yanked: None,
|
||||
},
|
||||
],
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
/// Respect PEP 714 (see: <https://peps.python.org/pep-0714/>).
|
||||
#[test]
|
||||
fn parse_core_metadata() {
|
||||
let text = r#"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<h1>Links for jinja2</h1>
|
||||
<a href="/whl/Jinja2-3.1.2-py3-none-any.whl" data-dist-info-metadata="true">Jinja2-3.1.2-py3-none-any.whl</a><br/>
|
||||
<a href="/whl/Jinja2-3.1.3-py3-none-any.whl" data-core-metadata="true">Jinja2-3.1.3-py3-none-any.whl</a><br/>
|
||||
<a href="/whl/Jinja2-3.1.4-py3-none-any.whl" data-dist-info-metadata="false">Jinja2-3.1.4-py3-none-any.whl</a><br/>
|
||||
<a href="/whl/Jinja2-3.1.5-py3-none-any.whl" data-core-metadata="false">Jinja2-3.1.5-py3-none-any.whl</a><br/>
|
||||
<a href="/whl/Jinja2-3.1.6-py3-none-any.whl" data-core-metadata="true" data-dist-info-metadata="false">Jinja2-3.1.6-py3-none-any.whl</a><br/>
|
||||
</body>
|
||||
</html>
|
||||
"#;
|
||||
let base = Url::parse("https://account.d.codeartifact.us-west-2.amazonaws.com/pypi/shared-packages-pypi/simple/flask/")
|
||||
.unwrap();
|
||||
let result = SimpleHtml::parse(text, &base).unwrap();
|
||||
insta::assert_debug_snapshot!(result, @r###"
|
||||
SimpleHtml {
|
||||
base: BaseUrl(
|
||||
Url {
|
||||
scheme: "https",
|
||||
cannot_be_a_base: false,
|
||||
username: "",
|
||||
password: None,
|
||||
host: Some(
|
||||
Domain(
|
||||
"account.d.codeartifact.us-west-2.amazonaws.com",
|
||||
),
|
||||
),
|
||||
port: None,
|
||||
path: "/pypi/shared-packages-pypi/simple/flask/",
|
||||
query: None,
|
||||
fragment: None,
|
||||
},
|
||||
),
|
||||
files: [
|
||||
File {
|
||||
core_metadata: Some(
|
||||
Bool(
|
||||
true,
|
||||
),
|
||||
),
|
||||
dist_info_metadata: None,
|
||||
data_dist_info_metadata: None,
|
||||
filename: "Jinja2-3.1.2-py3-none-any.whl",
|
||||
hashes: Hashes {
|
||||
md5: None,
|
||||
sha256: None,
|
||||
sha384: None,
|
||||
sha512: None,
|
||||
},
|
||||
requires_python: None,
|
||||
size: None,
|
||||
upload_time: None,
|
||||
url: "/whl/Jinja2-3.1.2-py3-none-any.whl",
|
||||
yanked: None,
|
||||
},
|
||||
File {
|
||||
core_metadata: Some(
|
||||
Bool(
|
||||
true,
|
||||
),
|
||||
),
|
||||
dist_info_metadata: None,
|
||||
data_dist_info_metadata: None,
|
||||
filename: "Jinja2-3.1.3-py3-none-any.whl",
|
||||
hashes: Hashes {
|
||||
md5: None,
|
||||
sha256: None,
|
||||
sha384: None,
|
||||
sha512: None,
|
||||
},
|
||||
requires_python: None,
|
||||
size: None,
|
||||
upload_time: None,
|
||||
url: "/whl/Jinja2-3.1.3-py3-none-any.whl",
|
||||
yanked: None,
|
||||
},
|
||||
File {
|
||||
core_metadata: Some(
|
||||
Bool(
|
||||
false,
|
||||
),
|
||||
),
|
||||
dist_info_metadata: None,
|
||||
data_dist_info_metadata: None,
|
||||
filename: "Jinja2-3.1.4-py3-none-any.whl",
|
||||
hashes: Hashes {
|
||||
md5: None,
|
||||
sha256: None,
|
||||
sha384: None,
|
||||
sha512: None,
|
||||
},
|
||||
requires_python: None,
|
||||
size: None,
|
||||
upload_time: None,
|
||||
url: "/whl/Jinja2-3.1.4-py3-none-any.whl",
|
||||
yanked: None,
|
||||
},
|
||||
File {
|
||||
core_metadata: Some(
|
||||
Bool(
|
||||
false,
|
||||
),
|
||||
),
|
||||
dist_info_metadata: None,
|
||||
data_dist_info_metadata: None,
|
||||
filename: "Jinja2-3.1.5-py3-none-any.whl",
|
||||
hashes: Hashes {
|
||||
md5: None,
|
||||
sha256: None,
|
||||
sha384: None,
|
||||
sha512: None,
|
||||
},
|
||||
requires_python: None,
|
||||
size: None,
|
||||
upload_time: None,
|
||||
url: "/whl/Jinja2-3.1.5-py3-none-any.whl",
|
||||
yanked: None,
|
||||
},
|
||||
File {
|
||||
core_metadata: Some(
|
||||
Bool(
|
||||
true,
|
||||
),
|
||||
),
|
||||
dist_info_metadata: None,
|
||||
data_dist_info_metadata: None,
|
||||
filename: "Jinja2-3.1.6-py3-none-any.whl",
|
||||
hashes: Hashes {
|
||||
md5: None,
|
||||
sha256: None,
|
||||
sha384: None,
|
||||
sha512: None,
|
||||
},
|
||||
requires_python: None,
|
||||
size: None,
|
||||
upload_time: None,
|
||||
url: "/whl/Jinja2-3.1.6-py3-none-any.whl",
|
||||
yanked: None,
|
||||
},
|
||||
],
|
||||
}
|
||||
"###);
|
||||
}
|
|
@ -453,4 +453,326 @@ impl CacheControlDirective {
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn cache_control_token() {
|
||||
let cc: CacheControl = CacheControlParser::new(["no-cache"]).collect();
|
||||
assert!(cc.no_cache);
|
||||
assert!(!cc.must_revalidate);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_control_max_age() {
|
||||
let cc: CacheControl = CacheControlParser::new(["max-age=60"]).collect();
|
||||
assert_eq!(Some(60), cc.max_age_seconds);
|
||||
assert!(!cc.must_revalidate);
|
||||
}
|
||||
|
||||
// [RFC 9111 S5.2.1.1] says that client MUST NOT quote max-age, but we
|
||||
// support parsing it that way anyway.
|
||||
//
|
||||
// [RFC 9111 S5.2.1.1]: https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.1.1
|
||||
#[test]
|
||||
fn cache_control_max_age_quoted() {
|
||||
let cc: CacheControl = CacheControlParser::new([r#"max-age="60""#]).collect();
|
||||
assert_eq!(Some(60), cc.max_age_seconds);
|
||||
assert!(!cc.must_revalidate);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_control_max_age_invalid() {
|
||||
let cc: CacheControl = CacheControlParser::new(["max-age=6a0"]).collect();
|
||||
assert_eq!(None, cc.max_age_seconds);
|
||||
assert!(cc.must_revalidate);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_control_immutable() {
|
||||
let cc: CacheControl = CacheControlParser::new(["max-age=31536000, immutable"]).collect();
|
||||
assert_eq!(Some(31_536_000), cc.max_age_seconds);
|
||||
assert!(cc.immutable);
|
||||
assert!(!cc.must_revalidate);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_control_unrecognized() {
|
||||
let cc: CacheControl = CacheControlParser::new(["lion,max-age=60,zebra"]).collect();
|
||||
assert_eq!(Some(60), cc.max_age_seconds);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_control_invalid_squashes_remainder() {
|
||||
let cc: CacheControl = CacheControlParser::new(["no-cache,\x00,max-age=60"]).collect();
|
||||
// The invalid data doesn't impact things before it.
|
||||
assert!(cc.no_cache);
|
||||
// The invalid data precludes parsing anything after.
|
||||
assert_eq!(None, cc.max_age_seconds);
|
||||
// The invalid contents should force revalidation.
|
||||
assert!(cc.must_revalidate);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_control_invalid_squashes_remainder_but_not_other_header_values() {
|
||||
let cc: CacheControl =
|
||||
CacheControlParser::new(["no-cache,\x00,max-age=60", "max-stale=30"]).collect();
|
||||
// The invalid data doesn't impact things before it.
|
||||
assert!(cc.no_cache);
|
||||
// The invalid data precludes parsing anything after
|
||||
// in the same header value, but not in other
|
||||
// header values.
|
||||
assert_eq!(Some(30), cc.max_stale_seconds);
|
||||
// The invalid contents should force revalidation.
|
||||
assert!(cc.must_revalidate);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_control_parse_token() {
|
||||
let directives = CacheControlParser::new(["no-cache"]).collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
directives,
|
||||
vec![CacheControlDirective {
|
||||
name: "no-cache".to_string(),
|
||||
value: vec![]
|
||||
}]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_control_parse_token_to_token_value() {
|
||||
let directives = CacheControlParser::new(["max-age=60"]).collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
directives,
|
||||
vec![CacheControlDirective {
|
||||
name: "max-age".to_string(),
|
||||
value: b"60".to_vec(),
|
||||
}]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_control_parse_token_to_quoted_string() {
|
||||
let directives =
|
||||
CacheControlParser::new([r#"private="cookie,x-something-else""#]).collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
directives,
|
||||
vec![CacheControlDirective {
|
||||
name: "private".to_string(),
|
||||
value: b"cookie,x-something-else".to_vec(),
|
||||
}]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_control_parse_token_to_quoted_string_with_escape() {
|
||||
let directives =
|
||||
CacheControlParser::new([r#"private="something\"crazy""#]).collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
directives,
|
||||
vec![CacheControlDirective {
|
||||
name: "private".to_string(),
|
||||
value: br#"something"crazy"#.to_vec(),
|
||||
}]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_control_parse_multiple_directives() {
|
||||
let header = r#"max-age=60, no-cache, private="cookie", no-transform"#;
|
||||
let directives = CacheControlParser::new([header]).collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
directives,
|
||||
vec![
|
||||
CacheControlDirective {
|
||||
name: "max-age".to_string(),
|
||||
value: b"60".to_vec(),
|
||||
},
|
||||
CacheControlDirective {
|
||||
name: "no-cache".to_string(),
|
||||
value: vec![]
|
||||
},
|
||||
CacheControlDirective {
|
||||
name: "private".to_string(),
|
||||
value: b"cookie".to_vec(),
|
||||
},
|
||||
CacheControlDirective {
|
||||
name: "no-transform".to_string(),
|
||||
value: vec![]
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_control_parse_multiple_directives_across_multiple_header_values() {
|
||||
let headers = [
|
||||
r"max-age=60, no-cache",
|
||||
r#"private="cookie""#,
|
||||
r"no-transform",
|
||||
];
|
||||
let directives = CacheControlParser::new(headers).collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
directives,
|
||||
vec![
|
||||
CacheControlDirective {
|
||||
name: "max-age".to_string(),
|
||||
value: b"60".to_vec(),
|
||||
},
|
||||
CacheControlDirective {
|
||||
name: "no-cache".to_string(),
|
||||
value: vec![]
|
||||
},
|
||||
CacheControlDirective {
|
||||
name: "private".to_string(),
|
||||
value: b"cookie".to_vec(),
|
||||
},
|
||||
CacheControlDirective {
|
||||
name: "no-transform".to_string(),
|
||||
value: vec![]
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_control_parse_one_header_invalid() {
|
||||
let headers = [
|
||||
r"max-age=60, no-cache",
|
||||
r#", private="cookie""#,
|
||||
r"no-transform",
|
||||
];
|
||||
let directives = CacheControlParser::new(headers).collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
directives,
|
||||
vec![
|
||||
CacheControlDirective {
|
||||
name: "max-age".to_string(),
|
||||
value: b"60".to_vec(),
|
||||
},
|
||||
CacheControlDirective {
|
||||
name: "no-cache".to_string(),
|
||||
value: vec![]
|
||||
},
|
||||
CacheControlDirective {
|
||||
name: "must-revalidate".to_string(),
|
||||
value: vec![]
|
||||
},
|
||||
CacheControlDirective {
|
||||
name: "no-transform".to_string(),
|
||||
value: vec![]
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_control_parse_invalid_directive_drops_remainder() {
|
||||
let header = r#"max-age=60, no-cache, ="cookie", no-transform"#;
|
||||
let directives = CacheControlParser::new([header]).collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
directives,
|
||||
vec![
|
||||
CacheControlDirective {
|
||||
name: "max-age".to_string(),
|
||||
value: b"60".to_vec(),
|
||||
},
|
||||
CacheControlDirective {
|
||||
name: "no-cache".to_string(),
|
||||
value: vec![]
|
||||
},
|
||||
CacheControlDirective {
|
||||
name: "must-revalidate".to_string(),
|
||||
value: vec![]
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_control_parse_name_normalized() {
|
||||
let header = r"MAX-AGE=60";
|
||||
let directives = CacheControlParser::new([header]).collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
directives,
|
||||
vec![CacheControlDirective {
|
||||
name: "max-age".to_string(),
|
||||
value: b"60".to_vec(),
|
||||
},]
|
||||
);
|
||||
}
|
||||
|
||||
// When a duplicate directive is found, we keep the first one
|
||||
// and add in a `must-revalidate` directive to indicate that
|
||||
// things are stale and the client should do a re-check.
|
||||
#[test]
|
||||
fn cache_control_parse_duplicate_directives() {
|
||||
let header = r"max-age=60, no-cache, max-age=30";
|
||||
let directives = CacheControlParser::new([header]).collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
directives,
|
||||
vec![
|
||||
CacheControlDirective {
|
||||
name: "max-age".to_string(),
|
||||
value: b"60".to_vec(),
|
||||
},
|
||||
CacheControlDirective {
|
||||
name: "no-cache".to_string(),
|
||||
value: vec![]
|
||||
},
|
||||
CacheControlDirective {
|
||||
name: "must-revalidate".to_string(),
|
||||
value: vec![]
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_control_parse_duplicate_directives_across_headers() {
|
||||
let headers = [r"max-age=60, no-cache", r"max-age=30"];
|
||||
let directives = CacheControlParser::new(headers).collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
directives,
|
||||
vec![
|
||||
CacheControlDirective {
|
||||
name: "max-age".to_string(),
|
||||
value: b"60".to_vec(),
|
||||
},
|
||||
CacheControlDirective {
|
||||
name: "no-cache".to_string(),
|
||||
value: vec![]
|
||||
},
|
||||
CacheControlDirective {
|
||||
name: "must-revalidate".to_string(),
|
||||
value: vec![]
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
// Tests that we don't emit must-revalidate multiple times
|
||||
// even when something is duplicated multiple times.
|
||||
#[test]
|
||||
fn cache_control_parse_duplicate_redux() {
|
||||
let header = r"max-age=60, no-cache, no-cache, max-age=30";
|
||||
let directives = CacheControlParser::new([header]).collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
directives,
|
||||
vec![
|
||||
CacheControlDirective {
|
||||
name: "max-age".to_string(),
|
||||
value: b"60".to_vec(),
|
||||
},
|
||||
CacheControlDirective {
|
||||
name: "no-cache".to_string(),
|
||||
value: vec![]
|
||||
},
|
||||
CacheControlDirective {
|
||||
name: "must-revalidate".to_string(),
|
||||
value: vec![]
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,320 +0,0 @@
|
|||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn cache_control_token() {
|
||||
let cc: CacheControl = CacheControlParser::new(["no-cache"]).collect();
|
||||
assert!(cc.no_cache);
|
||||
assert!(!cc.must_revalidate);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_control_max_age() {
|
||||
let cc: CacheControl = CacheControlParser::new(["max-age=60"]).collect();
|
||||
assert_eq!(Some(60), cc.max_age_seconds);
|
||||
assert!(!cc.must_revalidate);
|
||||
}
|
||||
|
||||
// [RFC 9111 S5.2.1.1] says that client MUST NOT quote max-age, but we
|
||||
// support parsing it that way anyway.
|
||||
//
|
||||
// [RFC 9111 S5.2.1.1]: https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.1.1
|
||||
#[test]
|
||||
fn cache_control_max_age_quoted() {
|
||||
let cc: CacheControl = CacheControlParser::new([r#"max-age="60""#]).collect();
|
||||
assert_eq!(Some(60), cc.max_age_seconds);
|
||||
assert!(!cc.must_revalidate);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_control_max_age_invalid() {
|
||||
let cc: CacheControl = CacheControlParser::new(["max-age=6a0"]).collect();
|
||||
assert_eq!(None, cc.max_age_seconds);
|
||||
assert!(cc.must_revalidate);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_control_immutable() {
|
||||
let cc: CacheControl = CacheControlParser::new(["max-age=31536000, immutable"]).collect();
|
||||
assert_eq!(Some(31_536_000), cc.max_age_seconds);
|
||||
assert!(cc.immutable);
|
||||
assert!(!cc.must_revalidate);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_control_unrecognized() {
|
||||
let cc: CacheControl = CacheControlParser::new(["lion,max-age=60,zebra"]).collect();
|
||||
assert_eq!(Some(60), cc.max_age_seconds);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_control_invalid_squashes_remainder() {
|
||||
let cc: CacheControl = CacheControlParser::new(["no-cache,\x00,max-age=60"]).collect();
|
||||
// The invalid data doesn't impact things before it.
|
||||
assert!(cc.no_cache);
|
||||
// The invalid data precludes parsing anything after.
|
||||
assert_eq!(None, cc.max_age_seconds);
|
||||
// The invalid contents should force revalidation.
|
||||
assert!(cc.must_revalidate);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_control_invalid_squashes_remainder_but_not_other_header_values() {
|
||||
let cc: CacheControl =
|
||||
CacheControlParser::new(["no-cache,\x00,max-age=60", "max-stale=30"]).collect();
|
||||
// The invalid data doesn't impact things before it.
|
||||
assert!(cc.no_cache);
|
||||
// The invalid data precludes parsing anything after
|
||||
// in the same header value, but not in other
|
||||
// header values.
|
||||
assert_eq!(Some(30), cc.max_stale_seconds);
|
||||
// The invalid contents should force revalidation.
|
||||
assert!(cc.must_revalidate);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_control_parse_token() {
|
||||
let directives = CacheControlParser::new(["no-cache"]).collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
directives,
|
||||
vec![CacheControlDirective {
|
||||
name: "no-cache".to_string(),
|
||||
value: vec![]
|
||||
}]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_control_parse_token_to_token_value() {
|
||||
let directives = CacheControlParser::new(["max-age=60"]).collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
directives,
|
||||
vec![CacheControlDirective {
|
||||
name: "max-age".to_string(),
|
||||
value: b"60".to_vec(),
|
||||
}]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_control_parse_token_to_quoted_string() {
|
||||
let directives =
|
||||
CacheControlParser::new([r#"private="cookie,x-something-else""#]).collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
directives,
|
||||
vec![CacheControlDirective {
|
||||
name: "private".to_string(),
|
||||
value: b"cookie,x-something-else".to_vec(),
|
||||
}]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_control_parse_token_to_quoted_string_with_escape() {
|
||||
let directives = CacheControlParser::new([r#"private="something\"crazy""#]).collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
directives,
|
||||
vec![CacheControlDirective {
|
||||
name: "private".to_string(),
|
||||
value: br#"something"crazy"#.to_vec(),
|
||||
}]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_control_parse_multiple_directives() {
|
||||
let header = r#"max-age=60, no-cache, private="cookie", no-transform"#;
|
||||
let directives = CacheControlParser::new([header]).collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
directives,
|
||||
vec![
|
||||
CacheControlDirective {
|
||||
name: "max-age".to_string(),
|
||||
value: b"60".to_vec(),
|
||||
},
|
||||
CacheControlDirective {
|
||||
name: "no-cache".to_string(),
|
||||
value: vec![]
|
||||
},
|
||||
CacheControlDirective {
|
||||
name: "private".to_string(),
|
||||
value: b"cookie".to_vec(),
|
||||
},
|
||||
CacheControlDirective {
|
||||
name: "no-transform".to_string(),
|
||||
value: vec![]
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_control_parse_multiple_directives_across_multiple_header_values() {
|
||||
let headers = [
|
||||
r"max-age=60, no-cache",
|
||||
r#"private="cookie""#,
|
||||
r"no-transform",
|
||||
];
|
||||
let directives = CacheControlParser::new(headers).collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
directives,
|
||||
vec![
|
||||
CacheControlDirective {
|
||||
name: "max-age".to_string(),
|
||||
value: b"60".to_vec(),
|
||||
},
|
||||
CacheControlDirective {
|
||||
name: "no-cache".to_string(),
|
||||
value: vec![]
|
||||
},
|
||||
CacheControlDirective {
|
||||
name: "private".to_string(),
|
||||
value: b"cookie".to_vec(),
|
||||
},
|
||||
CacheControlDirective {
|
||||
name: "no-transform".to_string(),
|
||||
value: vec![]
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_control_parse_one_header_invalid() {
|
||||
let headers = [
|
||||
r"max-age=60, no-cache",
|
||||
r#", private="cookie""#,
|
||||
r"no-transform",
|
||||
];
|
||||
let directives = CacheControlParser::new(headers).collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
directives,
|
||||
vec![
|
||||
CacheControlDirective {
|
||||
name: "max-age".to_string(),
|
||||
value: b"60".to_vec(),
|
||||
},
|
||||
CacheControlDirective {
|
||||
name: "no-cache".to_string(),
|
||||
value: vec![]
|
||||
},
|
||||
CacheControlDirective {
|
||||
name: "must-revalidate".to_string(),
|
||||
value: vec![]
|
||||
},
|
||||
CacheControlDirective {
|
||||
name: "no-transform".to_string(),
|
||||
value: vec![]
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_control_parse_invalid_directive_drops_remainder() {
|
||||
let header = r#"max-age=60, no-cache, ="cookie", no-transform"#;
|
||||
let directives = CacheControlParser::new([header]).collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
directives,
|
||||
vec![
|
||||
CacheControlDirective {
|
||||
name: "max-age".to_string(),
|
||||
value: b"60".to_vec(),
|
||||
},
|
||||
CacheControlDirective {
|
||||
name: "no-cache".to_string(),
|
||||
value: vec![]
|
||||
},
|
||||
CacheControlDirective {
|
||||
name: "must-revalidate".to_string(),
|
||||
value: vec![]
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_control_parse_name_normalized() {
|
||||
let header = r"MAX-AGE=60";
|
||||
let directives = CacheControlParser::new([header]).collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
directives,
|
||||
vec![CacheControlDirective {
|
||||
name: "max-age".to_string(),
|
||||
value: b"60".to_vec(),
|
||||
},]
|
||||
);
|
||||
}
|
||||
|
||||
// When a duplicate directive is found, we keep the first one
|
||||
// and add in a `must-revalidate` directive to indicate that
|
||||
// things are stale and the client should do a re-check.
|
||||
#[test]
|
||||
fn cache_control_parse_duplicate_directives() {
|
||||
let header = r"max-age=60, no-cache, max-age=30";
|
||||
let directives = CacheControlParser::new([header]).collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
directives,
|
||||
vec![
|
||||
CacheControlDirective {
|
||||
name: "max-age".to_string(),
|
||||
value: b"60".to_vec(),
|
||||
},
|
||||
CacheControlDirective {
|
||||
name: "no-cache".to_string(),
|
||||
value: vec![]
|
||||
},
|
||||
CacheControlDirective {
|
||||
name: "must-revalidate".to_string(),
|
||||
value: vec![]
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_control_parse_duplicate_directives_across_headers() {
|
||||
let headers = [r"max-age=60, no-cache", r"max-age=30"];
|
||||
let directives = CacheControlParser::new(headers).collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
directives,
|
||||
vec![
|
||||
CacheControlDirective {
|
||||
name: "max-age".to_string(),
|
||||
value: b"60".to_vec(),
|
||||
},
|
||||
CacheControlDirective {
|
||||
name: "no-cache".to_string(),
|
||||
value: vec![]
|
||||
},
|
||||
CacheControlDirective {
|
||||
name: "must-revalidate".to_string(),
|
||||
value: vec![]
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
// Tests that we don't emit must-revalidate multiple times
|
||||
// even when something is duplicated multiple times.
|
||||
#[test]
|
||||
fn cache_control_parse_duplicate_redux() {
|
||||
let header = r"max-age=60, no-cache, no-cache, max-age=30";
|
||||
let directives = CacheControlParser::new([header]).collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
directives,
|
||||
vec![
|
||||
CacheControlDirective {
|
||||
name: "max-age".to_string(),
|
||||
value: b"60".to_vec(),
|
||||
},
|
||||
CacheControlDirective {
|
||||
name: "no-cache".to_string(),
|
||||
value: vec![]
|
||||
},
|
||||
CacheControlDirective {
|
||||
name: "must-revalidate".to_string(),
|
||||
value: vec![]
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
|
@ -947,4 +947,107 @@ impl Connectivity {
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
mod tests {
|
||||
use std::str::FromStr;
|
||||
|
||||
use url::Url;
|
||||
|
||||
use uv_normalize::PackageName;
|
||||
use uv_pypi_types::{JoinRelativeError, SimpleJson};
|
||||
|
||||
use crate::{html::SimpleHtml, SimpleMetadata, SimpleMetadatum};
|
||||
|
||||
#[test]
|
||||
fn ignore_failing_files() {
|
||||
// 1.7.7 has an invalid requires-python field (double comma), 1.7.8 is valid
|
||||
let response = r#"
|
||||
{
|
||||
"files": [
|
||||
{
|
||||
"core-metadata": false,
|
||||
"data-dist-info-metadata": false,
|
||||
"filename": "pyflyby-1.7.7.tar.gz",
|
||||
"hashes": {
|
||||
"sha256": "0c4d953f405a7be1300b440dbdbc6917011a07d8401345a97e72cd410d5fb291"
|
||||
},
|
||||
"requires-python": ">=2.5, !=3.0.*, !=3.1.*, !=3.2.*, !=3.2.*, !=3.3.*, !=3.4.*,, !=3.5.*, !=3.6.*, <4",
|
||||
"size": 427200,
|
||||
"upload-time": "2022-05-19T09:14:36.591835Z",
|
||||
"url": "https://files.pythonhosted.org/packages/61/93/9fec62902d0b4fc2521333eba047bff4adbba41f1723a6382367f84ee522/pyflyby-1.7.7.tar.gz",
|
||||
"yanked": false
|
||||
},
|
||||
{
|
||||
"core-metadata": false,
|
||||
"data-dist-info-metadata": false,
|
||||
"filename": "pyflyby-1.7.8.tar.gz",
|
||||
"hashes": {
|
||||
"sha256": "1ee37474f6da8f98653dbcc208793f50b7ace1d9066f49e2707750a5ba5d53c6"
|
||||
},
|
||||
"requires-python": ">=2.5, !=3.0.*, !=3.1.*, !=3.2.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*, <4",
|
||||
"size": 424460,
|
||||
"upload-time": "2022-08-04T10:42:02.190074Z",
|
||||
"url": "https://files.pythonhosted.org/packages/ad/39/17180d9806a1c50197bc63b25d0f1266f745fc3b23f11439fccb3d6baa50/pyflyby-1.7.8.tar.gz",
|
||||
"yanked": false
|
||||
}
|
||||
]
|
||||
}
|
||||
"#;
|
||||
let data: SimpleJson = serde_json::from_str(response).unwrap();
|
||||
let base = Url::parse("https://pypi.org/simple/pyflyby/").unwrap();
|
||||
let simple_metadata = SimpleMetadata::from_files(
|
||||
data.files,
|
||||
&PackageName::from_str("pyflyby").unwrap(),
|
||||
&base,
|
||||
);
|
||||
let versions: Vec<String> = simple_metadata
|
||||
.iter()
|
||||
.map(|SimpleMetadatum { version, .. }| version.to_string())
|
||||
.collect();
|
||||
assert_eq!(versions, ["1.7.8".to_string()]);
|
||||
}
|
||||
|
||||
/// Test for AWS Code Artifact registry
|
||||
///
|
||||
/// See: <https://github.com/astral-sh/uv/issues/1388>
|
||||
#[test]
|
||||
fn relative_urls_code_artifact() -> Result<(), JoinRelativeError> {
|
||||
let text = r#"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Links for flask</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Links for flask</h1>
|
||||
<a href="0.1/Flask-0.1.tar.gz#sha256=9da884457e910bf0847d396cb4b778ad9f3c3d17db1c5997cb861937bd284237" data-gpg-sig="false" >Flask-0.1.tar.gz</a>
|
||||
<br/>
|
||||
<a href="0.10.1/Flask-0.10.1.tar.gz#sha256=4c83829ff83d408b5e1d4995472265411d2c414112298f2eb4b359d9e4563373" data-gpg-sig="false" >Flask-0.10.1.tar.gz</a>
|
||||
<br/>
|
||||
<a href="3.0.1/flask-3.0.1.tar.gz#sha256=6489f51bb3666def6f314e15f19d50a1869a19ae0e8c9a3641ffe66c77d42403" data-requires-python=">=3.8" data-gpg-sig="false" >flask-3.0.1.tar.gz</a>
|
||||
<br/>
|
||||
</body>
|
||||
</html>
|
||||
"#;
|
||||
|
||||
// Note the lack of a trailing `/` here is important for coverage of url-join behavior
|
||||
let base = Url::parse("https://account.d.codeartifact.us-west-2.amazonaws.com/pypi/shared-packages-pypi/simple/flask")
|
||||
.unwrap();
|
||||
let SimpleHtml { base, files } = SimpleHtml::parse(text, &base).unwrap();
|
||||
|
||||
// Test parsing of the file urls
|
||||
let urls = files
|
||||
.iter()
|
||||
.map(|file| uv_pypi_types::base_url_join_relative(base.as_url().as_str(), &file.url))
|
||||
.collect::<Result<Vec<_>, JoinRelativeError>>()?;
|
||||
let urls = urls.iter().map(reqwest::Url::as_str).collect::<Vec<_>>();
|
||||
insta::assert_debug_snapshot!(urls, @r###"
|
||||
[
|
||||
"https://account.d.codeartifact.us-west-2.amazonaws.com/pypi/shared-packages-pypi/simple/0.1/Flask-0.1.tar.gz#sha256=9da884457e910bf0847d396cb4b778ad9f3c3d17db1c5997cb861937bd284237",
|
||||
"https://account.d.codeartifact.us-west-2.amazonaws.com/pypi/shared-packages-pypi/simple/0.10.1/Flask-0.10.1.tar.gz#sha256=4c83829ff83d408b5e1d4995472265411d2c414112298f2eb4b359d9e4563373",
|
||||
"https://account.d.codeartifact.us-west-2.amazonaws.com/pypi/shared-packages-pypi/simple/3.0.1/flask-3.0.1.tar.gz#sha256=6489f51bb3666def6f314e15f19d50a1869a19ae0e8c9a3641ffe66c77d42403",
|
||||
]
|
||||
"###);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,102 +0,0 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use url::Url;
|
||||
|
||||
use uv_normalize::PackageName;
|
||||
use uv_pypi_types::{JoinRelativeError, SimpleJson};
|
||||
|
||||
use crate::{html::SimpleHtml, SimpleMetadata, SimpleMetadatum};
|
||||
|
||||
#[test]
|
||||
fn ignore_failing_files() {
|
||||
// 1.7.7 has an invalid requires-python field (double comma), 1.7.8 is valid
|
||||
let response = r#"
|
||||
{
|
||||
"files": [
|
||||
{
|
||||
"core-metadata": false,
|
||||
"data-dist-info-metadata": false,
|
||||
"filename": "pyflyby-1.7.7.tar.gz",
|
||||
"hashes": {
|
||||
"sha256": "0c4d953f405a7be1300b440dbdbc6917011a07d8401345a97e72cd410d5fb291"
|
||||
},
|
||||
"requires-python": ">=2.5, !=3.0.*, !=3.1.*, !=3.2.*, !=3.2.*, !=3.3.*, !=3.4.*,, !=3.5.*, !=3.6.*, <4",
|
||||
"size": 427200,
|
||||
"upload-time": "2022-05-19T09:14:36.591835Z",
|
||||
"url": "https://files.pythonhosted.org/packages/61/93/9fec62902d0b4fc2521333eba047bff4adbba41f1723a6382367f84ee522/pyflyby-1.7.7.tar.gz",
|
||||
"yanked": false
|
||||
},
|
||||
{
|
||||
"core-metadata": false,
|
||||
"data-dist-info-metadata": false,
|
||||
"filename": "pyflyby-1.7.8.tar.gz",
|
||||
"hashes": {
|
||||
"sha256": "1ee37474f6da8f98653dbcc208793f50b7ace1d9066f49e2707750a5ba5d53c6"
|
||||
},
|
||||
"requires-python": ">=2.5, !=3.0.*, !=3.1.*, !=3.2.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*, <4",
|
||||
"size": 424460,
|
||||
"upload-time": "2022-08-04T10:42:02.190074Z",
|
||||
"url": "https://files.pythonhosted.org/packages/ad/39/17180d9806a1c50197bc63b25d0f1266f745fc3b23f11439fccb3d6baa50/pyflyby-1.7.8.tar.gz",
|
||||
"yanked": false
|
||||
}
|
||||
]
|
||||
}
|
||||
"#;
|
||||
let data: SimpleJson = serde_json::from_str(response).unwrap();
|
||||
let base = Url::parse("https://pypi.org/simple/pyflyby/").unwrap();
|
||||
let simple_metadata = SimpleMetadata::from_files(
|
||||
data.files,
|
||||
&PackageName::from_str("pyflyby").unwrap(),
|
||||
&base,
|
||||
);
|
||||
let versions: Vec<String> = simple_metadata
|
||||
.iter()
|
||||
.map(|SimpleMetadatum { version, .. }| version.to_string())
|
||||
.collect();
|
||||
assert_eq!(versions, ["1.7.8".to_string()]);
|
||||
}
|
||||
|
||||
/// Test for AWS Code Artifact registry
|
||||
///
|
||||
/// See: <https://github.com/astral-sh/uv/issues/1388>
|
||||
#[test]
|
||||
fn relative_urls_code_artifact() -> Result<(), JoinRelativeError> {
|
||||
let text = r#"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Links for flask</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Links for flask</h1>
|
||||
<a href="0.1/Flask-0.1.tar.gz#sha256=9da884457e910bf0847d396cb4b778ad9f3c3d17db1c5997cb861937bd284237" data-gpg-sig="false" >Flask-0.1.tar.gz</a>
|
||||
<br/>
|
||||
<a href="0.10.1/Flask-0.10.1.tar.gz#sha256=4c83829ff83d408b5e1d4995472265411d2c414112298f2eb4b359d9e4563373" data-gpg-sig="false" >Flask-0.10.1.tar.gz</a>
|
||||
<br/>
|
||||
<a href="3.0.1/flask-3.0.1.tar.gz#sha256=6489f51bb3666def6f314e15f19d50a1869a19ae0e8c9a3641ffe66c77d42403" data-requires-python=">=3.8" data-gpg-sig="false" >flask-3.0.1.tar.gz</a>
|
||||
<br/>
|
||||
</body>
|
||||
</html>
|
||||
"#;
|
||||
|
||||
// Note the lack of a trailing `/` here is important for coverage of url-join behavior
|
||||
let base = Url::parse("https://account.d.codeartifact.us-west-2.amazonaws.com/pypi/shared-packages-pypi/simple/flask")
|
||||
.unwrap();
|
||||
let SimpleHtml { base, files } = SimpleHtml::parse(text, &base).unwrap();
|
||||
|
||||
// Test parsing of the file urls
|
||||
let urls = files
|
||||
.iter()
|
||||
.map(|file| uv_pypi_types::base_url_join_relative(base.as_url().as_str(), &file.url))
|
||||
.collect::<Result<Vec<_>, JoinRelativeError>>()?;
|
||||
let urls = urls.iter().map(reqwest::Url::as_str).collect::<Vec<_>>();
|
||||
insta::assert_debug_snapshot!(urls, @r###"
|
||||
[
|
||||
"https://account.d.codeartifact.us-west-2.amazonaws.com/pypi/shared-packages-pypi/simple/0.1/Flask-0.1.tar.gz#sha256=9da884457e910bf0847d396cb4b778ad9f3c3d17db1c5997cb861937bd284237",
|
||||
"https://account.d.codeartifact.us-west-2.amazonaws.com/pypi/shared-packages-pypi/simple/0.10.1/Flask-0.10.1.tar.gz#sha256=4c83829ff83d408b5e1d4995472265411d2c414112298f2eb4b359d9e4563373",
|
||||
"https://account.d.codeartifact.us-west-2.amazonaws.com/pypi/shared-packages-pypi/simple/3.0.1/flask-3.0.1.tar.gz#sha256=6489f51bb3666def6f314e15f19d50a1869a19ae0e8c9a3641ffe66c77d42403",
|
||||
]
|
||||
"###);
|
||||
|
||||
Ok(())
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue