use crate::core::{Finding, Severity};
use super::UpgradeCheckArgs;
use anyhow::Result;
use colored::*;
pub fn run(args: &UpgradeCheckArgs) -> Result<()> {
eprintln!("{}", "⬆️ Forge Guard — Upgrade Check".bold());
let mut findings: Vec<Finding> = Vec::new();
if args.storage_collision {
eprintln!("\n🔍 Checking for storage collisions...");
findings.extend(check_storage_collisions(args)?);
}
if args.uups {
eprintln!("\n🔍 Checking UUPS upgrade path...");
findings.extend(check_uups(args)?);
}
eprintln!("\n🔍 Analyzing proxy configuration...");
findings.extend(check_proxy_config(args)?);
if findings.is_empty() {
println!("\n{} No upgrade issues found!", "✅".green().bold());
} else {
println!(
"\n{} {} upgrade-related issues found:",
"⚠️".yellow().bold(),
findings.len()
);
for f in &findings {
let color = if f.severity >= Severity::High {
"red"
} else {
"yellow"
};
println!(
"\n [{}] {}",
f.severity.to_string().color(color).bold(),
f.title.bold()
);
println!(" {}", f.description);
println!(" 💡 {}", f.recommendation);
}
}
if args.shared.json {
println!("{}", serde_json::to_string_pretty(&findings)?);
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_upgrade_check_arg_defaults() {
let args = UpgradeCheckArgs {
shared: super::super::SharedFlags {
chain: "ethereum".into(),
project: std::path::PathBuf::from("."),
json: false,
markdown: false,
html: false,
strict: false,
offline: false,
production: false,
report: false,
parallelism: 4,
},
proxy: None,
implementation: None,
all: false,
storage_collision: false,
uups: false,
};
assert!(args.proxy.is_none());
assert!(args.implementation.is_none());
assert!(!args.all);
assert!(!args.storage_collision);
assert!(!args.uups);
}
#[test]
fn test_upgrade_check_with_addresses() {
let args = UpgradeCheckArgs {
shared: super::super::SharedFlags::default(),
proxy: Some("0xproxy".into()),
implementation: Some("0ximpl".into()),
all: true,
storage_collision: true,
uups: true,
};
assert_eq!(args.proxy.as_deref(), Some("0xproxy"));
assert_eq!(args.implementation.as_deref(), Some("0ximpl"));
assert!(args.all);
assert!(args.storage_collision);
assert!(args.uups);
}
#[test]
fn test_check_storage_collisions_returns_empty() {
let args = UpgradeCheckArgs {
shared: super::super::SharedFlags::default(),
proxy: None,
implementation: None,
all: false,
storage_collision: false,
uups: false,
};
let findings = check_storage_collisions(&args).unwrap();
assert!(findings.is_empty());
}
#[test]
fn test_check_uups_returns_empty() {
let args = UpgradeCheckArgs {
shared: super::super::SharedFlags::default(),
proxy: None,
implementation: None,
all: false,
storage_collision: false,
uups: false,
};
let findings = check_uups(&args).unwrap();
assert!(findings.is_empty());
}
#[test]
fn test_check_proxy_config_returns_empty() {
let args = UpgradeCheckArgs {
shared: super::super::SharedFlags::default(),
proxy: None,
implementation: None,
all: false,
storage_collision: false,
uups: false,
};
let findings = check_proxy_config(&args).unwrap();
assert!(findings.is_empty());
}
#[test]
fn test_severity_comparison_in_upgrade_check() {
assert!(Severity::High >= Severity::High);
assert!(Severity::Critical >= Severity::High);
assert!(!(Severity::Medium >= Severity::High));
assert!(!(Severity::Low >= Severity::High));
}
}
fn check_storage_collisions(_args: &UpgradeCheckArgs) -> Result<Vec<Finding>> {
Ok(Vec::new())
}
fn check_uups(_args: &UpgradeCheckArgs) -> Result<Vec<Finding>> {
Ok(Vec::new())
}
fn check_proxy_config(_args: &UpgradeCheckArgs) -> Result<Vec<Finding>> {
Ok(Vec::new())
}