use super::FuzzArgs;
use anyhow::{Context, Result};
use colored::*;
pub fn run(args: &FuzzArgs) -> Result<()> {
eprintln!("{}", "🎯 Forge Guard — Fuzzing Campaign".bold());
eprintln!(" Runs: {}", args.runs);
eprintln!(
" Seed: {}",
args.seed.map_or("random".into(), |s| s.to_string())
);
eprintln!(" Contract: {}", args.contract.as_deref().unwrap_or("all"));
let mut cmd = std::process::Command::new("forge");
cmd.arg("test");
cmd.arg("--fuzz-runs").arg(args.runs.to_string());
if let Some(seed) = args.seed {
cmd.arg("--fuzz-seed").arg(seed.to_string());
}
if let Some(test) = &args.test {
cmd.arg("--match-test").arg(test);
}
if let Some(contract) = &args.contract {
cmd.arg("--match-contract").arg(contract);
}
eprintln!("\n🚀 Running fuzzing campaign...\n");
let status = cmd.status().context("Failed to run forge test")?;
if !status.success() {
anyhow::bail!("Fuzzing found issues — inspect output above");
}
eprintln!(
"\n{}",
"✅ Fuzzing passed — no issues found.".green().bold()
);
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fuzz_arg_defaults() {
let args = FuzzArgs {
shared: super::super::SharedFlags {
chain: "ethereum".into(),
project: std::path::PathBuf::from("."),
json: false,
markdown: false,
strict: false,
offline: false,
production: false,
report: false,
parallelism: 4,
},
runs: 10_000,
seed: None,
test: None,
contract: None,
};
assert_eq!(args.runs, 10_000);
assert!(args.seed.is_none());
assert!(args.test.is_none());
assert!(args.contract.is_none());
}
#[test]
fn test_fuzz_with_all_options() {
let args = FuzzArgs {
shared: super::super::SharedFlags::default(),
runs: 50_000,
seed: Some(42),
test: Some("testFuzzDeposit".into()),
contract: Some("Vault".into()),
};
assert_eq!(args.runs, 50_000);
assert_eq!(args.seed, Some(42));
assert_eq!(args.test.as_deref(), Some("testFuzzDeposit"));
assert_eq!(args.contract.as_deref(), Some("Vault"));
}
#[test]
fn test_fuzz_seed_display() {
let with_seed = Some(42u64);
let without_seed: Option<u64> = None;
assert_eq!(with_seed.map_or("random".into(), |s| s.to_string()), "42");
assert_eq!(
without_seed.map_or("random".into(), |s| s.to_string()),
"random"
);
}
#[test]
fn test_fuzz_contract_display() {
let with_contract = Some("Vault");
let without_contract: Option<&str> = None;
assert_eq!(with_contract.unwrap_or("all"), "Vault");
assert_eq!(without_contract.unwrap_or("all"), "all");
}
}