use super::*;
use crate::core::manifest::framework::framework_agent_categories;
use std::fs;
use tempfile::TempDir;
fn write(dir: &Path, rel: &str, body: &str) {
let path = dir.join(rel);
fs::create_dir_all(path.parent().unwrap()).unwrap();
fs::write(path, body).unwrap();
}
fn bundled_anchors() -> MarkerAnchors {
let categories = framework_agent_categories().expect("bundled manifest must be valid");
MarkerAnchors::from_categories(&categories)
}
fn nested(root: &Path) -> Vec<String> {
let budget = ProbeBudget::new();
nested_probe_roots(root, &bundled_anchors(), &budget, &[root.to_path_buf()])
.roots
.into_iter()
.map(|p| {
p.strip_prefix(root)
.unwrap()
.to_string_lossy()
.replace('\\', "/")
})
.collect()
}
#[test]
fn anchors_cover_every_bundled_marker() {
let categories = framework_agent_categories().expect("bundled manifest must be valid");
let anchors = MarkerAnchors::from_categories(&categories);
let declared = categories
.language
.iter()
.chain(categories.framework.iter())
.chain(categories.platform.iter());
for entry in declared {
for marker in &entry.markers {
let head = marker
.split("::")
.next()
.unwrap()
.split('/')
.next()
.unwrap()
.to_string();
assert!(
anchors.matches(&head),
"`{marker}` (declared by {}) must yield a matching anchor",
entry.stem
);
}
}
}
#[test]
fn anchor_globs_match_dotnet() {
let anchors = bundled_anchors();
assert!(anchors.matches("MyApp.csproj"));
assert!(anchors.matches("MyApp.sln"));
assert!(
!anchors.matches("MyApp.csproj.bak"),
"the glob is suffix-anchored, as `marker_present` is"
);
}
#[test]
fn nested_ui_package_is_found() {
let tmp = TempDir::new().unwrap();
write(tmp.path(), "Cargo.toml", "[workspace]\nmembers = []\n");
write(tmp.path(), "crates/app/Cargo.toml", "[package]\n");
write(tmp.path(), "crates/app/ui/package.json", "{}");
assert_eq!(nested(tmp.path()), vec!["crates/app", "crates/app/ui"]);
}
#[test]
fn depth_bound_stops_the_walk() {
let tmp = TempDir::new().unwrap();
write(tmp.path(), "a/b/c/d/package.json", "{}");
write(tmp.path(), "a/b/c/d/e/package.json", "{}");
let budget = ProbeBudget::new();
let probe = nested_probe_roots(
tmp.path(),
&bundled_anchors(),
&budget,
&[tmp.path().to_path_buf()],
);
assert!(
probe.roots.contains(&tmp.path().join("a/b/c/d")),
"depth 4 is walked"
);
assert!(
!probe.roots.contains(&tmp.path().join("a/b/c/d/e")),
"depth 5 is past the bound and must not be probed"
);
assert!(
probe.depth_limited,
"leaving a child of the deepest walked directory unread is the depth limit (#7781)"
);
assert!(
!probe.truncated,
"the depth bound is the declared scope, never a resource cap (#7781 round-3)"
);
}
#[test]
fn depth_limited_tree_is_not_truncated() {
let tmp = TempDir::new().unwrap();
write(tmp.path(), "a/package.json", "{}");
write(tmp.path(), "a/b/c/d/e/package.json", "{}");
let budget = ProbeBudget::new();
let probe = nested_probe_roots(
tmp.path(),
&bundled_anchors(),
&budget,
&[tmp.path().to_path_buf()],
);
assert_eq!(
probe.roots,
vec![tmp.path().join("a")],
"everything within the bound is still found"
);
assert!(probe.depth_limited, "the walk left depth-5 children unread");
assert!(
!probe.truncated,
"no resource cap was exhausted, so the fail-closed flag stays clear"
);
}
#[test]
fn skipped_directories_are_never_probed() {
let tmp = TempDir::new().unwrap();
for skipped in [
"node_modules",
"target",
"dist",
"build",
"vendor",
"testdata",
"test-data",
"fixtures",
".git",
] {
write(tmp.path(), &format!("{skipped}/pkg/package.json"), "{}");
}
assert!(
nested(tmp.path()).is_empty(),
"no skipped tree may contribute a probe root"
);
}
#[test]
fn gitignored_directory_is_skipped() {
let tmp = TempDir::new().unwrap();
write(tmp.path(), ".gitignore", "generated/\n");
write(tmp.path(), "generated/app/package.json", "{}");
write(tmp.path(), "kept/package.json", "{}");
assert_eq!(nested(tmp.path()), vec!["kept"]);
}
#[test]
fn gitignore_path_rules_are_ignored() {
let names = gitignore_dir_names(
"# comment\n\n!kept\n**/target/\n/out\nbuild-cache/\n**/out/\nsrc/generated\n*.log\n**/a/b/\n",
);
assert_eq!(
names,
vec![
"target".to_string(),
"out".to_string(),
"build-cache".to_string(),
"out".to_string(),
]
);
}
#[test]
fn walk_is_deterministic() {
let tmp = TempDir::new().unwrap();
for name in ["zeta", "alpha", "mid"] {
write(tmp.path(), &format!("{name}/package.json"), "{}");
}
let first = nested(tmp.path());
assert_eq!(first, vec!["alpha", "mid", "zeta"]);
assert_eq!(nested(tmp.path()), first, "repeat runs agree");
}
#[test]
fn missing_root_is_not_an_error() {
let tmp = TempDir::new().unwrap();
let missing = tmp.path().join("gone");
let budget = ProbeBudget::new();
let probe = nested_probe_roots(&missing, &bundled_anchors(), &budget, &[]);
assert!(probe.roots.is_empty());
assert!(
!probe.truncated,
"an empty tree is read in full, not truncated"
);
}
#[cfg(unix)]
#[test]
fn unreadable_subdirectory_is_skipped_not_an_error() {
use std::os::unix::fs::PermissionsExt;
let tmp = TempDir::new().unwrap();
write(tmp.path(), "denied/app/package.json", "{}");
write(tmp.path(), "readable/package.json", "{}");
let denied = tmp.path().join("denied");
let restore = fs::metadata(&denied).unwrap().permissions();
fs::set_permissions(&denied, fs::Permissions::from_mode(0o000)).unwrap();
let budget = ProbeBudget::new();
let probe = nested_probe_roots(
tmp.path(),
&bundled_anchors(),
&budget,
&[tmp.path().to_path_buf()],
);
fs::set_permissions(&denied, restore).unwrap();
let found: Vec<String> = probe
.roots
.iter()
.map(|p| p.strip_prefix(tmp.path()).unwrap().to_string_lossy().into())
.collect();
assert_eq!(
found,
vec!["readable".to_string()],
"an unreadable directory must cost only itself, never its siblings"
);
assert!(
!probe.truncated,
"a permission denial is absence, not a tripped scan bound"
);
}
#[cfg(unix)]
#[test]
fn directory_symlink_is_not_traversed() {
let outside = TempDir::new().unwrap();
write(outside.path(), "escaped/package.json", "{}");
let tmp = TempDir::new().unwrap();
write(tmp.path(), "real/package.json", "{}");
std::os::unix::fs::symlink(outside.path().join("escaped"), tmp.path().join("linked")).unwrap();
assert_eq!(
nested(tmp.path()),
vec!["real"],
"a directory symlink must contribute no probe root — the walk cannot leave the project"
);
}
#[test]
#[serial_test::serial]
fn scanned_dirs_bound_reports_truncation() {
use tracing_subscriber::layer::SubscriberExt;
crate::test_support::enable_event_capture();
const FANOUT: usize = 64;
const {
assert!(
1 + FANOUT + FANOUT * FANOUT > MAX_SCANNED_DIRS,
"the fixture below must exceed the bound it is pinning"
)
};
let tmp = TempDir::new().unwrap();
for outer in 0..FANOUT {
for inner in 0..FANOUT {
fs::create_dir_all(tmp.path().join(format!("d{outer}/d{inner}"))).unwrap();
}
}
let buffer = trusty_common::log_buffer::LogBuffer::new(64);
let subscriber = tracing_subscriber::registry().with(
trusty_common::log_buffer::LogBufferLayer::new(buffer.clone()),
);
let probe = tracing::subscriber::with_default(subscriber, || {
let budget = ProbeBudget::new();
nested_probe_roots(
tmp.path(),
&bundled_anchors(),
&budget,
&[tmp.path().to_path_buf()],
)
});
assert!(
probe.truncated,
"a walk stopped by MAX_SCANNED_DIRS is truncated"
);
assert!(
!probe.depth_limited,
"a two-tier fixture never reaches the depth bound (#7781 round-3)"
);
let lines = buffer.tail(64);
assert!(
lines.iter().any(|l| l.contains("MAX_SCANNED_DIRS")),
"the WARN must name the bound that tripped, or an operator cannot tell \
which ceiling to raise: {lines:#?}"
);
}
#[test]
#[serial_test::serial]
fn both_bounds_can_trip_together() {
use tracing_subscriber::layer::SubscriberExt;
crate::test_support::enable_event_capture();
let tmp = TempDir::new().unwrap();
let tier = tmp.path().join("a/b/c");
for i in 0..MAX_SCANNED_DIRS {
fs::create_dir_all(tier.join(format!("d{i:05}"))).unwrap();
}
fs::create_dir_all(tier.join("d00000/unread")).unwrap();
let buffer = trusty_common::log_buffer::LogBuffer::new(64);
let subscriber = tracing_subscriber::registry().with(
trusty_common::log_buffer::LogBufferLayer::new(buffer.clone()),
);
let probe = tracing::subscriber::with_default(subscriber, || {
let budget = ProbeBudget::new();
nested_probe_roots(
tmp.path(),
&bundled_anchors(),
&budget,
&[tmp.path().to_path_buf()],
)
});
assert!(
probe.depth_limited,
"a child left unread at the declared depth is the depth limit, whatever \
ended the walk afterwards"
);
assert!(
probe.truncated,
"the depth-4 tier is wider than MAX_SCANNED_DIRS, so the resource cap \
ended the walk"
);
let lines = buffer.tail(64);
assert!(
lines
.iter()
.any(|l| l.contains("WARN") && l.contains("MAX_SCANNED_DIRS")),
"the resource cap still names itself: {lines:#?}"
);
assert!(
lines
.iter()
.any(|l| l.contains("DEBUG") && l.contains("stopped at its declared depth")),
"the depth limit must be logged even when a resource cap ended the walk — \
the early returns used to skip it: {lines:#?}"
);
}
#[test]
#[serial_test::serial]
fn depth_limit_is_logged_at_debug() {
use tracing_subscriber::layer::SubscriberExt;
crate::test_support::enable_event_capture();
let tmp = TempDir::new().unwrap();
write(tmp.path(), "a/b/c/d/e/package.json", "{}");
let buffer = trusty_common::log_buffer::LogBuffer::new(64);
let subscriber = tracing_subscriber::registry().with(
trusty_common::log_buffer::LogBufferLayer::new(buffer.clone()),
);
let probe = tracing::subscriber::with_default(subscriber, || {
let budget = ProbeBudget::new();
nested_probe_roots(
tmp.path(),
&bundled_anchors(),
&budget,
&[tmp.path().to_path_buf()],
)
});
assert!(
probe.depth_limited,
"the fixture nests past the depth bound"
);
let lines = buffer.tail(64);
assert!(
lines
.iter()
.any(|l| l.contains("DEBUG") && l.contains("stopped at its declared depth")),
"the depth limit must reach the log, or only the flag records it: {lines:#?}"
);
assert!(
!lines.iter().any(|l| l.contains("WARN")),
"the declared depth is not a resource cap and must not warn: {lines:#?}"
);
}
#[test]
fn a_small_tree_is_not_truncated() {
let tmp = TempDir::new().unwrap();
write(tmp.path(), "pkg/package.json", "{}");
let budget = ProbeBudget::new();
let probe = nested_probe_roots(
tmp.path(),
&bundled_anchors(),
&budget,
&[tmp.path().to_path_buf()],
);
assert_eq!(probe.roots, vec![tmp.path().join("pkg")]);
assert!(
!probe.truncated,
"no resource cap is tripped by a two-directory tree"
);
assert!(
!probe.depth_limited,
"a two-directory tree is read to its own bottom, well inside the depth bound"
);
}
#[test]
fn member_cap_is_shared() {
let tmp = TempDir::new().unwrap();
write(tmp.path(), "pkg/package.json", "{}");
let already: Vec<PathBuf> = (0..=MAX_WORKSPACE_MEMBERS)
.map(|i| tmp.path().join(format!("filler{i}")))
.collect();
let budget = ProbeBudget::new();
let probe = nested_probe_roots(tmp.path(), &bundled_anchors(), &budget, &already);
assert!(
probe.roots.is_empty(),
"at the shared cap the walk contributes nothing"
);
assert!(
probe.truncated,
"the member cap is a RESOURCE cap: hitting it is truncation, not an empty tree (#7781)"
);
}