use super::*;
use crate::core::config::Language;
fn resolved_test_config() -> crate::core::config::ResolvedCrateConfig {
let cfg: crate::core::config::NewAlefConfig = toml::from_str(
r#"
[workspace]
languages = ["python"]
[[crates]]
name = "test-lib"
sources = ["src/lib.rs"]
[crates.test.python]
command = "pytest"
[crates.test.rust]
e2e = "cargo test"
"#,
)
.unwrap();
cfg.resolve().unwrap().remove(0)
}
fn scanned_names(names: &[&str]) -> Vec<String> {
let directory = tempfile::tempdir().expect("temporary project");
for name in names {
let path = directory.path().join(name);
let marker = crate::core::hash::header(crate::core::hash::CommentStyle::Hash);
std::fs::write(&path, format!("{marker}\nseeded = true\n")).expect("seed stamped file");
}
let mut found: Vec<String> = collect_alef_hashes(directory.path())
.into_iter()
.filter_map(|(path, _, _)| path.file_name()?.to_str().map(str::to_owned))
.collect();
found.sort();
found
}
fn scanned_relative_paths(relative_paths: &[&str]) -> Vec<String> {
let directory = tempfile::tempdir().expect("temporary project");
for relative in relative_paths {
let path = directory.path().join(relative);
std::fs::create_dir_all(path.parent().expect("seeded path has a parent")).expect("seed parent directory");
let marker = crate::core::hash::header(crate::core::hash::CommentStyle::Hash);
std::fs::write(&path, format!("{marker}\nseeded = true\n")).expect("seed stamped file");
}
let mut found: Vec<String> = collect_alef_hashes(directory.path())
.into_iter()
.filter_map(|(path, _, _)| {
path.strip_prefix(directory.path())
.ok()?
.to_str()
.map(|value| value.replace('\\', "/"))
})
.collect();
found.sort();
found
}
#[test]
fn the_ownership_walk_reaches_the_dot_directories_alef_stamps() {
let found = scanned_relative_paths(&[
"packages/reachable.toml",
".cargo/config.toml",
".github/skills/api/SKILL.md",
".venv/lib/cached.toml",
]);
assert!(
found.contains(&"packages/reachable.toml".to_string()),
"control: a stamped file outside every dot-directory must be found, else this test \
proves nothing about the dot-directory cases; walk returned {found:?}"
);
assert!(
found.contains(&".cargo/config.toml".to_string()),
"alef writes and stamps `.cargo/config.toml` itself; a stamp nothing ever reads back \
is not a freshness check. Walk returned {found:?}"
);
assert!(
found.contains(&".github/skills/api/SKILL.md".to_string()),
"generated agent skills are stamped alef output and must be verifiable; walk returned \
{found:?}"
);
assert!(
!found.contains(&".venv/lib/cached.toml".to_string()),
"the dot-directory prune must still keep the walk out of tool caches -- the fix is an \
allowlist of the dot-directories alef writes into, not a removal of the prune. Walk \
returned {found:?}"
);
}
#[test]
fn the_ownership_walk_does_not_descend_into_a_nested_worktree() {
let found = scanned_relative_paths(&[".claude/skills/api/SKILL.md", ".claude/worktrees/other/config.toml"]);
assert!(
found.contains(&".claude/skills/api/SKILL.md".to_string()),
"control: `.claude` must be walked, else the exclusion below is vacuous; walk \
returned {found:?}"
);
assert!(
!found.contains(&".claude/worktrees/other/config.toml".to_string()),
"a nested worktree is a different checkout of this repository; its stamps are not \
this tree's. Walk returned {found:?}"
);
}
#[test]
fn both_consumers_build_their_managed_set_only_from_the_shared_surface() {
let stage_calls = [
"pipeline::generate(",
"pipeline::generate_stubs(",
"pipeline::generate_service_api(",
"pipeline::generate_public_api(",
"pipeline::scaffold(",
"pipeline::readme(",
"e2e::generate_e2e(",
"e2e::generate_e2e_with_log(",
"docs::generate_docs_stage(",
];
let regions = [
(
"alef verify's frozen report",
include_str!("../helpers.rs")
.split("pub(crate) fn collect_managed_surface")
.next()
.expect("helpers splits on the shared collector"),
),
("alef adopt's candidate set", include_str!("../adopt_command.rs")),
];
for (name, region) in regions {
for call in stage_calls {
assert!(
!region.contains(call),
"{name} calls {call} directly -- the frozen report and the candidate set \
must not enumerate generation stages separately, or they disagree again"
);
}
assert!(
region.contains("collect_managed_surface("),
"{name} must derive its managed set from the shared surface"
);
}
}
#[test]
fn ownership_walk_opens_every_extension_the_emit_side_stamps() {
assert_eq!(
scanned_names(&[
"foo-config.cmake",
"app.csproj",
"gem.gemspec",
"build.zig.zon",
"pom.xml"
]),
vec![
"app.csproj",
"build.zig.zon",
"foo-config.cmake",
"gem.gemspec",
"pom.xml"
],
);
}
#[test]
fn ownership_walk_opens_the_filename_keyed_files_the_emit_side_stamps() {
assert_eq!(scanned_names(&["makefile"]), vec!["makefile"]);
assert_eq!(
scanned_names(&[
"Makefile",
"GNUmakefile",
"Rakefile",
"Makevars",
"Makevars.in",
"Makevars.win.in",
"go.mod"
]),
vec![
"GNUmakefile",
"Makefile",
"Makevars",
"Makevars.in",
"Makevars.win.in",
"Rakefile",
"go.mod"
],
);
}
#[test]
fn ownership_walk_still_skips_an_extension_alef_never_stamps() {
assert!(scanned_names(&["notes.rtf", "archive.tar"]).is_empty());
}
#[test]
fn default_log_level_maps_verbosity_to_levels() {
assert_eq!(default_log_level(0, false), "info");
assert_eq!(default_log_level(1, false), "debug");
assert_eq!(default_log_level(2, false), "trace");
assert_eq!(default_log_level(9, false), "trace");
assert_eq!(default_log_level(0, true), "error");
assert_eq!(default_log_level(3, true), "error");
}
#[test]
fn resolve_test_languages_allows_explicit_test_only_language() {
let config = resolved_test_config();
let langs = resolve_test_languages(&config, Some(&["rust".to_string()]), true).unwrap();
assert_eq!(langs, vec![Language::Rust]);
}
#[test]
fn resolve_test_languages_appends_e2e_only_languages() {
let config = resolved_test_config();
let langs = resolve_test_languages(&config, None, true).unwrap();
assert_eq!(langs, vec![Language::Python, Language::Rust]);
}
#[test]
fn resolve_test_languages_omits_e2e_only_languages_without_e2e() {
let config = resolved_test_config();
let langs = resolve_test_languages(&config, None, false).unwrap();
assert_eq!(langs, vec![Language::Python]);
}
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,
}
}
#[test]
fn generated_files_match_disk_true_when_bodies_match() {
let dir = tempfile::tempdir().unwrap();
std::fs::write(dir.path().join("binding.go"), "package x\n\nvar a = 1\n").unwrap();
let files = vec![gen_file("binding.go", "package x\n\nvar a = 1\n")];
assert!(generated_files_match_disk(&files, dir.path()));
}
#[test]
fn generated_files_match_disk_ignores_embedded_hash_line() {
let dir = tempfile::tempdir().unwrap();
let generated = "// This file is auto-generated by alef — DO NOT EDIT.\npackage x\n\nvar a = 1\n";
std::fs::write(
dir.path().join("binding.go"),
"// This file is auto-generated by alef — DO NOT EDIT.\n// alef:hash:deadbeef\npackage x\n\nvar a = 1\n",
)
.unwrap();
let files = vec![gen_file("binding.go", generated)];
assert!(generated_files_match_disk(&files, dir.path()));
}
#[test]
fn generated_files_match_disk_false_when_body_differs() {
let dir = tempfile::tempdir().unwrap();
std::fs::write(dir.path().join("binding.go"), "package x\n\nvar a = 1\n").unwrap();
let files = vec![gen_file("binding.go", "package x\n\nimport \"fmt\"\n\nvar a = 1\n")];
assert!(!generated_files_match_disk(&files, dir.path()));
}
#[test]
fn generated_files_match_disk_false_when_file_missing() {
let dir = tempfile::tempdir().unwrap();
let files = vec![gen_file("binding.go", "package x\n")];
assert!(!generated_files_match_disk(&files, dir.path()));
}
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,
}
}
#[test]
fn missing_managed_paths_reports_an_absent_headered_file() {
let dir = tempfile::tempdir().expect("tempdir");
let files = vec![gen_file("SomeType.java", "final class SomeType {}\n")];
let missing = missing_managed_paths(&files, dir.path());
assert_eq!(missing, vec![dir.path().join("SomeType.java").display().to_string()]);
}
#[test]
fn missing_managed_paths_reports_nothing_when_every_headered_file_exists() {
let dir = tempfile::tempdir().expect("tempdir");
std::fs::write(
dir.path().join("SomeType.java"),
"final class SomeType { /* stale */ }\n",
)
.unwrap();
let files = vec![gen_file("SomeType.java", "final class SomeType {}\n")];
assert!(missing_managed_paths(&files, dir.path()).is_empty());
}
#[test]
fn missing_managed_paths_ignores_an_absent_unheadered_scaffold_file() {
let dir = tempfile::tempdir().expect("tempdir");
let files = vec![gen_file_unheadered("Cargo.toml", "[package]\nname = \"demo\"\n")];
assert!(missing_managed_paths(&files, dir.path()).is_empty());
}
#[test]
fn marker_line_finds_the_line_carrying_the_provenance_marker() {
let header = crate::core::hash::header(crate::core::hash::CommentStyle::DoubleSlash);
assert_eq!(
marker_line(&header),
Some("// This file is auto-generated by alef — DO NOT EDIT.")
);
}
#[test]
fn marker_line_finds_nothing_in_content_without_a_marker() {
assert_eq!(marker_line("final class SomeType {}\n"), None);
}
#[test]
fn verify_walk_detects_an_edited_generated_file() {
let directory = tempfile::tempdir().expect("tempdir");
let path = directory.path().join("binding.rs");
let original = "// This file is auto-generated by alef — DO NOT EDIT.\nfn value() -> u8 { 1 }\n";
let hash = crate::core::hash::compute_file_hash(original);
let finalized = crate::core::hash::inject_hash_line(original, &hash);
std::fs::write(&path, finalized.replace("{ 1 }", "{ 2 }")).expect("edit generated file");
let stale = verify_walk(directory.path()).expect("verify generated files");
assert_eq!(stale.len(), 1);
assert_eq!(stale[0].path, path.display().to_string());
}
#[test]
fn verify_walk_detects_a_hand_edited_dependency_version_in_a_generated_manifest() {
let directory = tempfile::tempdir().expect("tempdir");
let path = directory.path().join("Cargo.toml");
let original = "# This file is auto-generated by alef — DO NOT EDIT.\n\
[dependencies]\n\
base64 = \"0.22\"\n";
let hash = crate::core::hash::compute_file_hash(original);
let finalized = crate::core::hash::inject_hash_line(original, &hash);
std::fs::write(&path, finalized.replace("0.22", "0.23")).expect("hand-edit generated manifest");
let stale = verify_walk(directory.path()).expect("verify generated files");
assert_eq!(
stale.len(),
1,
"a hand-edited dependency version must be reported stale"
);
assert_eq!(stale[0].path, path.display().to_string());
}
#[test]
fn verify_walk_detects_a_mixed_stamped_and_unstamped_generated_tree() {
let directory = tempfile::tempdir().expect("tempdir");
let path = directory.path().join("unstamped.rs");
std::fs::write(
&path,
"// This file is auto-generated by alef — DO NOT EDIT.\nfn generated() {}\n",
)
.expect("write generated file");
let stamped_path = directory.path().join("stamped.rs");
let stamped_body = "// This file is auto-generated by alef — DO NOT EDIT.\nfn stamped() {}\n";
let stamped_hash = crate::core::hash::compute_file_hash(stamped_body);
std::fs::write(
&stamped_path,
crate::core::hash::inject_hash_line(stamped_body, &stamped_hash),
)
.expect("write stamped generated file");
let stale = verify_walk(directory.path()).expect("verify generated files");
assert_eq!(stale.len(), 1);
assert_eq!(stale[0].path, path.display().to_string());
assert_eq!(stale[0].embedded, "<missing>");
}
fn write_stamped(dir: &std::path::Path, name: &str, key: &str, value: &str) -> std::path::PathBuf {
let path = dir.join(name);
let body = "// This file is auto-generated by alef — DO NOT EDIT.\nfn generated() {}\n";
let stamped = crate::core::hash::inject_stamp_line(body, key, value);
let hash = crate::core::hash::compute_file_hash(&stamped);
std::fs::write(&path, crate::core::hash::inject_hash_line(&stamped, &hash)).expect("write stamped file");
path
}
#[test]
fn write_stamped_produces_a_file_the_hash_walk_actually_collects() {
let dir = tempfile::tempdir().expect("tempdir");
write_stamped(dir.path(), "header.h", "handle-abi", "1");
let collected = collect_alef_hashes(dir.path());
assert_eq!(
collected.len(),
1,
"the stamped fixture must be visible to the hash walk"
);
assert_eq!(
crate::core::hash::extract_stamp(&collected[0].2, "handle-abi").as_deref(),
Some("1"),
"the stamp must survive alongside the hash line"
);
}
#[test]
fn find_stamp_disagreement_reports_two_distinct_values() {
let dir = tempfile::tempdir().expect("tempdir");
write_stamped(dir.path(), "header.h", "handle-abi", "1");
write_stamped(dir.path(), "binding.zig", "handle-abi", "2");
let disagreement =
find_stamp_disagreement(dir.path(), "handle-abi").expect("two distinct stamp values must be reported");
assert_eq!(disagreement.key, "handle-abi");
assert_eq!(disagreement.examples.len(), 2);
let values: Vec<&str> = disagreement.examples.iter().map(|(_, v)| v.as_str()).collect();
assert!(values.contains(&"1"));
assert!(values.contains(&"2"));
}
#[test]
fn find_stamp_disagreement_is_none_when_every_stamped_file_agrees() {
let dir = tempfile::tempdir().expect("tempdir");
write_stamped(dir.path(), "header.h", "handle-abi", "2");
write_stamped(dir.path(), "binding.zig", "handle-abi", "2");
assert!(find_stamp_disagreement(dir.path(), "handle-abi").is_none());
}
#[test]
fn find_stamp_disagreement_is_none_when_nothing_is_stamped() {
let dir = tempfile::tempdir().expect("tempdir");
std::fs::write(
dir.path().join("header.h"),
"// This file is auto-generated by alef — DO NOT EDIT.\nfn generated() {}\n",
)
.expect("write unstamped file");
assert!(find_stamp_disagreement(dir.path(), "handle-abi").is_none());
}
#[test]
fn find_stamp_disagreement_ignores_a_different_key() {
let dir = tempfile::tempdir().expect("tempdir");
write_stamped(dir.path(), "header.h", "some-other-marker", "1");
write_stamped(dir.path(), "binding.zig", "some-other-marker", "2");
assert!(find_stamp_disagreement(dir.path(), "handle-abi").is_none());
}
fn stage_failure(paths: &[&str]) -> StageFailure {
StageFailure {
stage: "e2e",
message: "56 e2e assertion(s) reference a field the availability oracle cannot resolve".to_owned(),
paths: paths.iter().map(std::path::PathBuf::from).collect(),
}
}
#[test]
fn a_stage_failure_confined_to_e2e_paths_does_not_affect_an_unrelated_ownership_target() {
let failure = stage_failure(&["e2e/python/test_smoke.py", "e2e/go/smoke_test.go"]);
assert!(!failure.affects_any(&["packages/dart/rust/Cargo.toml".to_owned()]));
assert!(!failure.affects_any(&["packages/**/*.gemspec".to_owned()]));
}
#[test]
fn a_stage_failure_that_rendered_the_requested_target_does_affect_it() {
let failure = stage_failure(&["e2e/python/test_smoke.py", "e2e/go/smoke_test.go"]);
assert!(failure.affects_any(&["e2e/python/test_smoke.py".to_owned()]));
assert!(failure.affects_any(&["e2e/python/*.py".to_owned()]));
assert!(failure.affects_any(&[
"packages/dart/rust/Cargo.toml".to_owned(),
"e2e/go/smoke_test.go".to_owned(),
]));
}
#[test]
fn a_stage_failure_never_affects_an_empty_target_list() {
let failure = stage_failure(&["e2e/python/test_smoke.py"]);
assert!(!failure.affects_any(&[]));
}
fn swift_only_config() -> crate::core::config::ResolvedCrateConfig {
let cfg: crate::core::config::NewAlefConfig = toml::from_str(
r#"
[workspace]
languages = ["swift"]
[[crates]]
name = "toolkit"
sources = ["src/lib.rs"]
"#,
)
.unwrap();
cfg.resolve().unwrap().remove(0)
}
#[test]
fn a_post_build_owned_path_not_produced_in_band_is_not_reported_as_an_orphan() {
let dir = tempfile::tempdir().expect("tempdir");
let config = swift_only_config();
let api = crate::core::ir::ApiSurface::default();
let config_path = dir.path().join("alef.toml");
let owned_path = dir
.path()
.join("packages/swift/Sources/RustBridge/SwiftBridgeCore.swift");
std::fs::create_dir_all(owned_path.parent().unwrap()).expect("create Sources/RustBridge");
let header = crate::core::hash::header(crate::core::hash::CommentStyle::DoubleSlash);
let marked = crate::core::hash::inject_hash_line(&header, &"0".repeat(64));
std::fs::write(&owned_path, marked).expect("write post-build-owned file");
let found = find_missing_and_frozen_generated_files(&[Language::Swift], &api, &config, &config_path, dir.path())
.expect("collect_managed_surface must succeed over a swift-only crate");
assert!(
found.managed_paths.contains(&owned_path),
"post-build-owned paths must be folded into the managed surface: {:?}",
found.managed_paths
);
let orphans = super::super::verify_orphans::find_orphaned_generated_files(dir.path(), &found.managed_paths);
assert!(
orphans.is_empty(),
"a path a post-build step owns unguarded must never be reported as an orphan just \
because `alef verify` cannot run that step itself: {orphans:?}"
);
}
#[test]
fn find_missing_and_frozen_generated_files_splits_out_a_gitignored_managed_path() {
let dir = tempfile::tempdir().expect("tempdir");
let config = swift_only_config();
let api = crate::core::ir::ApiSurface::default();
let config_path = dir.path().join("alef.toml");
let baseline = find_missing_and_frozen_generated_files(&[Language::Swift], &api, &config, &config_path, dir.path())
.expect("collect_managed_surface must succeed over a swift-only crate");
assert!(
!baseline.missing.is_empty(),
"fixture precondition: a fresh directory with no generated output must have at least one \
managed path outstanding, or this test cannot prove anything -- got: {:?}",
baseline.missing
);
assert!(
baseline.missing_gitignored.is_empty(),
"no .gitignore exists yet, so nothing should split out: {:?}",
baseline.missing_gitignored
);
let target = baseline.missing.first().cloned().expect("checked non-empty above");
let target_relative = std::path::Path::new(&target)
.strip_prefix(dir.path())
.expect("missing paths are joined onto base_dir")
.to_owned();
let git_init_status = crate::test_support::git_command(dir.path())
.args(["init", "--quiet"])
.status()
.expect("git init must run");
if !git_init_status.success() {
return;
}
let ignore_parent = dir
.path()
.join(target_relative.parent().unwrap_or_else(|| std::path::Path::new(".")));
std::fs::create_dir_all(&ignore_parent).expect("create the target's parent directory");
std::fs::write(
ignore_parent.join(".gitignore"),
format!(
"{}\n",
target_relative
.file_name()
.expect("a managed path has a file name")
.to_string_lossy()
),
)
.expect("write a .gitignore excluding exactly the chosen target");
let found = find_missing_and_frozen_generated_files(&[Language::Swift], &api, &config, &config_path, dir.path())
.expect("collect_managed_surface must succeed over a swift-only crate");
assert!(
found.missing_gitignored.contains(&target),
"the gitignored managed path must move to missing_gitignored, got: {:?}",
found.missing_gitignored
);
assert!(
!found.missing.contains(&target),
"the gitignored managed path must not remain in plain missing (its remedy differs -- \
`alef generate` cannot fix it), got: {:?}",
found.missing
);
}
fn registry_test_apps_workspace() -> (tempfile::TempDir, std::path::PathBuf) {
let dir = tempfile::tempdir().expect("tempdir");
std::fs::create_dir_all(dir.path().join("src")).expect("create src dir");
std::fs::create_dir_all(dir.path().join("fixtures")).expect("create fixtures dir");
std::fs::write(
dir.path().join("src/lib.rs"),
"pub fn greet(name: String) -> String { format!(\"hi {name}\") }\n",
)
.expect("write lib.rs");
std::fs::write(
dir.path().join("Cargo.toml"),
"[package]\nname = \"measurelib\"\nversion = \"0.1.0\"\nedition = \"2024\"\n",
)
.expect("write Cargo.toml");
std::fs::write(
dir.path().join("fixtures/greet_basic.json"),
r#"{
"id": "greet_basic",
"description": "greet",
"category": "smoke",
"tags": ["smoke"],
"call": "_default",
"input": { "name": "world" },
"assertions": [{ "type": "not_error" }]
}
"#,
)
.expect("write fixture json");
std::fs::write(dir.path().join(".gitignore"), "/test_apps/\n").expect("write .gitignore");
let git_init_status = crate::test_support::git_command(dir.path())
.args(["init", "--quiet"])
.status()
.expect("git init must run");
assert!(git_init_status.success(), "git init must succeed in this environment");
let config_path = dir.path().join("alef.toml");
(dir, config_path)
}
const REGISTRY_TEST_APPS_CALL_BLOCK: &str = r#"
[crates.e2e]
fixtures = "fixtures"
output = "e2e"
languages = ["python"]
[crates.e2e.call]
function = "greet"
module = "measurelib"
result_var = "result"
[[crates.e2e.call.args]]
name = "name"
field = "input.name"
type = "string"
"#;
#[test]
fn registry_test_apps_output_under_a_whole_directory_gitignore_is_a_hard_failure_without_ignore_ephemeral() {
let (dir, config_path) = registry_test_apps_workspace();
let config_toml = format!(
"[workspace]\nlanguages = [\"python\"]\n\n[[crates]]\nname = \"measurelib\"\nsources = [\"src/lib.rs\"]\n{REGISTRY_TEST_APPS_CALL_BLOCK}"
);
std::fs::write(&config_path, &config_toml).expect("write alef.toml");
let cfg: crate::core::config::NewAlefConfig = toml::from_str(&config_toml).expect("config parses");
let config = cfg.resolve().expect("config resolves").remove(0);
assert!(
config.verify.ignore_ephemeral.is_empty(),
"fixture precondition: no opt-out configured"
);
let api = crate::core::ir::ApiSurface::default();
let _cwd = crate::test_support::CwdGuard::enter(dir.path());
let found = find_missing_and_frozen_generated_files(&[Language::Python], &api, &config, &config_path, dir.path())
.expect("collect_managed_surface must succeed");
let test_apps_missing_gitignored = found
.missing_gitignored
.iter()
.filter(|path| path.contains("test_apps"))
.count();
assert!(
test_apps_missing_gitignored > 0,
"fixture precondition: registry-mode output must exist and land in missing_gitignored \
with no opt-out configured, got: {:?}",
found.missing_gitignored
);
}
#[test]
fn ignore_ephemeral_excludes_registry_test_apps_output_from_missing_and_missing_gitignored() {
let (dir, config_path) = registry_test_apps_workspace();
let config_toml = format!(
"[workspace]\nlanguages = [\"python\"]\n\n[[crates]]\nname = \"measurelib\"\nsources = [\"src/lib.rs\"]\n{REGISTRY_TEST_APPS_CALL_BLOCK}\n[crates.verify]\nignore_ephemeral = [\"test_apps/**\"]\n"
);
std::fs::write(&config_path, &config_toml).expect("write alef.toml");
let cfg: crate::core::config::NewAlefConfig = toml::from_str(&config_toml).expect("config parses");
let config = cfg.resolve().expect("config resolves").remove(0);
assert_eq!(config.verify.ignore_ephemeral, vec!["test_apps/**".to_string()]);
let api = crate::core::ir::ApiSurface::default();
let _cwd = crate::test_support::CwdGuard::enter(dir.path());
let found = find_missing_and_frozen_generated_files(&[Language::Python], &api, &config, &config_path, dir.path())
.expect("collect_managed_surface must succeed");
assert!(
found.missing_gitignored.iter().any(|path| path.contains("test_apps")),
"fixture precondition: registry-mode output must still be gitignored-missing BEFORE the \
opt-out is applied, got: {:?}",
found.missing_gitignored
);
let (missing, missing_excluded) = config.verify.partition_ephemeral(found.missing, dir.path());
let (missing_gitignored, gitignored_excluded) =
config.verify.partition_ephemeral(found.missing_gitignored, dir.path());
assert!(
!missing_gitignored.iter().any(|path| path.contains("test_apps")),
"ignore_ephemeral must remove every test_apps path from missing_gitignored: {missing_gitignored:?}"
);
assert!(
!missing.iter().any(|path| path.contains("test_apps")),
"ignore_ephemeral must remove every test_apps path from missing: {missing:?}"
);
assert!(
gitignored_excluded > 0,
"the exclusion must be counted, not just applied silently"
);
assert_eq!(
missing_excluded, 0,
"no plain-missing entries exist under test_apps in this fixture"
);
}