use super::*;
use base64::Engine;
fn drift_test_config() -> crate::core::config::ResolvedCrateConfig {
let cfg: crate::core::config::NewAlefConfig = toml::from_str(
r#"
[workspace]
languages = ["rust"]
[[crates]]
name = "sample"
sources = ["src/lib.rs"]
"#,
)
.expect("valid config");
cfg.resolve().expect("resolvable").remove(0)
}
fn gen_file(rel: &str, content: &str) -> crate::core::backend::GeneratedFile {
crate::core::backend::GeneratedFile {
path: std::path::PathBuf::from(rel),
content: content.to_string(),
generated_header: true,
}
}
fn gen_file_unheadered(rel: &str, content: &str) -> crate::core::backend::GeneratedFile {
crate::core::backend::GeneratedFile {
path: std::path::PathBuf::from(rel),
content: content.to_string(),
generated_header: false,
}
}
fn e2e_owned_output_config() -> crate::core::config::ResolvedCrateConfig {
let cfg: crate::core::config::NewAlefConfig = toml::from_str(
r#"
[workspace]
languages = ["rust"]
[[crates]]
name = "sample"
sources = ["src/lib.rs"]
[crates.e2e]
fixtures = "fixtures"
output = "e2e"
[crates.e2e.call]
function = "run"
[crates.e2e.registry]
output = "test_apps"
"#,
)
.expect("valid config");
cfg.resolve().expect("resolvable").remove(0)
}
fn seed_owned_output(base: &std::path::Path, relative: &str, content: &[u8]) {
let full_path = base.join(relative);
std::fs::create_dir_all(full_path.parent().expect("output parent")).expect("create output parent");
std::fs::write(&full_path, content).expect("seed owned output");
crate::cli::cache::record_scaffold_owned_path(base, &full_path).expect("record output ownership");
}
#[test]
fn drifted_managed_paths_compares_current_and_stale_owned_maven_wrappers() {
let Some(_poly) = crate::test_support::tool_available_with_stable_path("poly") else {
return;
};
let dir = tempfile::tempdir().expect("tempdir");
let current = "e2e/java/mvnw";
let stale = "e2e/java/mvnw.cmd";
seed_owned_output(dir.path(), current, b"#!/bin/sh\nexec mvn\n");
seed_owned_output(dir.path(), stale, b"@echo old\r\n");
let files = vec![
gen_file_unheadered(current, "#!/bin/sh\nexec mvn\n"),
gen_file_unheadered(stale, "@echo new\r\n"),
];
let (drifted, stats) = drifted_managed_paths(&files, dir.path(), &e2e_owned_output_config());
assert_eq!(drifted, vec![dir.path().join(stale).display().to_string()]);
assert_eq!(
stats.content_verified_paths,
std::collections::HashSet::from([dir.path().join(current), dir.path().join(stale)])
);
}
#[test]
fn drifted_managed_paths_compares_current_and_stale_owned_gradle_wrappers() {
let Some(_poly) = crate::test_support::tool_available_with_stable_path("poly") else {
return;
};
let dir = tempfile::tempdir().expect("tempdir");
let current = "test_apps/kotlin_android/gradlew";
let stale = "test_apps/kotlin_android/gradle/wrapper/gradle-wrapper.properties";
seed_owned_output(dir.path(), current, b"#!/bin/sh\nexec gradle\n");
seed_owned_output(dir.path(), stale, b"distributionUrl=old\n");
let files = vec![
gen_file_unheadered(current, "#!/bin/sh\nexec gradle\n"),
gen_file_unheadered(stale, "distributionUrl=new\n"),
];
let (drifted, stats) = drifted_managed_paths(&files, dir.path(), &e2e_owned_output_config());
assert_eq!(drifted, vec![dir.path().join(stale).display().to_string()]);
assert_eq!(
stats.content_verified_paths,
std::collections::HashSet::from([dir.path().join(current), dir.path().join(stale)])
);
}
#[test]
fn drifted_managed_paths_compares_owned_gradle_wrapper_jar_bytes() {
const JAR: &str = "test_apps/kotlin_android/gradle/wrapper/gradle-wrapper.jar";
let dir = tempfile::tempdir().expect("tempdir");
let generated = [0x50, 0x4b, 0x03, 0x04, 0xff];
seed_owned_output(dir.path(), JAR, &generated);
let file = gen_file_unheadered(JAR, &base64::engine::general_purpose::STANDARD.encode(generated));
let (current, current_stats) =
drifted_managed_paths(std::slice::from_ref(&file), dir.path(), &e2e_owned_output_config());
assert!(current.is_empty());
assert_eq!(
current_stats.content_verified_paths,
std::collections::HashSet::from([dir.path().join(JAR)])
);
std::fs::write(dir.path().join(JAR), [0x50, 0x4b, 0x03]).expect("mutate wrapper jar");
let (stale, stale_stats) =
drifted_managed_paths(std::slice::from_ref(&file), dir.path(), &e2e_owned_output_config());
assert_eq!(stale, vec![dir.path().join(JAR).display().to_string()]);
assert_eq!(
stale_stats.content_verified_paths,
std::collections::HashSet::from([dir.path().join(JAR)])
);
}
#[test]
fn drifted_managed_paths_ignores_unowned_markerless_wrapper() {
let dir = tempfile::tempdir().expect("tempdir");
let relative = "e2e/java/mvnw";
let full_path = dir.path().join(relative);
std::fs::create_dir_all(full_path.parent().expect("output parent")).expect("create output parent");
std::fs::write(&full_path, "old\n").expect("seed unowned wrapper");
let (drifted, stats) = drifted_managed_paths(
&[gen_file_unheadered(relative, "new\n")],
dir.path(),
&e2e_owned_output_config(),
);
assert!(drifted.is_empty());
assert!(stats.content_verified_paths.is_empty());
}
#[test]
fn drifted_managed_paths_reports_invalid_utf8_owned_text_as_drift() {
let dir = tempfile::tempdir().expect("tempdir");
let relative = "e2e/java/mvnw";
seed_owned_output(dir.path(), relative, &[0xff, 0xfe, 0xfd]);
let (drifted, stats) = drifted_managed_paths(
&[gen_file_unheadered(relative, "#!/bin/sh\nexec mvn\n")],
dir.path(),
&e2e_owned_output_config(),
);
assert_eq!(drifted, vec![dir.path().join(relative).display().to_string()]);
assert!(stats.content_verified_paths.is_empty());
}
#[test]
fn drifted_managed_paths_preserves_create_once_exclusion_for_owned_seed() {
let dir = tempfile::tempdir().expect("tempdir");
let relative = "Cargo.toml";
seed_owned_output(dir.path(), relative, b"[package]\nname = \"consumer\"\n");
let (drifted, stats) = drifted_managed_paths(
&[gen_file_unheadered(relative, "[package]\nname = \"generated\"\n")],
dir.path(),
&e2e_owned_output_config(),
);
assert!(drifted.is_empty());
assert!(stats.content_verified_paths.is_empty());
}
#[test]
fn managed_surface_reports_a_mutated_owned_java_wrapper() {
let Some(_poly) = crate::test_support::tool_available_with_stable_path("poly") else {
return;
};
let dir = tempfile::tempdir().expect("tempdir");
std::fs::create_dir_all(dir.path().join("src")).expect("create source directory");
std::fs::create_dir_all(dir.path().join("fixtures")).expect("create fixtures directory");
std::fs::write(dir.path().join("src/lib.rs"), "pub fn noop() {}\n").expect("write source");
std::fs::write(
dir.path().join("Cargo.toml"),
"[package]\nname = \"sample_crate\"\nversion = \"0.1.0\"\nedition = \"2024\"\n",
)
.expect("write Cargo.toml");
std::fs::write(
dir.path().join("fixtures/smoke.json"),
r#"{
"id": "wrapper_smoke",
"description": "wrapper smoke",
"category": "smoke",
"tags": ["smoke"],
"input": {},
"assertions": []
}
"#,
)
.expect("write fixture");
let config_toml = r#"
[workspace]
languages = ["java"]
[workspace.package_metadata]
repository = "https://github.com/example/sample-crate"
authors = ["Example Author <author@example.invalid>"]
license = "MIT"
[[crates]]
name = "sample_crate"
sources = ["src/lib.rs"]
[crates.java]
package = "dev.sample_crate"
ffi_style = "panama"
[crates.e2e]
fixtures = "fixtures"
output = "e2e"
[crates.e2e.call]
function = "noop"
result_var = "result"
"#;
let config_path = dir.path().join("alef.toml");
std::fs::write(&config_path, config_toml).expect("write config");
let config: crate::core::config::NewAlefConfig = toml::from_str(config_toml).expect("config parses");
let resolved = config.resolve().expect("config resolves").remove(0);
let _cwd = crate::test_support::CwdGuard::enter(dir.path());
let api = crate::cli::pipeline::extract(&resolved, &config_path, false).expect("extract source API");
let (surface, stage_failures) = collect_managed_surface(
&[crate::core::config::Language::Java],
&api,
&resolved,
&config_path,
dir.path(),
)
.expect("collect real managed surface");
assert!(
stage_failures.is_empty(),
"Java render must succeed without stage failures"
);
let wrapper = surface
.iter()
.find(|file| file.path == std::path::Path::new("e2e/java/mvnw"))
.expect("real Java E2E surface must include mvnw");
let rendered = crate::cli::commands::adopt::managed_outputs(std::slice::from_ref(wrapper), dir.path())
.pop()
.expect("one managed wrapper output");
let full_path = dir.path().join(&wrapper.path);
std::fs::create_dir_all(full_path.parent().expect("wrapper parent")).expect("create wrapper parent");
std::fs::write(&full_path, rendered.content).expect("seed rendered wrapper");
crate::cli::cache::record_scaffold_owned_path(dir.path(), &full_path).expect("record wrapper ownership");
std::fs::write(&full_path, "tampered wrapper\n").expect("mutate wrapper");
let found = find_missing_and_frozen_generated_files(
&[crate::core::config::Language::Java],
&api,
&resolved,
&config_path,
dir.path(),
)
.expect("verify managed surface");
assert_eq!(found.drifted, vec![full_path.display().to_string()]);
}
#[test]
fn drifted_managed_paths_reports_a_marked_file_whose_body_no_longer_matches() {
let _poly = crate::test_support::tool_available_with_stable_path("poly");
let dir = tempfile::tempdir().expect("tempdir");
let old_file = gen_file("lib.rs", "pub fn greet() -> &'static str { \"old\" }\n");
let old_rendered = crate::cli::commands::adopt::managed_outputs(std::slice::from_ref(&old_file), dir.path());
std::fs::write(dir.path().join("lib.rs"), &old_rendered[0].content).unwrap();
let files = vec![gen_file("lib.rs", "pub fn greet() -> &'static str { \"new\" }\n")];
let (drifted, _stats) = drifted_managed_paths(&files, dir.path(), &drift_test_config());
assert_eq!(
drifted,
vec![dir.path().join("lib.rs").display().to_string()],
"a marked file whose source changed since it was last written must be reported drifted"
);
}
#[test]
fn drifted_managed_paths_is_silent_once_the_marked_file_matches_the_fresh_render() {
let _poly = crate::test_support::tool_available_with_stable_path("poly");
let dir = tempfile::tempdir().expect("tempdir");
let file = gen_file("lib.rs", "pub fn greet() -> &'static str { \"new\" }\n");
let rendered = crate::cli::commands::adopt::managed_outputs(std::slice::from_ref(&file), dir.path());
std::fs::write(dir.path().join("lib.rs"), &rendered[0].content).unwrap();
let files = vec![file];
let (drifted, _stats) = drifted_managed_paths(&files, dir.path(), &drift_test_config());
assert!(drifted.is_empty());
}
#[test]
fn drifted_managed_paths_ignores_a_declared_user_owned_file_even_when_it_still_has_an_old_marker() {
let dir = tempfile::tempdir().expect("tempdir");
std::fs::create_dir_all(dir.path().join("test")).expect("test directory");
std::fs::write(
dir.path().join("alef.toml"),
"[workspace]\nlanguages = [\"rust\"]\n\
[workspace.ownership]\nuser_owned = [\"test/manual.rs\"]\n\
[[crates]]\nname = \"sample\"\nsources = [\"src/lib.rs\"]\n",
)
.expect("ownership config");
let old_file = gen_file("test/manual.rs", "pub fn manual() -> u8 { 1 }\n");
let rendered = crate::cli::commands::adopt::managed_outputs(std::slice::from_ref(&old_file), dir.path());
std::fs::write(dir.path().join("test/manual.rs"), &rendered[0].content).expect("marked legacy file");
let fresh = vec![gen_file("test/manual.rs", "pub fn manual() -> u8 { 2 }\n")];
let (drifted, stats) = drifted_managed_paths(&fresh, dir.path(), &drift_test_config());
assert!(
drifted.is_empty(),
"a declared user-owned path is outside alef's drift verdict"
);
assert_eq!(stats, FormatDriftStats::default());
}
#[test]
fn drifted_managed_paths_ignores_a_file_that_carries_no_marker_at_all() {
let dir = tempfile::tempdir().expect("tempdir");
std::fs::write(dir.path().join("lib.rs"), "pub fn greet() {}\n").unwrap();
let files = vec![gen_file("lib.rs", "pub fn greet() { /* changed */ }\n")];
let (drifted, _stats) = drifted_managed_paths(&files, dir.path(), &drift_test_config());
assert!(drifted.is_empty());
}
#[test]
fn drifted_managed_paths_now_catches_a_toml_file_via_the_real_poly_fmt_pass() {
let Some(_poly) = crate::test_support::tool_available_with_stable_path("poly") else {
return;
};
let dir = tempfile::tempdir().expect("tempdir");
let header = crate::core::hash::header(crate::core::hash::CommentStyle::Hash);
std::fs::write(
dir.path().join("pyproject.toml"),
format!("{header}\n[project]\nname = \"old\"\n"),
)
.unwrap();
let files = vec![gen_file("pyproject.toml", "[project]\nname=\"new\"\n")];
let (drifted, stats) = drifted_managed_paths(&files, dir.path(), &drift_test_config());
assert_eq!(
drifted,
vec![dir.path().join("pyproject.toml").display().to_string()],
"a marked TOML file whose source changed must now be reported drifted via the real \
formatter pass, not silently predicted-and-missed"
);
assert_eq!(
stats.compared, 1,
"the real-formatter pass must have actually examined this file"
);
assert_eq!(stats.skipped_missing_formatter, 0);
}
#[test]
fn drifted_managed_paths_is_silent_once_a_toml_file_matches_the_real_formatters_output() {
let Some(_poly) = crate::test_support::tool_available_with_stable_path("poly") else {
return;
};
let dir = tempfile::tempdir().expect("tempdir");
let header = crate::core::hash::header(crate::core::hash::CommentStyle::Hash);
let rendered_raw = "[project]\nname=\"same\"\n";
let scratch = dir.path().join("scratch.toml");
std::fs::write(&scratch, format!("{header}\n{rendered_raw}")).unwrap();
crate::cli::pipeline::poly_format_strict(std::slice::from_ref(&scratch), dir.path())
.expect("poly fmt must succeed");
let converged = std::fs::read_to_string(&scratch).unwrap();
std::fs::write(dir.path().join("pyproject.toml"), &converged).unwrap();
std::fs::remove_file(&scratch).ok();
let files = vec![gen_file("pyproject.toml", rendered_raw)];
let (drifted, stats) = drifted_managed_paths(&files, dir.path(), &drift_test_config());
assert!(
drifted.is_empty(),
"an already up-to-date TOML file must not be reported drifted: {drifted:?}"
);
assert_eq!(stats.compared, 1);
}
#[test]
fn drifted_managed_paths_reports_a_self_marking_file_whose_body_no_longer_matches() {
let dir = tempfile::tempdir().expect("tempdir");
let header = crate::core::hash::header(crate::core::hash::CommentStyle::DoubleSlash);
std::fs::write(
dir.path().join("reference.md"),
format!("{header}Describes `greet`, which says hi.\n"),
)
.unwrap();
let files = vec![gen_file_unheadered(
"reference.md",
&format!("{header}Describes `greet`, which says hi warmly.\n"),
)];
let (drifted, _stats) = drifted_managed_paths(&files, dir.path(), &drift_test_config());
assert_eq!(
drifted,
vec![dir.path().join("reference.md").display().to_string()],
"a self-marking file (marker baked into content, generated_header: false) whose body \
changed must still be reported drifted"
);
}
#[test]
fn drifted_managed_paths_is_silent_once_a_self_marking_file_matches() {
let dir = tempfile::tempdir().expect("tempdir");
let header = crate::core::hash::header(crate::core::hash::CommentStyle::DoubleSlash);
let content = format!("{header}Describes `greet`, which says hi warmly.\n");
std::fs::write(dir.path().join("reference.md"), &content).unwrap();
let files = vec![gen_file_unheadered("reference.md", &content)];
let (drifted, _stats) = drifted_managed_paths(&files, dir.path(), &drift_test_config());
assert!(drifted.is_empty());
}
fn e2e_override_config() -> crate::core::config::ResolvedCrateConfig {
let cfg: crate::core::config::NewAlefConfig = toml::from_str(
r#"
[workspace]
languages = ["rust"]
[[crates]]
name = "sample"
sources = ["src/lib.rs"]
[crates.e2e]
fixtures = "fixtures"
output = "e2e"
[crates.e2e.call]
function = "run"
[crates.e2e.format]
dart = "cd {dir} && dart format ."
"#,
)
.expect("valid config");
cfg.resolve().expect("resolvable").remove(0)
}
#[test]
fn a_poly_excluded_path_is_silent_while_a_genuinely_drifted_one_is_still_reported() {
let Some(_poly) = crate::test_support::tool_available_with_stable_path("poly") else {
return;
};
let dir = tempfile::tempdir().expect("tempdir");
std::fs::write(
dir.path().join("poly.toml"),
"[discovery]\nexclude = [\"**/pyproject.toml\"]\n",
)
.unwrap();
let excluded_render = "[project]\nname=\"same\"\n";
let excluded = gen_file("pyproject.toml", excluded_render);
let rendered = crate::cli::commands::adopt::managed_outputs(std::slice::from_ref(&excluded), dir.path());
std::fs::write(dir.path().join("pyproject.toml"), &rendered[0].content).unwrap();
let header = crate::core::hash::header(crate::core::hash::CommentStyle::DoubleSlash);
std::fs::write(
dir.path().join("lib.rs"),
format!("{header}\npub fn greet() -> &'static str {{\n \"old\"\n}}\n"),
)
.unwrap();
let drifted_rs = gen_file("lib.rs", "pub fn greet() -> &'static str {\n \"new\"\n}\n");
let files = vec![excluded, drifted_rs];
let (drifted, stats) = drifted_managed_paths(&files, dir.path(), &drift_test_config());
assert_eq!(
drifted,
vec![dir.path().join("lib.rs").display().to_string()],
"exactly the genuinely-drifted file must be reported: the poly-excluded one is not \
reformatted by anything alef runs, so its raw render IS its disk content"
);
assert_eq!(
stats.matched_render_exactly, 1,
"only the excluded file MATCHED the render; the drifted one was settled against the render \
too but did not match, which is why it is in `drifted` above and not counted here"
);
assert_eq!(
stats.compared, 1,
"only the Rust path poly owns may be staged; no temp copy may be staged for the excluded \
TOML path poly never reaches"
);
}
#[test]
fn a_file_owned_by_an_e2e_format_override_is_counted_rather_than_judged() {
let dir = tempfile::tempdir().expect("tempdir");
std::fs::create_dir_all(dir.path().join("e2e/dart/test")).unwrap();
let header = crate::core::hash::header(crate::core::hash::CommentStyle::DoubleSlash);
std::fs::write(
dir.path().join("e2e/dart/test/smoke_test.dart"),
format!("{header}\nvoid main() {{}}\n"),
)
.unwrap();
let files = vec![gen_file(
"e2e/dart/test/smoke_test.dart",
"void main() { print('x'); }\n",
)];
let (drifted, stats) = drifted_managed_paths(&files, dir.path(), &e2e_override_config());
assert!(
drifted.is_empty(),
"alef cannot predict what `dart format` leaves on disk, so it must not claim drift: {drifted:?}"
);
assert_eq!(
stats.skipped_no_faithful_prediction, 1,
"the path must be COUNTED as unexaminable, not dropped"
);
assert_eq!(stats.compared, 0);
assert_eq!(stats.matched_render_exactly, 0);
}