use pysentry::DependencyScanner;
use std::collections::HashSet;
use std::path::Path;
const FIXTURE_DIR: &str = concat!(
env!("CARGO_MANIFEST_DIR"),
"/tests/fixtures/per-group-scope"
);
#[allow(clippy::expect_used)]
async fn scan(
include_dev: bool,
include_optional: bool,
direct_only: bool,
groups: Option<HashSet<String>>,
) -> (HashSet<String>, HashSet<String>) {
let scanner = DependencyScanner::new(include_dev, include_optional, direct_only, None, groups);
let (deps, _, _) = scanner
.scan_project(Path::new(FIXTURE_DIR))
.await
.expect("scan_project must succeed for per-group fixture");
let direct: HashSet<String> = deps
.iter()
.filter(|d| d.is_direct)
.map(|d| d.name.to_string())
.collect();
let all: HashSet<String> = deps.iter().map(|d| d.name.to_string()).collect();
(direct, all)
}
#[tokio::test]
async fn test_no_group_flag_reports_all() {
let (direct, _all) = scan(true, true, false, None).await;
assert!(direct.contains("requests"), "requests must be direct");
assert!(direct.contains("httpx"), "httpx must be direct");
assert!(direct.contains("pytest"), "pytest must be direct");
}
#[tokio::test]
async fn test_group_prod_closure_includes_transitive_excludes_dev() {
let groups = Some(["prod".to_string()].into());
let (direct, all) = scan(true, true, false, groups).await;
assert!(all.contains("requests"), "requests must be included");
assert!(
all.contains("urllib3"),
"urllib3 (transitive of requests) must be included"
);
assert!(
all.contains("charset-normalizer"),
"charset-normalizer (transitive of requests) must be included"
);
assert!(all.contains("httpx"), "httpx must be included");
assert!(
all.contains("anyio"),
"anyio (transitive of httpx) must be included"
);
assert!(
all.contains("httpcore"),
"httpcore (transitive of httpx) must be included"
);
assert!(
all.contains("h11"),
"h11 (transitive of httpcore, 2 levels deep) must be included"
);
assert!(
all.contains("certifi"),
"certifi (shared transitive) must be included"
);
assert!(
all.contains("idna"),
"idna (shared transitive) must be included"
);
assert!(!all.contains("pytest"), "pytest must be excluded");
assert!(
!all.contains("iniconfig"),
"iniconfig (pytest transitive) must be excluded"
);
assert!(
!all.contains("pluggy"),
"pluggy (pytest transitive) must be excluded"
);
assert!(
direct.contains("requests"),
"requests must be direct (seed)"
);
assert!(direct.contains("httpx"), "httpx must be direct (seed)");
assert!(
!direct.contains("urllib3"),
"urllib3 must not be direct (transitive)"
);
assert!(
!direct.contains("anyio"),
"anyio must not be direct (transitive)"
);
assert!(
!direct.contains("httpcore"),
"httpcore must not be direct (transitive)"
);
}
#[tokio::test]
async fn test_exclude_extra_regression() {
let (direct, all) = scan(false, false, false, None).await;
assert!(
direct.contains("requests"),
"requests (main dep) must be direct with --exclude-extra"
);
assert!(
!all.contains("httpx"),
"httpx must be absent with --exclude-extra"
);
assert!(
!all.contains("pytest"),
"pytest must be absent with --exclude-extra"
);
assert!(
all.contains("urllib3"),
"urllib3 (requests transitive, not shared) must be included"
);
assert!(
all.contains("charset-normalizer"),
"charset-normalizer (requests transitive, not shared) must be included"
);
assert!(
all.contains("certifi"),
"certifi (requests transitive, SHARED with httpx) must still be included"
);
assert!(
all.contains("idna"),
"idna (requests transitive, SHARED with httpx) must still be included"
);
}
#[test]
fn test_unknown_group_errors() {
assert_cmd::Command::cargo_bin("pysentry")
.expect("pysentry binary must be compiled")
.arg(FIXTURE_DIR)
.args(["--group", "unknown_group"])
.assert()
.failure()
.stderr(predicates::str::contains("available groups:"));
}