334 lines
10 KiB
Rust
334 lines
10 KiB
Rust
//! Go 模块协议验收只使用拥有的传输和临时摘要缓存。
|
|
|
|
use super::*;
|
|
mod fixture;
|
|
mod proofs;
|
|
|
|
#[test]
|
|
fn independent_worker_graphs_merge_without_erasing_previous_modules() {
|
|
let mut registry = fixture::graph();
|
|
registry.module(
|
|
"example.com/z",
|
|
"1.0.0",
|
|
"module example.com/z\nrequire example.com/b v1.0.0\n",
|
|
);
|
|
registry.seal();
|
|
let cache = fixture::Cache::default();
|
|
let mut lock = Lock::new(vec![]);
|
|
for root in ["go:example.com/a@1.0.0", "go:example.com/z@1.0.0"] {
|
|
resolve_with(
|
|
&[root.parse().unwrap()],
|
|
BuildTarget::host().unwrap(),
|
|
&fixture::runtime(),
|
|
®istry,
|
|
&cache,
|
|
&mut lock,
|
|
&fixture::verifier(),
|
|
)
|
|
.unwrap();
|
|
}
|
|
assert_eq!(lock.libs.len(), 5);
|
|
assert_eq!(
|
|
lock.libs
|
|
.iter()
|
|
.filter(|lib| lib.spec.name == "example.com/b")
|
|
.count(),
|
|
2
|
|
);
|
|
validate_with(&lock, &fixture::verifier()).unwrap();
|
|
for (name, version) in [("a", "1.2.0"), ("z", "1.0.0")] {
|
|
let closure = selected(
|
|
&lock.libs,
|
|
&[format!("go:example.com/{name}@1.0.0").parse().unwrap()],
|
|
)
|
|
.unwrap();
|
|
assert_eq!(
|
|
closure
|
|
.iter()
|
|
.find(|lib| lib.spec.name == "example.com/b")
|
|
.unwrap()
|
|
.spec
|
|
.version,
|
|
version
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn resolves_authenticated_mvs_graph_and_replays_locked_hashes() {
|
|
let registry = fixture::graph();
|
|
let cache = fixture::Cache::default();
|
|
let mut lock = Lock::new(vec![]);
|
|
let runtime = fixture::runtime();
|
|
resolve_with(
|
|
&["go:example.com/a@1.0.0".parse().unwrap()],
|
|
BuildTarget::host().unwrap(),
|
|
&runtime,
|
|
®istry,
|
|
&cache,
|
|
&mut lock,
|
|
&fixture::verifier(),
|
|
)
|
|
.unwrap();
|
|
assert_eq!(lock.libs.len(), 4);
|
|
assert_eq!(
|
|
selected(&lock.libs, &["go:example.com/a@1.0.0".parse().unwrap()])
|
|
.unwrap()
|
|
.into_iter()
|
|
.find(|lib| lib.spec.name == "example.com/b")
|
|
.unwrap()
|
|
.spec
|
|
.version,
|
|
"1.2.0"
|
|
);
|
|
let records = evidence(&lock).unwrap();
|
|
assert!(
|
|
records[0]
|
|
.records
|
|
.iter()
|
|
.any(|record| record.module == "example.com/b" && record.version == "1.0.0")
|
|
);
|
|
let checked = validate_with(&lock, &fixture::verifier()).unwrap();
|
|
for lib in &lock.libs {
|
|
let artifact = &lib.artifacts[0];
|
|
let bytes = cache
|
|
.get(&artifact.sha256, artifact.bytes)
|
|
.unwrap()
|
|
.unwrap();
|
|
checked
|
|
.verify_zip(&lib.spec.name, &lib.spec.version, &bytes)
|
|
.unwrap();
|
|
assert!(
|
|
checked
|
|
.verify_zip(&lib.spec.name, &lib.spec.version, &bytes[..bytes.len() / 2])
|
|
.is_err()
|
|
);
|
|
}
|
|
assert!(
|
|
validate_lock(&lock).is_err(),
|
|
"fixture signing key must never become a product trust root"
|
|
);
|
|
lock.libs[0].dependencies.clear();
|
|
assert!(validate_with(&lock, &fixture::verifier()).is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn mvs_keeps_requirements_from_unselected_versions() {
|
|
let mut registry = fixture::Registry::default();
|
|
for (name, version, requires) in [
|
|
(
|
|
"a",
|
|
"1.0.0",
|
|
"require example.com/b v1.0.0\nrequire example.com/c v1.0.0\n",
|
|
),
|
|
("b", "1.0.0", "require example.com/d v1.2.0\n"),
|
|
("b", "1.1.0", "require example.com/d v1.0.0\n"),
|
|
(
|
|
"c",
|
|
"1.0.0",
|
|
"require example.com/b v1.1.0\nrequire example.com/d v1.0.0\n",
|
|
),
|
|
("d", "1.0.0", ""),
|
|
("d", "1.2.0", ""),
|
|
] {
|
|
registry.module(
|
|
&format!("example.com/{name}"),
|
|
version,
|
|
&format!("module example.com/{name}\ngo 1.16\n{requires}"),
|
|
);
|
|
}
|
|
registry.seal();
|
|
let roots = ["go:example.com/a@1.0.0".parse().unwrap()];
|
|
let mut lock = Lock::new(vec![]);
|
|
resolve_with(
|
|
&roots,
|
|
BuildTarget::host().unwrap(),
|
|
&fixture::runtime(),
|
|
®istry,
|
|
&fixture::Cache::default(),
|
|
&mut lock,
|
|
&fixture::verifier(),
|
|
)
|
|
.unwrap();
|
|
assert_eq!(lock.libs.len(), 6);
|
|
let versions = selected(&lock.libs, &roots)
|
|
.unwrap()
|
|
.into_iter()
|
|
.map(|lib| (lib.spec.name.as_str(), lib.spec.version.as_str()))
|
|
.collect::<BTreeMap<_, _>>();
|
|
assert_eq!(versions["example.com/b"], "1.1.0");
|
|
assert_eq!(versions["example.com/d"], "1.2.0");
|
|
let older = lock
|
|
.libs
|
|
.iter_mut()
|
|
.find(|lib| lib.spec.name == "example.com/b" && lib.spec.version == "1.0.0")
|
|
.unwrap();
|
|
older.dependencies[0].spec.version = "1.0.0".into();
|
|
assert!(validate_with(&lock, &fixture::verifier()).is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn shared_module_keeps_distinct_worker_selections() {
|
|
let mut registry = fixture::Registry::default();
|
|
for (name, version, requires) in [
|
|
("x", "1.0.0", "require example.com/d v1.0.0\n"),
|
|
("y", "1.0.0", "require example.com/d v1.1.0\n"),
|
|
("d", "1.0.0", ""),
|
|
("d", "1.1.0", ""),
|
|
] {
|
|
registry.module(
|
|
&format!("example.com/{name}"),
|
|
version,
|
|
&format!("module example.com/{name}\n{requires}"),
|
|
);
|
|
}
|
|
registry.seal();
|
|
let first = vec!["go:example.com/x@1.0.0".parse().unwrap()];
|
|
let second = vec![
|
|
"go:example.com/x@1.0.0".parse().unwrap(),
|
|
"go:example.com/y@1.0.0".parse().unwrap(),
|
|
];
|
|
let mut lock = Lock::new(vec![]);
|
|
for roots in [&first, &second] {
|
|
resolve_with(
|
|
roots,
|
|
BuildTarget::host().unwrap(),
|
|
&fixture::runtime(),
|
|
®istry,
|
|
&fixture::Cache::default(),
|
|
&mut lock,
|
|
&fixture::verifier(),
|
|
)
|
|
.unwrap();
|
|
}
|
|
let restored: Lock = serde_json::from_str(&lock.encode().unwrap()).unwrap();
|
|
validate_with(&restored, &fixture::verifier()).unwrap();
|
|
assert_eq!(restored.libs.len(), 4);
|
|
for (roots, expected) in [(&first, "1.0.0"), (&second, "1.1.0")] {
|
|
let closure = selected(&restored.libs, roots).unwrap();
|
|
assert_eq!(
|
|
closure
|
|
.iter()
|
|
.filter(|lib| lib.spec.name == "example.com/d")
|
|
.count(),
|
|
1
|
|
);
|
|
assert_eq!(
|
|
closure
|
|
.iter()
|
|
.find(|lib| lib.spec.name == "example.com/d")
|
|
.unwrap()
|
|
.spec
|
|
.version,
|
|
expected
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn rejected_metadata_archive_or_rollback_never_publishes_lock() {
|
|
let registry = fixture::graph();
|
|
let runtime = fixture::runtime();
|
|
for suffix in ["mod", "zip"] {
|
|
let mut changed = registry.clone();
|
|
changed
|
|
.0
|
|
.get_mut(&format!(
|
|
"https://proxy.golang.org/example.com/b/@v/v1.0.0.{suffix}"
|
|
))
|
|
.unwrap()
|
|
.extend(b"changed");
|
|
if suffix == "zip" {
|
|
changed.0.insert(
|
|
"https://proxy.golang.org/example.com/b/@v/v1.2.0.zip".into(),
|
|
fixture::zip("example.com/b", "1.2.0", "changed"),
|
|
);
|
|
}
|
|
let cache = fixture::Cache::default();
|
|
let mut lock = Lock::new(vec![]);
|
|
let before = lock.encode().unwrap();
|
|
assert!(
|
|
resolve_with(
|
|
&["go:example.com/a@1.0.0".parse().unwrap()],
|
|
BuildTarget::host().unwrap(),
|
|
&runtime,
|
|
&changed,
|
|
&cache,
|
|
&mut lock,
|
|
&fixture::verifier()
|
|
)
|
|
.is_err()
|
|
);
|
|
assert_eq!(lock.encode().unwrap(), before);
|
|
assert!(cache.0.borrow().is_empty());
|
|
}
|
|
let cache = fixture::Cache::default();
|
|
let mut lock = Lock::new(vec![]);
|
|
let mut first = fixture::Registry::default();
|
|
first.module("example.com/a", "1.0.0", "module example.com/a\n");
|
|
first.seal();
|
|
let roots = ["go:example.com/a@1.0.0".parse().unwrap()];
|
|
resolve_with(
|
|
&roots,
|
|
BuildTarget::host().unwrap(),
|
|
&runtime,
|
|
&first,
|
|
&cache,
|
|
&mut lock,
|
|
&fixture::verifier(),
|
|
)
|
|
.unwrap();
|
|
let mut next = first.clone();
|
|
next.module("example.com/z", "1.0.0", "module example.com/z\n");
|
|
next.seal();
|
|
resolve_with(
|
|
&roots,
|
|
BuildTarget::host().unwrap(),
|
|
&runtime,
|
|
&next,
|
|
&cache,
|
|
&mut lock,
|
|
&fixture::verifier(),
|
|
)
|
|
.unwrap();
|
|
assert_eq!(lock.go_sumdb.len(), 2);
|
|
let before = lock.encode().unwrap();
|
|
let error = resolve_with(
|
|
&roots,
|
|
BuildTarget::host().unwrap(),
|
|
&runtime,
|
|
&first,
|
|
&cache,
|
|
&mut lock,
|
|
&fixture::verifier(),
|
|
)
|
|
.unwrap_err();
|
|
assert!(error.contains("rollback"), "{error}");
|
|
assert_eq!(lock.encode().unwrap(), before);
|
|
}
|
|
|
|
#[test]
|
|
fn module_directives_check_identity_toolchain_and_requirements() {
|
|
let source = b"module example.com/a\ngo 1.25\nrequire (\nexample.com/b v1.2.0 // indirect\n)\n";
|
|
let parsed = module::requirements(source, "example.com/a", "1.25.0").unwrap();
|
|
assert_eq!(parsed["example.com/b"], "1.2.0");
|
|
assert!(module::requirements(source, "example.com/other", "1.25.0").is_err());
|
|
assert!(module::requirements(source, "example.com/a", "1.24.0").is_err());
|
|
for directive in [
|
|
"replace example.com/b => ../local",
|
|
"exclude example.com/b v1.0.0",
|
|
"require (\nexample.com/b v1.0.0",
|
|
"module example.com/a",
|
|
] {
|
|
assert!(
|
|
module::requirements(
|
|
format!("module example.com/a\n{directive}\n").as_bytes(),
|
|
"example.com/a",
|
|
"1.25.0"
|
|
)
|
|
.is_err()
|
|
);
|
|
}
|
|
assert_eq!(module::escape("example.com/Mixed"), "example.com/!mixed");
|
|
}
|