trident-client 0.12.0

Trident is Rust based fuzzing framework for Solana programs written in Anchor.
Documentation
use anyhow::Error;
use fehler::throws;
use pretty_assertions::assert_str_eq;
use std::fs;
use std::io::Read;
use std::path::Path;
use std::path::PathBuf;
use trident_client::___private::Commander;
use trident_idl_spec::Idl;
use trident_template::TridentTemplates;

#[throws]
#[tokio::test]
async fn test_types_generation() {
    let templates = setup_templates()?;
    verify_types(&templates).await?;
}

#[throws]
#[tokio::test]
async fn test_fuzz_accounts_generation() {
    let templates = setup_templates()?;
    verify_fuzz_accounts(&templates).await?;
}

#[throws]
#[tokio::test]
async fn test_test_fuzz_generation() {
    let templates = setup_templates()?;
    verify_test_fuzz(&templates).await?;
}

// Helper function to generate templates with proper error handling
fn generate_templates(
    templates: &TridentTemplates,
    idls: Vec<Idl>,
) -> anyhow::Result<trident_template::GeneratedFiles> {
    let current_package_version = env!("CARGO_PKG_VERSION");
    templates
        .generate(&idls, current_package_version)
        .map_err(|e| anyhow::anyhow!("Template generation failed: {}", e))
}

#[throws]
async fn verify_types(templates: &TridentTemplates) {
    let idls = vec![
        read_idl("additional_program.json")?,
        read_idl("idl_test.json")?,
    ];

    let generated_files = generate_templates(templates, idls)?;

    let generated_types = &generated_files.types;
    let expected_types_path = construct_path("fuzz_template/types.rs");
    let expected_types = fs::read_to_string(&expected_types_path)?;
    let formatted_types = Commander::format_program_code_nightly(generated_types).await?;

    assert_str_eq!(formatted_types, expected_types, "Types.rs does not match");
}

#[throws]
async fn verify_fuzz_accounts(templates: &TridentTemplates) {
    let idls = vec![
        read_idl("additional_program.json")?,
        read_idl("idl_test.json")?,
    ];

    let generated_files = generate_templates(templates, idls)?;

    let generated_fuzz = &generated_files.fuzz_accounts;
    let expected_fuzz_path = construct_path("fuzz_template/fuzz_accounts.rs");
    let expected_fuzz = fs::read_to_string(&expected_fuzz_path)?;
    let formatted_fuzz = Commander::format_program_code_nightly(generated_fuzz).await?;

    assert_str_eq!(
        formatted_fuzz,
        expected_fuzz,
        "Fuzz transactions does not match"
    );
}

#[throws]
async fn verify_test_fuzz(templates: &TridentTemplates) {
    let idls = vec![
        read_idl("additional_program.json")?,
        read_idl("idl_test.json")?,
    ];

    let generated_files = generate_templates(templates, idls)?;

    let generated_test_fuzz = &generated_files.test_fuzz;
    let expected_test_fuzz_path = construct_path("fuzz_template/test_fuzz.rs");
    let expected_test_fuzz = fs::read_to_string(&expected_test_fuzz_path)?;
    let formatted_test_fuzz = Commander::format_program_code_nightly(generated_test_fuzz).await?;

    assert_str_eq!(
        formatted_test_fuzz,
        expected_test_fuzz,
        "Test fuzz does not match"
    );
}

#[throws]
fn read_idl(idl_name: &str) -> Idl {
    let current_dir = std::env::current_dir()?;
    let anchor_idl_path: PathBuf = [
        current_dir.as_ref(),
        Path::new(&format!("tests/anchor_idl/{}", idl_name)),
    ]
    .iter()
    .collect();

    let mut idl_file = std::fs::File::open(&anchor_idl_path)?;
    let mut json_content = String::new();
    idl_file.read_to_string(&mut json_content)?;

    match serde_json::from_str::<Idl>(&json_content) {
        Ok(parsed_idl) => parsed_idl,
        Err(e) => {
            panic!("Failed to parse {}: {}", anchor_idl_path.display(), e);
        }
    }
}

// Helper function to construct paths relative to CARGO_MANIFEST_DIR
fn construct_path(relative_path: &str) -> PathBuf {
    let manifest_dir = env!("CARGO_MANIFEST_DIR");
    let mut path = PathBuf::from(manifest_dir);
    path.push("tests");
    path.push(relative_path);
    path
}

// Helper function to set up the template
#[throws]
fn setup_templates() -> TridentTemplates {
    TridentTemplates::default()
}