use super::*;
use crate::core::config::{ResolvedCrateConfig, SyncConfig, TextReplacement};
use tracing_test::traced_test;
fn catch_all_config(root: &std::path::Path, sync: SyncConfig) -> ResolvedCrateConfig {
ResolvedCrateConfig {
name: "sample_core".to_string(),
version_from: root.join("Cargo.toml").to_string_lossy().into_owned(),
sources: vec![root.join("src/lib.rs")],
workspace_root: Some(root.to_path_buf()),
sync: Some(sync),
..ResolvedCrateConfig::default()
}
}
fn seed_crate(root: &std::path::Path) {
std::fs::create_dir_all(root.join("src")).expect("source directory");
std::fs::write(root.join("src/lib.rs"), "pub fn transform() {}\n").expect("Rust source");
std::fs::write(
root.join("Cargo.toml"),
"[package]\nname = \"sample_core\"\nversion = \"1.2.3\"\nedition = \"2024\"\n",
)
.expect("Cargo.toml");
}
fn run_sync(root: &std::path::Path, config: &ResolvedCrateConfig) {
let config_path = root.join("alef.toml");
std::fs::write(&config_path, "").expect("alef.toml");
let original_cwd = std::env::current_dir().expect("current directory");
std::env::set_current_dir(root).expect("enter temporary directory");
let result = sync_versions(config, &config_path, None, true, true, None);
std::env::set_current_dir(&original_cwd).expect("restore current directory");
result.expect("sync versions");
}
#[test]
fn catch_all_semver_rewrite_leaves_a_stampable_file_that_carries_no_marker() {
let _guard = CWD_LOCK.lock().unwrap_or_else(|error| error.into_inner());
let temporary = tempfile::tempdir().expect("temporary directory");
let root = temporary.path();
seed_crate(root);
let hand_written = "# maintained by hand\npinned = \"9.9.9\"\n";
std::fs::write(root.join("notes.toml"), hand_written).expect("hand-written file");
run_sync(
root,
&catch_all_config(
root,
SyncConfig {
extra_paths: vec!["notes.toml".to_string()],
text_replacements: Vec::new(),
},
),
);
assert_eq!(
std::fs::read_to_string(root.join("notes.toml")).expect("read notes.toml"),
hand_written,
"a stampable file with no alef marker reads as hand-written and must survive the catch-all"
);
}
#[test]
fn catch_all_semver_rewrite_still_updates_a_stampable_file_alef_owns() {
let _guard = CWD_LOCK.lock().unwrap_or_else(|error| error.into_inner());
let temporary = tempfile::tempdir().expect("temporary directory");
let root = temporary.path();
seed_crate(root);
let owned = format!(
"{}\npinned = \"9.9.9\"\n",
crate::core::hash::header(crate::core::hash::CommentStyle::Hash)
);
std::fs::write(root.join("owned.toml"), &owned).expect("alef-owned file");
run_sync(
root,
&catch_all_config(
root,
SyncConfig {
extra_paths: vec!["owned.toml".to_string()],
text_replacements: Vec::new(),
},
),
);
let after = std::fs::read_to_string(root.join("owned.toml")).expect("read owned.toml");
assert!(
after.contains("1.2.3") && !after.contains("9.9.9"),
"an alef-owned file must still be rewritten by the catch-all, got:\n{after}"
);
}
#[test]
fn catch_all_rewrite_still_touches_an_unstampable_file_because_absence_proves_nothing() {
let _guard = CWD_LOCK.lock().unwrap_or_else(|error| error.into_inner());
let temporary = tempfile::tempdir().expect("temporary directory");
let root = temporary.path();
seed_crate(root);
std::fs::write(root.join("NOTES.md"), "docs for 9.9.9\n").expect("markdown file");
run_sync(
root,
&catch_all_config(
root,
SyncConfig {
extra_paths: vec!["NOTES.md".to_string()],
text_replacements: Vec::new(),
},
),
);
assert!(
std::fs::read_to_string(root.join("NOTES.md"))
.expect("read NOTES.md")
.contains("1.2.3"),
"an extension alef cannot stamp must stay eligible — marker absence is not evidence there"
);
}
#[test]
fn text_replacement_leaves_a_stampable_file_that_carries_no_marker() {
let _guard = CWD_LOCK.lock().unwrap_or_else(|error| error.into_inner());
let temporary = tempfile::tempdir().expect("temporary directory");
let root = temporary.path();
seed_crate(root);
let hand_written = "version: 9.9.9\n";
std::fs::write(root.join("hand.yaml"), hand_written).expect("hand-written yaml");
run_sync(
root,
&catch_all_config(
root,
SyncConfig {
extra_paths: Vec::new(),
text_replacements: vec![TextReplacement {
path: "hand.yaml".to_string(),
search: r"version: [0-9.]+".to_string(),
replace: "version: {version}".to_string(),
}],
},
),
);
assert_eq!(
std::fs::read_to_string(root.join("hand.yaml")).expect("read hand.yaml"),
hand_written,
"text_replacements must honour the same ownership predicate as the semver catch-all"
);
}
#[traced_test]
#[test]
fn text_replacement_refusal_warns_that_the_declared_sync_target_went_unwritten() {
let _guard = CWD_LOCK.lock().unwrap_or_else(|error| error.into_inner());
let temporary = tempfile::tempdir().expect("temporary directory");
let root = temporary.path();
seed_crate(root);
std::fs::write(root.join("hand.yaml"), "version: 9.9.9\n").expect("hand-written yaml");
run_sync(
root,
&catch_all_config(
root,
SyncConfig {
extra_paths: Vec::new(),
text_replacements: vec![TextReplacement {
path: "hand.yaml".to_string(),
search: r"version: [0-9.]+".to_string(),
replace: "version: {version}".to_string(),
}],
},
),
);
assert!(
logs_contain("declared sync.text_replacements target was not updated to 1.2.3"),
"expected a sync-target-not-updated warning naming the expected version"
);
assert!(
logs_contain("ownership guard refused the write"),
"expected the warning to name the refusal reason"
);
assert!(
logs_contain("hand.yaml"),
"expected the warning to name the refused file"
);
}
#[traced_test]
#[test]
fn text_replacement_success_does_not_warn_that_the_sync_target_went_unwritten() {
let _guard = CWD_LOCK.lock().unwrap_or_else(|error| error.into_inner());
let temporary = tempfile::tempdir().expect("temporary directory");
let root = temporary.path();
seed_crate(root);
let owned = format!(
"{}\nversion: 9.9.9\n",
crate::core::hash::header(crate::core::hash::CommentStyle::Hash)
);
std::fs::write(root.join("owned.yaml"), &owned).expect("alef-owned yaml");
run_sync(
root,
&catch_all_config(
root,
SyncConfig {
extra_paths: Vec::new(),
text_replacements: vec![TextReplacement {
path: "owned.yaml".to_string(),
search: r"version: [0-9.]+".to_string(),
replace: "version: {version}".to_string(),
}],
},
),
);
let after = std::fs::read_to_string(root.join("owned.yaml")).expect("read owned.yaml");
assert!(
after.contains("1.2.3") && !after.contains("9.9.9"),
"an alef-owned text_replacements target must still be rewritten, got:\n{after}"
);
assert!(
!logs_contain("was not updated"),
"a successfully-written sync target must not warn that it went unwritten"
);
}