use std::path::{Path, PathBuf};
fn missing_path(tag: &str) -> PathBuf {
PathBuf::from(format!("/nonexistent-pmat-{tag}-9f3a/does/not/exist"))
}
fn assert_names_missing_path(err: &anyhow::Error, path: &Path) {
let msg = format!("{err:#}");
assert!(
msg.contains("Path not found"),
"expected the missing-path guard, got: {msg}"
);
assert!(
msg.contains(&path.display().to_string()),
"error must name the offending path, got: {msg}"
);
}
#[tokio::test]
async fn gh666_dag_rejects_missing_path() {
let path = missing_path("dag");
let err = super::complexity_handlers::handle_analyze_dag(
crate::cli::DagType::FullDependency,
path.clone(),
None,
None,
None,
false,
false,
false,
false,
false,
)
.await
.expect_err("a missing path must not produce a graph");
assert_names_missing_path(&err, &path);
}
#[tokio::test]
async fn gh666_defects_rejects_missing_path() {
use super::analyze_defects_handler::{handle_analyze_defects, OutputFormat};
let path = missing_path("defects-path");
let err = handle_analyze_defects(Some(&path), None, None, OutputFormat::Json)
.await
.expect_err("a missing path must not produce a defect report");
assert_names_missing_path(&err, &path);
}
#[tokio::test]
async fn gh664_defects_rejects_missing_file() {
use super::analyze_defects_handler::{handle_analyze_defects, OutputFormat};
let file = missing_path("defects-file").join("nope.rs");
let err = handle_analyze_defects(None, Some(&file), None, OutputFormat::Json)
.await
.expect_err("a missing --file must not be counted as scanned");
assert_names_missing_path(&err, &file);
}
#[tokio::test]
async fn gh666_provability_rejects_missing_path() {
use super::provability_handler::ProvabilityConfig;
let path = missing_path("provability");
let err = super::provability_handler::handle_analyze_provability(ProvabilityConfig {
project_path: path.clone(),
functions: vec![],
analysis_depth: 1,
format: crate::cli::ProvabilityOutputFormat::Json,
high_confidence_only: false,
include_evidence: false,
output: None,
top_files: 0,
})
.await
.expect_err("a missing path must not produce a provability report");
assert_names_missing_path(&err, &path);
}
#[tokio::test]
async fn gh666_defect_prediction_rejects_missing_path() {
use super::defect_prediction_handler::{
handle_analyze_defect_prediction, DefectPredictionConfig,
};
let path = missing_path("defect-prediction");
let err = handle_analyze_defect_prediction(DefectPredictionConfig {
project_path: path.clone(),
confidence_threshold: 0.5,
min_lines: 10,
include_low_confidence: false,
format: crate::cli::DefectPredictionOutputFormat::Json,
high_risk_only: false,
include_recommendations: false,
include: None,
exclude: None,
output: None,
perf: false,
top_files: 0,
})
.await
.expect_err("a missing path must not produce a defect prediction");
assert_names_missing_path(&err, &path);
}
#[tokio::test]
async fn gh663_comprehensive_rejects_missing_path() {
use super::comprehensive_analysis_handler::{
handle_analyze_comprehensive, ComprehensiveAnalysisConfig,
};
let path = missing_path("comprehensive");
let err = handle_analyze_comprehensive(ComprehensiveAnalysisConfig {
project_path: path.clone(),
file: None,
files: vec![],
format: crate::cli::ComprehensiveOutputFormat::Json,
include_duplicates: false,
include_dead_code: false,
include_defects: false,
include_complexity: true,
include_tdg: false,
confidence_threshold: 0.5,
min_lines: 10,
include: None,
exclude: None,
output: None,
perf: false,
executive_summary: false,
top_files: 10,
})
.await
.expect_err("a missing path must not score 100%");
assert_names_missing_path(&err, &path);
}
#[tokio::test]
async fn deep_context_rejects_missing_path() {
let path = missing_path("deep-context");
let err = super::advanced_analysis_handlers::handle_analyze_deep_context(
path.clone(),
None,
crate::cli::DeepContextOutputFormat::Json,
false,
vec![],
vec![],
30,
None,
None,
vec![],
vec![],
None,
false,
false,
10,
)
.await
.expect_err("a missing path must not produce a deep context report");
assert_names_missing_path(&err, &path);
}
#[tokio::test]
async fn proof_annotations_rejects_missing_path() {
let path = missing_path("proof-annotations");
let err = super::proof_annotations_handler::handle_analyze_proof_annotations(
path.clone(),
crate::cli::ProofAnnotationOutputFormat::Json,
false,
false,
None,
None,
None,
false,
false,
)
.await
.expect_err("a missing path must not produce proof annotations");
assert_names_missing_path(&err, &path);
}
#[tokio::test]
async fn assemblyscript_rejects_missing_path() {
let path = missing_path("assemblyscript");
let err = super::wasm_handlers::handle_analyze_assemblyscript(
path.clone(),
crate::cli::ComplexityOutputFormat::Json,
false,
false,
false,
None,
30,
false,
)
.await
.expect_err("a missing path must not produce an AssemblyScript report");
assert_names_missing_path(&err, &path);
}
#[tokio::test]
async fn webassembly_rejects_missing_path() {
let path = missing_path("webassembly");
let err = super::wasm_handlers::handle_analyze_webassembly(
path.clone(),
crate::cli::ComplexityOutputFormat::Json,
true,
true,
false,
false,
false,
None,
false,
)
.await
.expect_err("a missing path must not produce a WebAssembly report");
assert_names_missing_path(&err, &path);
}
#[tokio::test]
async fn gh681_entropy_rejects_missing_path() {
use crate::cli::handlers::analysis_handlers::route_analyze_command;
use crate::cli::AnalyzeCommands;
let path = missing_path("entropy");
let err = route_analyze_command(AnalyzeCommands::Entropy {
path: path.clone(),
project_path: None,
format: crate::cli::EntropyOutputFormat::Json,
output: None,
min_severity: crate::cli::EntropySeverity::Medium,
top_violations: 20,
file: None,
include_tests: false,
})
.await
.expect_err("a missing path must not produce entropy violations");
assert_names_missing_path(&err, &path);
}
#[tokio::test]
async fn gh681_entropy_rejects_missing_file() {
use crate::cli::handlers::analysis_handlers::route_analyze_command;
use crate::cli::AnalyzeCommands;
let file = missing_path("entropy-file").join("nope.rs");
let err = route_analyze_command(AnalyzeCommands::Entropy {
path: PathBuf::from("."),
project_path: None,
format: crate::cli::EntropyOutputFormat::Json,
output: None,
min_severity: crate::cli::EntropySeverity::Medium,
top_violations: 20,
file: Some(file.clone()),
include_tests: false,
})
.await
.expect_err("a missing --file must not produce entropy violations");
assert_names_missing_path(&err, &file);
}
#[test]
fn gh662_cuda_tdg_rejects_missing_path() {
let path = missing_path("cuda-tdg");
let err = crate::tdg::CudaSimdAnalyzer::new()
.analyze(&path)
.expect_err("a missing path must not be scored");
assert_names_missing_path(&err, &path);
}
#[tokio::test]
async fn gh662_cuda_tdg_reports_unmeasured_for_empty_directory() {
use crate::cli::commands::CudaTdgOutputFormat;
use crate::cli::handlers::cuda_tdg_handlers::{handle_cuda_tdg_command, CudaTdgCommandConfig};
let dir = tempfile::TempDir::new().expect("tempdir");
let report = dir.path().join("report.txt");
handle_cuda_tdg_command(CudaTdgCommandConfig {
path: dir.path().to_path_buf(),
command: None,
format: CudaTdgOutputFormat::Terminal,
min_score: 85.0,
fail_on_p0: false,
simd: true,
wgpu: true,
output: Some(report.clone()),
quiet: false,
})
.await
.expect("an empty directory is not an error, it is simply unmeasured");
let written = std::fs::read_to_string(&report).expect("report written");
assert!(
written.contains("not measured"),
"an unread tree must be reported as unmeasured, got: {written}"
);
assert!(
!written.contains("Gateway: PASSED"),
"a gateway cannot pass on zero measured files, got: {written}"
);
assert!(
!written.contains("/100"),
"no score may be synthesised from zero files, got: {written}"
);
}
#[cfg(unix)]
#[tokio::test]
async fn gh682_complexity_rejects_unreadable_directory() {
use std::os::unix::fs::PermissionsExt;
let parent = tempfile::TempDir::new().expect("tempdir");
let locked = parent.path().join("noread");
std::fs::create_dir(&locked).expect("create dir");
std::fs::write(locked.join("main.rs"), "fn main() { let _ = 1; }").expect("write");
std::fs::set_permissions(&locked, std::fs::Permissions::from_mode(0o000)).expect("chmod 000");
let permissions_bite = std::fs::read_dir(&locked).is_err();
let result = super::complexity_handlers::handle_analyze_complexity(
locked.clone(),
None,
vec![],
Some("rust".to_string()),
crate::cli::ComplexityOutputFormat::Json,
None,
None,
None,
vec![],
false,
0,
false,
30,
)
.await;
let _ = std::fs::set_permissions(&locked, std::fs::Permissions::from_mode(0o755));
if !permissions_bite {
return;
}
let err = result.expect_err("an unreadable directory must not be reported as clean");
let msg = format!("{err:#}");
assert!(
msg.contains("Path not readable"),
"expected the unreadable-path guard, got: {msg}"
);
assert!(
msg.contains("noread"),
"error must name the offending path, got: {msg}"
);
}