use std::path::Path;
use crate::baseline::{self as generic, BaselineConfig, Fingerprintable};
use super::config::ConfigIssue;
use super::CleanupResult;
const BASELINE_KEY: &str = "cleanup";
struct CleanupFinding<'a>(&'a ConfigIssue);
impl Fingerprintable for CleanupFinding<'_> {
fn fingerprint(&self) -> String {
format!("{}::{}", self.0.category, self.0.message)
}
fn description(&self) -> String {
self.0.message.clone()
}
fn context_label(&self) -> String {
self.0.category.clone()
}
}
pub type CleanupBaseline = generic::Baseline<()>;
pub type BaselineComparison = generic::Comparison;
pub fn save_baseline(
source_path: &Path,
result: &CleanupResult,
) -> crate::error::Result<std::path::PathBuf> {
let config = BaselineConfig::new(source_path, BASELINE_KEY);
let items: Vec<CleanupFinding> = result.config_issues.iter().map(CleanupFinding).collect();
generic::save(&config, &result.component_id, &items, ())
}
pub fn load_baseline(source_path: &Path) -> Option<CleanupBaseline> {
let config = BaselineConfig::new(source_path, BASELINE_KEY);
generic::load::<()>(&config).ok().flatten()
}
pub fn compare(result: &CleanupResult, baseline: &CleanupBaseline) -> BaselineComparison {
let items: Vec<CleanupFinding> = result.config_issues.iter().map(CleanupFinding).collect();
generic::compare(&items, baseline)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::cleanup::config::IssueSeverity;
use crate::cleanup::CleanupSummary;
fn make_issue(category: &str, message: &str) -> ConfigIssue {
ConfigIssue {
severity: IssueSeverity::Warning,
category: category.to_string(),
message: message.to_string(),
fix_hint: None,
}
}
fn make_result(issues: Vec<ConfigIssue>) -> CleanupResult {
CleanupResult {
component_id: "test".to_string(),
summary: CleanupSummary {
config_issues: issues.len(),
},
config_issues: issues,
hints: vec![],
}
}
#[test]
fn save_and_load_roundtrips() {
let dir = tempfile::tempdir().unwrap();
let result = make_result(vec![
make_issue("local_path", "local_path does not exist: /old/path"),
make_issue("extensions", "Extension 'old-ext' could not be loaded"),
]);
save_baseline(dir.path(), &result).unwrap();
let loaded = load_baseline(dir.path()).unwrap();
assert_eq!(loaded.context_id, "test");
assert_eq!(loaded.item_count, 2);
}
#[test]
fn compare_detects_new_issue() {
let dir = tempfile::tempdir().unwrap();
let original = make_result(vec![make_issue("local_path", "local_path does not exist")]);
save_baseline(dir.path(), &original).unwrap();
let baseline = load_baseline(dir.path()).unwrap();
let current = make_result(vec![
make_issue("local_path", "local_path does not exist"),
make_issue("extensions", "Extension 'bad' could not be loaded"),
]);
let comparison = compare(¤t, &baseline);
assert!(comparison.drift_increased);
assert_eq!(comparison.new_items.len(), 1);
}
#[test]
fn compare_detects_resolved_issue() {
let dir = tempfile::tempdir().unwrap();
let original = make_result(vec![
make_issue("local_path", "local_path does not exist"),
make_issue("extensions", "Extension 'old' could not be loaded"),
]);
save_baseline(dir.path(), &original).unwrap();
let baseline = load_baseline(dir.path()).unwrap();
let current = make_result(vec![make_issue("local_path", "local_path does not exist")]);
let comparison = compare(¤t, &baseline);
assert!(!comparison.drift_increased);
assert_eq!(comparison.resolved_fingerprints.len(), 1);
}
}