use super::*;
pub(super) const NIF_CARGO_LOCK_SAMPLE: &str = r#"# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "example_core"
version = "0.15.6-rc.2"
dependencies = [
"serde",
]
[[package]]
name = "example_nif"
version = "0.15.6-rc.2"
dependencies = [
"example_core",
"rustler",
]
[[package]]
name = "serde"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
"#;
fn nif_versions() -> std::collections::HashMap<String, String> {
[
("example_core".to_string(), "0.15.6-rc.3".to_string()),
("example_nif".to_string(), "0.15.6-rc.3".to_string()),
]
.into()
}
#[test]
fn sync_cargo_lock_path_versions_bumps_only_sourceless_entries() {
let out = sync_cargo_lock_path_versions(NIF_CARGO_LOCK_SAMPLE, &nif_versions()).expect("lock updated");
assert!(
out.contains("version = 4"),
"lock format version line must be preserved:\n{out}"
);
assert_eq!(
out.matches("version = \"0.15.6-rc.3\"").count(),
2,
"both local crates must be bumped:\n{out}"
);
assert!(!out.contains("0.15.6-rc.2"), "old version must be gone:\n{out}");
assert!(
out.contains("version = \"1.0.219\""),
"registry dep version must not change:\n{out}"
);
assert!(
out.contains("source = \"registry+https://github.com/rust-lang/crates.io-index\""),
"registry source line must be preserved:\n{out}"
);
}
#[test]
fn sync_cargo_lock_path_versions_is_idempotent() {
let first = sync_cargo_lock_path_versions(NIF_CARGO_LOCK_SAMPLE, &nif_versions()).unwrap();
assert!(
sync_cargo_lock_path_versions(&first, &nif_versions()).is_none(),
"second call with same version must return None"
);
}
#[test]
fn sync_cargo_lock_path_versions_preserves_no_trailing_newline() {
let no_newline = NIF_CARGO_LOCK_SAMPLE.trim_end_matches('\n');
let out = sync_cargo_lock_path_versions(no_newline, &nif_versions()).unwrap();
assert!(
!out.ends_with('\n'),
"absence of trailing newline must be preserved:\n{out:?}"
);
}
#[test]
fn sync_cargo_lock_path_versions_all_registry_returns_none() {
let content = "version = 4\n\n[[package]]\nname = \"serde\"\nversion = \"1.0.219\"\nsource = \"registry+x\"\n";
assert!(sync_cargo_lock_path_versions(content, &nif_versions()).is_none());
}
#[test]
fn sync_cargo_lock_path_versions_preserves_independently_versioned_vendor() {
let content = format!("{NIF_CARGO_LOCK_SAMPLE}\n[[package]]\nname = \"browser-driver\"\nversion = \"0.9.2\"\n");
let out = sync_cargo_lock_path_versions(&content, &nif_versions()).expect("core bumped");
let parsed: toml::Value = toml::from_str(&out).expect("valid lock");
let driver = parsed["package"]
.as_array()
.unwrap()
.iter()
.find(|package| package["name"].as_str() == Some("browser-driver"))
.unwrap();
assert_eq!(driver["version"].as_str(), Some("0.9.2"));
}
#[test]
fn sync_elixir_nif_cargo_locks_uses_each_local_manifest_version() {
use crate::core::config::NewAlefConfig;
use crate::test_support::{CwdGuard, git_add, git_init, write_file};
let tmp = tempfile::tempdir().expect("workspace");
let root = tmp.path();
write_file(
root,
"Cargo.toml",
"[workspace]\nmembers = []\n[workspace.package]\nversion = \"1.10.3\"\n",
);
for (path, name, version) in [
("crates/core/Cargo.toml", "example_core", "1.10.3"),
("packages/elixir/native/example_nif/Cargo.toml", "example_nif", "1.10.3"),
("crates/helper/Cargo.toml", "helper", "2.7.0"),
("vendor/browser/Cargo.toml", "browser-driver", "0.9.2"),
] {
write_file(
root,
path,
&format!("[package]\nname = \"{name}\"\nversion = \"{version}\"\n"),
);
}
let content = format!(
"{NIF_CARGO_LOCK_SAMPLE}\n[[package]]\nname = \"browser-driver\"\nversion = \"0.9.2\"\n\n[[package]]\nname = \"helper\"\nversion = \"2.6.0\"\n"
);
let lock = write_file(root, "packages/elixir/native/example_nif/Cargo.lock", &content);
let cfg: NewAlefConfig =
toml::from_str("[workspace]\nlanguages = [\"elixir\"]\n[[crates]]\nname = \"example\"\nsources = []\n")
.expect("config");
let mut config = cfg.resolve().expect("resolved").remove(0);
config.workspace_root = Some(root.to_path_buf());
git_init(root);
git_add(
root,
&["Cargo.toml", "crates/core/Cargo.toml", "crates/helper/Cargo.toml"],
);
let _cwd = CwdGuard::enter(root);
let writable = crate::cli::git::IgnoreFilter::for_root(root);
let mut updated = Vec::new();
sync_elixir_nif_cargo_locks(&config, &writable, "1.10.3", &mut updated).expect("sync lock");
let first = std::fs::read_to_string(&lock).expect("lock");
let document: toml::Value = toml::from_str(&first).expect("valid lock");
let versions: std::collections::HashMap<_, _> = document["package"]
.as_array()
.expect("packages")
.iter()
.map(|package| {
(
package["name"].as_str().expect("name"),
package["version"].as_str().expect("version"),
)
})
.collect();
assert_eq!(versions["example_core"], "1.10.3");
assert_eq!(versions["example_nif"], "1.10.3");
assert_eq!(versions["helper"], "2.7.0");
assert_eq!(versions["browser-driver"], "0.9.2");
assert_eq!(versions["serde"], "1.0.219");
assert_eq!(updated.len(), 1);
sync_elixir_nif_cargo_locks(&config, &writable, "1.10.3", &mut updated).expect("second sync");
assert_eq!(std::fs::read_to_string(lock).expect("second lock"), first);
assert_eq!(updated.len(), 1);
}
#[test]
fn inherited_local_versions_resolve_the_owning_workspace() {
for (label, member, nested, expected) in [
("root", "version.workspace = true\n", "", Some("1.10.3")),
(
"nested",
"version.workspace = true\n",
"[workspace]\n[workspace.package]\nversion = \"2.7.0\"\n",
Some("2.7.0"),
),
(
"explicit",
"version.workspace = true\nworkspace = \"../../independent\"\n",
"[workspace]\n",
Some("2.7.0"),
),
(
"explicit_root",
"version.workspace = true\nworkspace = \"../..\"\n",
"[workspace]\n[workspace.package]\nversion = \"2.7.0\"\n",
Some("1.10.3"),
),
("unresolved", "version.workspace = true\n", "[workspace]\n", None),
("literal", "version = \"2.7.0\"\n", "", Some("2.7.0")),
] {
let tmp = tempfile::tempdir().expect("workspace");
write_inherited_version_fixture(tmp.path(), member, nested);
let versions = crate::cli::commands::version_manifests::local_cargo_package_versions(tmp.path(), "1.10.3");
assert_eq!(
versions.len(),
usize::from(expected.is_some()),
"{label}: discovery count"
);
assert_eq!(versions.get("helper").map(String::as_str), expected, "{label}");
let lock = "version = 4\n\n[[package]]\nname = \"helper\"\nversion = \"2.6.0\"\n";
let updated = sync_cargo_lock_path_versions(lock, &versions);
if let Some(expected) = expected {
let updated = updated.expect("resolved version updates lock");
let document: toml::Value = toml::from_str(&updated).expect("valid lock");
assert_eq!(document["package"][0]["version"].as_str(), Some(expected), "{label}");
assert!(
sync_cargo_lock_path_versions(&updated, &versions).is_none(),
"{label}: idempotent"
);
} else {
assert!(updated.is_none(), "{label}: unresolved inheritance preserves lock");
}
}
}
fn write_inherited_version_fixture(root: &std::path::Path, member: &str, nested: &str) {
use crate::test_support::write_file;
write_file(
root,
"Cargo.toml",
"[workspace]\n[workspace.package]\nversion = \"1.10.3\"\n",
);
write_file(root, "helper/Cargo.toml", nested);
write_file(
root,
"helper/member/Cargo.toml",
&format!("[package]\nname = \"helper\"\n{member}"),
);
write_file(
root,
"independent/Cargo.toml",
"[workspace]\n[workspace.package]\nversion = \"2.7.0\"\n",
);
}