Initial Dever 0.0.1

This commit is contained in:
Codex committed 2026-11-03 03:32:07 +00:00
commit 81233d098c
1479 files changed
+163782

No files matched your search

+276
View File
@@ -0,0 +1,276 @@
use super::Fixture;
use dever::compiler::project::dependency::library::npm::{self, Kind, Omission};
use serde_json::json;
use std::collections::BTreeMap;
#[test]
fn npm_scoped_metadata_uses_strict_official_url_policy() {
let mut fixture = Fixture::new();
fixture.package(
json!({"name":"app","version":"1.0.0","dependencies":{"@types/node":"^22"}}),
BTreeMap::new(),
);
fixture.package(
json!({"name":"@types/node","version":"22.10.0"}),
BTreeMap::new(),
);
fixture.resolve(&["npm:app@1.0.0"]).unwrap();
for address in [
"https://registry.npmjs.org/@types/node",
"https://registry.npmjs.org/@types/node/22.10.0",
"https://registry.npmjs.org/@types/node/-/archive-22.10.0.tgz",
] {
assert!(fixture.calls.borrow().iter().any(|url| url == address));
}
let graph = fixture.graph();
let node = graph
.instances
.iter()
.find(|node| node.spec.name == "@types/node")
.unwrap();
let files = fixture.install().unwrap();
assert_eq!(
files[&format!("{}/index.js", node.path)],
b"module.exports = 42;"
);
let lib = fixture
.lock
.libs
.iter()
.find(|lib| lib.spec.name == "@types/node")
.unwrap();
let mut source = lib.artifacts[0].source.clone().unwrap();
for locator in [
"https://registry.npmjs.org/@types%2fnode/-/archive-22.10.0.tgz",
"https://registry.npmjs.org/@types%2Fnode/-/archive-22.10.0.tgz",
"https://registry.npmjs.org/@types/node/%2e%2e/archive-22.10.0.tgz",
] {
source.locator = locator.into();
assert!(source.validate(&lib.spec).is_err());
}
}
#[test]
fn npm_nested_instances_keep_peer_context_and_install_offline() {
let mut fixture = Fixture::new();
for document in [
json!({"name":"left","version":"1.0.0","dependencies":{"plugin":"1.0.0","engine":"1.0.0"}}),
json!({"name":"right","version":"1.0.0","dependencies":{"plugin":"1.0.0","engine":"2.0.0"}}),
json!({"name":"plugin","version":"1.0.0","peerDependencies":{"engine":">=1 <3"}}),
json!({"name":"engine","version":"1.0.0"}),
json!({"name":"engine","version":"2.0.0"}),
] {
fixture.package(document, BTreeMap::new());
}
fixture
.resolve(&["npm:left@1.0.0", "npm:right@1.0.0"])
.unwrap();
let graph = fixture.graph();
for root in ["left", "right"] {
let plugin = graph
.instances
.iter()
.find(|node| node.path == format!("node_modules/{root}/node_modules/plugin"))
.unwrap();
assert_eq!(plugin.edges[0].kind, Kind::Peer);
assert_eq!(
plugin.edges[0].target.as_deref(),
Some(format!("node_modules/{root}/node_modules/engine").as_str())
);
}
fixture.documents.clear();
let calls = fixture.calls.borrow().len();
let files = fixture.install().unwrap();
assert_eq!(files.len(), 12);
assert_eq!(fixture.calls.borrow().len(), calls);
assert_eq!(fixture.lock.libs.len(), 5);
}
#[test]
fn npm_peer_conflict_backtracks_versions_and_incompatible_roots_fail_atomically() {
let mut fixture = Fixture::new();
for document in [
json!({"name":"app","version":"1.0.0","dependencies":{"plugin":"*","engine":"1.0.0"}}),
json!({"name":"plugin","version":"2.0.0","peerDependencies":{"engine":"2.0.0"}}),
json!({"name":"plugin","version":"1.0.0","peerDependencies":{"engine":"1.0.0"}}),
json!({"name":"engine","version":"1.0.0"}),
json!({"name":"engine","version":"2.0.0"}),
] {
fixture.package(document, BTreeMap::new());
}
fixture.resolve(&["npm:app@1.0.0"]).unwrap();
assert_eq!(
fixture
.graph()
.instances
.iter()
.find(|node| node.spec.name == "plugin")
.unwrap()
.spec
.version,
"1.0.0"
);
let before = fixture.lock.encode().unwrap();
assert!(
fixture
.resolve(&["npm:engine@1.0.0", "npm:engine@2.0.0"])
.unwrap_err()
.contains("incompatible")
);
assert_eq!(fixture.lock.encode().unwrap(), before);
}
#[test]
fn npm_optional_target_and_absent_peer_are_explicit_but_integrity_errors_are_fatal() {
let mut fixture = Fixture::new();
fixture.package(json!({"name":"app","version":"1.0.0","optionalDependencies":{"wrong":"*","missing":"*"},"peerDependencies":{"peer":"*"},"peerDependenciesMeta":{"peer":{"optional":true}}}),BTreeMap::new());
fixture.package(
json!({"name":"wrong","version":"1.0.0","os":["!linux"]}),
BTreeMap::new(),
);
fixture.resolve(&["npm:app@1.0.0"]).unwrap();
let graph = fixture.graph();
let edges = &graph.instances[0].edges;
for (name, reason) in [
("wrong", Omission::Target),
("missing", Omission::Unavailable),
("peer", Omission::OptionalPeerAbsent),
] {
assert_eq!(
edges
.iter()
.find(|edge| edge.name == name)
.unwrap()
.omission,
Some(reason)
);
}
fixture.install().unwrap();
let before = fixture.lock.encode().unwrap();
assert!(
fixture
.resolve(&["npm:missing@1.0.0"])
.unwrap_err()
.contains("no npm version")
);
assert_eq!(fixture.lock.encode().unwrap(), before);
fixture.documents.insert(
"https://registry.npmjs.org/wrong/-/archive-1.0.0.tgz".into(),
b"corrupt optional archive".to_vec(),
);
assert!(
fixture
.resolve(&["npm:app@1.0.0"])
.unwrap_err()
.contains("integrity")
);
assert_eq!(fixture.lock.encode().unwrap(), before);
}
#[test]
fn npm_one_version_cannot_change_bytes_between_worker_groups() {
let mut fixture = Fixture::new();
let metadata = json!({"name":"shared","version":"1.0.0"});
fixture.package(metadata.clone(), BTreeMap::new());
fixture.resolve(&["npm:shared@1.0.0"]).unwrap();
let before = fixture.lock.encode().unwrap();
fixture.package(
metadata,
BTreeMap::from([("package/changed.js".into(), b"different bytes".to_vec())]),
);
let error = fixture.resolve(&["npm:shared@1.0.0"]).unwrap_err();
assert!(error.contains("artifact differs"), "{error}");
assert_eq!(fixture.lock.encode().unwrap(), before);
}
#[test]
fn npm_bundled_packages_have_one_owner_and_tampered_manifest_or_graph_fails() {
let mut fixture = Fixture::new();
fixture.package(json!({"name":"app","version":"1.0.0","dependencies":{"tiny":"1.0.0"},"bundleDependencies":["tiny"]}),BTreeMap::from([
("package/node_modules/tiny/package.json".into(),serde_json::to_vec(&json!({"name":"tiny","version":"1.0.0"})).unwrap()),
("package/node_modules/tiny/index.js".into(),b"module.exports=7".to_vec()),
]));
fixture.resolve(&["npm:app@1.0.0"]).unwrap();
assert_eq!(fixture.lock.libs.len(), 1);
assert_eq!(fixture.install().unwrap().len(), 4);
let original = fixture.lock.npm[0].clone();
fixture.lock.npm[0]["instances"][0]["edges"][0]["requirement"] = json!("2.0.0");
assert!(fixture.install().unwrap_err().contains("range"));
fixture.lock.npm[0] = original;
let mut graph = fixture.graph();
graph.instances[1].source.prefix = "package/".into();
assert!(
npm::validate(&graph, &fixture.lock)
.unwrap_err()
.contains("owned")
);
}
#[test]
fn npm_registry_and_archive_metadata_must_agree_and_urls_are_official() {
let mut fixture = Fixture::new();
fixture.package(json!({"name":"app","version":"1.0.0"}), BTreeMap::new());
let address = "https://registry.npmjs.org/app/1.0.0";
let original = fixture.documents[address].clone();
let mut metadata: serde_json::Value = serde_json::from_slice(&original).unwrap();
metadata["dependencies"] = json!({"hidden":"1.0.0"});
fixture
.documents
.insert(address.into(), serde_json::to_vec(&metadata).unwrap());
assert!(
fixture
.resolve(&["npm:app@1.0.0"])
.unwrap_err()
.contains("metadata")
);
let mut metadata: serde_json::Value = serde_json::from_slice(&original).unwrap();
metadata["dist"]["tarball"] = json!("https://attacker.invalid/app.tgz");
fixture
.documents
.insert(address.into(), serde_json::to_vec(&metadata).unwrap());
assert!(
fixture
.resolve(&["npm:app@1.0.0"])
.unwrap_err()
.contains("official")
);
assert!(
!fixture
.calls
.borrow()
.iter()
.any(|url| url.contains("attacker"))
);
}
#[test]
fn npm_cycles_close_without_infinite_nested_copies_and_missing_build_receipt_fails() {
let mut fixture = Fixture::new();
fixture.package(
json!({"name":"alpha","version":"1.0.0","dependencies":{"beta":"1.0.0"}}),
BTreeMap::new(),
);
fixture.package(
json!({"name":"beta","version":"1.0.0","dependencies":{"alpha":"1.0.0"}}),
BTreeMap::new(),
);
fixture.resolve(&["npm:alpha@1.0.0"]).unwrap();
assert_eq!(fixture.graph().instances.len(), 2);
assert_eq!(fixture.install().unwrap().len(), 4);
fixture.package(
json!({"name":"hook","version":"1.0.0","scripts":{"install":"must not run"}}),
BTreeMap::new(),
);
fixture.resolve(&["npm:hook@1.0.0"]).unwrap();
let graph = fixture
.lock
.npm
.iter()
.find(|value| value["roots"][0]["name"] == "hook")
.unwrap()
.clone();
fixture.lock.npm = vec![graph];
assert!(fixture.install().unwrap_err().contains("lifecycle"));
fixture.lock.npm[0]["build"] = json!("0".repeat(64));
assert!(fixture.install().unwrap_err().contains("receipt"));
}