use hotspots_core::analysis::analyze_file_with_config;
use hotspots_core::delta::{Delta, FunctionDeltaEntry, FunctionStatus};
use hotspots_core::policy::{evaluate_policies, PolicyId, PolicySeverity};
use hotspots_core::risk::RiskBand;
use hotspots_core::snapshot::Snapshot;
use hotspots_core::{git::GitContext, AnalysisOptions, ResolvedConfig};
use std::path::Path;
use swc_common::{sync::Lrc, SourceMap};
use tempfile::Builder;
fn analyze_source(source: &str, suffix: &str) -> Vec<hotspots_core::report::FunctionRiskReport> {
let file = Builder::new()
.suffix(suffix)
.tempfile()
.expect("create temp file");
std::fs::write(file.path(), source).expect("write temp source");
let cm: Lrc<SourceMap> = Default::default();
let options = AnalysisOptions {
min_lrs: None,
top_n: None,
};
analyze_file_with_config(file.path(), &cm, 0, &options, None, None, None)
.expect("analysis should succeed")
}
#[test]
fn test_suppression_comment_extraction() {
let source = r#"
// hotspots-ignore: legacy code
function suppressed() {
if (true) {
return 1;
}
return 0;
}
function notSuppressed() {
if (true) {
return 1;
}
return 0;
}
"#;
let functions = analyze_source(source, ".ts");
assert_eq!(functions.len(), 2);
assert_eq!(
functions[0].suppression_reason,
Some("legacy code".to_string())
);
assert_eq!(functions[1].suppression_reason, None);
}
#[test]
fn test_suppression_without_reason() {
let source = r#"
// hotspots-ignore:
function suppressed() {
return 42;
}
"#;
let functions = analyze_source(source, ".ts");
assert_eq!(functions.len(), 1);
assert_eq!(functions[0].suppression_reason, Some(String::new()));
}
#[test]
fn test_suppression_blank_line_ignored() {
let source = r#"
// hotspots-ignore: should not work
function notSuppressed() {
return 42;
}
"#;
let functions = analyze_source(source, ".ts");
assert_eq!(functions.len(), 1);
assert_eq!(functions[0].suppression_reason, None);
}
#[test]
fn test_suppression_works_for_python() {
let source = "# hotspots-ignore: legacy code\ndef suppressed():\n if True:\n return 1\n return 0\n\n\ndef not_suppressed():\n if True:\n return 1\n return 0\n";
let functions = analyze_source(source, ".py");
assert_eq!(functions.len(), 2);
assert_eq!(
functions[0].suppression_reason,
Some("legacy code".to_string())
);
assert_eq!(functions[1].suppression_reason, None);
}
#[test]
fn test_suppression_works_for_go() {
let source = "// hotspots-ignore: legacy code\nfunc Suppressed() int {\n\tif true {\n\t\treturn 1\n\t}\n\treturn 0\n}\n\nfunc NotSuppressed() int {\n\tif true {\n\t\treturn 1\n\t}\n\treturn 0\n}\n";
let functions = analyze_source(source, ".go");
assert_eq!(functions.len(), 2);
assert_eq!(
functions[0].suppression_reason,
Some("legacy code".to_string())
);
assert_eq!(functions[1].suppression_reason, None);
}
#[test]
fn test_suppression_missing_reason_policy() {
use hotspots_core::delta::FunctionState;
use hotspots_core::report::MetricsReport;
let delta_entry = FunctionDeltaEntry {
function_id: "test.ts::foo".to_string(),
status: FunctionStatus::New,
before: None,
after: Some(FunctionState {
metrics: MetricsReport {
cc: 1,
nd: 0,
fo: 0,
ns: 0,
loc: 10,
},
lrs: 1.0,
band: RiskBand::Low,
}),
delta: None,
band_transition: None,
suppression_reason: Some(String::new()), rename_hint: None,
};
let delta = Delta {
schema_version: 1,
commit: hotspots_core::delta::DeltaCommitInfo {
sha: "abc123".to_string(),
parent: "parent123".to_string(),
},
baseline: false,
deltas: vec![delta_entry],
policy: None,
aggregates: None,
};
let git_context = GitContext {
head_sha: "abc123".to_string(),
parent_shas: vec!["parent123".to_string()],
timestamp: 1705600000,
branch: Some("main".to_string()),
is_detached: false,
message: Some("test commit".to_string()),
author: Some("Test Author".to_string()),
is_fix_commit: Some(false),
is_revert_commit: Some(false),
ticket_ids: vec![],
};
let snapshot = Snapshot::new(git_context, vec![]);
let config = ResolvedConfig::defaults().unwrap();
let results = evaluate_policies(&delta, &snapshot, Path::new("."), &config)
.unwrap()
.unwrap();
assert_eq!(results.warnings.len(), 1);
assert_eq!(results.warnings[0].id, PolicyId::SuppressionMissingReason);
assert_eq!(results.warnings[0].severity, PolicySeverity::Warning);
assert!(results.warnings[0]
.message
.contains("suppressed without reason"));
}
#[test]
fn test_suppressed_function_excluded_from_critical_introduction() {
use hotspots_core::delta::FunctionState;
use hotspots_core::report::MetricsReport;
let critical_entry = FunctionDeltaEntry {
function_id: "test.ts::critical".to_string(),
status: FunctionStatus::New,
before: None,
after: Some(FunctionState {
metrics: MetricsReport {
cc: 20,
nd: 10,
fo: 5,
ns: 3,
loc: 50,
},
lrs: 50.0,
band: RiskBand::Critical,
}),
delta: None,
band_transition: None,
suppression_reason: Some("legacy code, will refactor".to_string()), rename_hint: None,
};
let delta = Delta {
schema_version: 1,
commit: hotspots_core::delta::DeltaCommitInfo {
sha: "abc123".to_string(),
parent: "parent123".to_string(),
},
baseline: false,
deltas: vec![critical_entry],
policy: None,
aggregates: None,
};
let git_context = GitContext {
head_sha: "abc123".to_string(),
parent_shas: vec!["parent123".to_string()],
timestamp: 1705600000,
branch: Some("main".to_string()),
is_detached: false,
message: Some("test commit".to_string()),
author: Some("Test Author".to_string()),
is_fix_commit: Some(false),
is_revert_commit: Some(false),
ticket_ids: vec![],
};
let snapshot = Snapshot::new(git_context, vec![]);
let config = ResolvedConfig::defaults().unwrap();
let results = evaluate_policies(&delta, &snapshot, Path::new("."), &config)
.unwrap()
.unwrap();
assert_eq!(results.failed.len(), 0);
assert_eq!(results.warnings.len(), 0); }
#[test]
fn test_unsuppressed_function_triggers_critical_introduction() {
use hotspots_core::delta::FunctionState;
use hotspots_core::report::MetricsReport;
let critical_entry = FunctionDeltaEntry {
function_id: "test.ts::critical".to_string(),
status: FunctionStatus::New,
before: None,
after: Some(FunctionState {
metrics: MetricsReport {
cc: 20,
nd: 10,
fo: 5,
ns: 3,
loc: 50,
},
lrs: 50.0,
band: RiskBand::Critical,
}),
delta: None,
band_transition: None,
suppression_reason: None, rename_hint: None,
};
let delta = Delta {
schema_version: 1,
commit: hotspots_core::delta::DeltaCommitInfo {
sha: "abc123".to_string(),
parent: "parent123".to_string(),
},
baseline: false,
deltas: vec![critical_entry],
policy: None,
aggregates: None,
};
let git_context = GitContext {
head_sha: "abc123".to_string(),
parent_shas: vec!["parent123".to_string()],
timestamp: 1705600000,
branch: Some("main".to_string()),
is_detached: false,
message: Some("test commit".to_string()),
author: Some("Test Author".to_string()),
is_fix_commit: Some(false),
is_revert_commit: Some(false),
ticket_ids: vec![],
};
let snapshot = Snapshot::new(git_context, vec![]);
let config = ResolvedConfig::defaults().unwrap();
let results = evaluate_policies(&delta, &snapshot, Path::new("."), &config)
.unwrap()
.unwrap();
assert_eq!(results.failed.len(), 1);
assert_eq!(results.failed[0].id, PolicyId::CriticalIntroduction);
assert_eq!(results.failed[0].severity, PolicySeverity::Blocking);
}