#fuzz-testing #trdelnik #solana #programs #anchor #write #test-framework

macro turdle-test

The trdelnik_test crate helps you to write Rust tests for your programs with Trdelnik

1 unstable release

0.1.0 Jun 24, 2023

#7 in #trdelnik

26 downloads per month
Used in 2 crates (via turdle-client)

Custom license

6KB
50 lines

Trdelnik Logo

Trdelník

Ackee Blockchain Discord invitation

developed by Ackee Blockchain

Crates.io Crates.io Crates.io Crates.io
lint Test Escrow and Turnstile

Trdelník is Rust based testing framework providing several convenient developer tools for testing Solana programs written in Anchor.

  • Trdelnik fuzz - property-based and stateful testing;
  • Trdelnik client - build and deploy an Anchor program to a local cluster and run a test suite against it;
  • Trdelnik console - built-in console to give developers a command prompt for quick program interaction;
  • Trdelnik explorer - exploring a ledger changes.

Dependencies

Installation

cargo install trdelnik-cli

# or the specific version

cargo install --version <version> trdelnik-cli

Usage

# Navigate to your project root directory.
# Trdelnik initialization will generate `.program_client` and `trdelnik-tests` directories with all the necessary files.
trdelnik init
# Run the fuzzer on the given target.
trdelnik fuzz run <TARGET_NAME>
# Want more?
trdelnik --help

How to write fuzz tests?

Once you initialize Trdelnik in your Anchor project, you will find a fuzz test template in the trdelnik-tests/src/bin folder that you can modify according to your needs or create new targets. Do not forget to install honggfuzz-rs using cargo install honggfuzz.

# To run the fuzz test, execute this command from your terminal and replace <TARGET_NAME> with the name of your fuzz target (by default "fuzz_target")
trdelnik fuzz run <TARGET_NAME>

# To debug your fuzz target crash with parameters from a crash file
trdelnik fuzz run-debug <TARGET_NAME> <CRASH_FILE_PATH>

Under the hood Trdelnik uses honggfuzz-rs. You can pass parameters via environment variables. List of hongfuzz parameters can be found in honggfuzz usage documentation. For example:

# Time-out: 10 secs
# Number of concurrent fuzzing threads: 1
# Number of fuzzing iterations: 10000
# Display Solana logs in the terminal
HFUZZ_RUN_ARGS="-t 10 -n 1 -N 10000 -Q" trdelnik fuzz run <TARGET_NAME>

NOTE: If you will use the solana-program-test crate for fuzzing, creating a new test program using ProgramTest::new() will create temporary folders in your /tmp directory that will not be cleared in case your program panics. You might want to clear these folders manually.

How to write tests?

Trdelnik also supports writing integration tests in Rust.

Trdelnik Demo
// <my_project>/trdelnik-tests/tests/test.rs
// TODO: do not forget to add all necessary dependencies to the generated `trdelnik-tests/Cargo.toml`
use program_client::my_instruction;
use trdelnik_client::*;
use my_program;

#[throws]
#[fixture]
async fn init_fixture() -> Fixture {
  // create a test fixture
  let mut fixture = Fixture {
    client: Client::new(system_keypair(0)),
    // make sure your program is using a correct program ID
    program: program_keypair(1),
    state: keypair(42),
  };
  // deploy a tested program
  fixture.deploy().await?;
  // call instruction init
  my_instruction::initialize(
    &fixture.client,
    fixture.state.pubkey(),
    fixture.client.payer().pubkey(),
    System::id(),
    Some(fixture.state.clone()),
  ).await?;
  fixture
}

#[trdelnik_test]
async fn test_happy_path(#[future] init_fixture: Result<Fixture>) {
  let fixture = init_fixture.await?;
  // call the instruction
  my_instruction::do_something(
    &fixture.client,
    "dummy_string".to_owned(),
    fixture.state.pubkey(),
    None,
  ).await?;
  // check the test result
  let state = fixture.get_state().await?;
  assert_eq!(state.something_changed, "yes");
}

Make sure your program is using a correct program ID in the derive_id!(...) macro and inside Anchor.toml. If not, obtain the public key of a key pair you're using and replace it in these two places. To get the program ID of a key pair (key pair's public key) the trdelnik key-pair command can be used. For example

$ trdelnik key-pair program 7

will print information about the key pair received from program_keypair(7).

Instructions with custom structures

  • If you want to test an instruction which has custom structure as an argument
pub struct MyStruct {
  amount: u64,
}

// ...

pub fn my_instruction(ctx: Context<Ctx>, data: MyStruct) { /* ... */ }
  • You should add an import to the .program_client crate
// .program_client/src/lib.rs

// DO NOT EDIT - automatically generated file
pub mod my_program_instruction {
  use trdelnik_client::*;
  use my_program::MyStruct; // add this import

// ...
}
  • This file is automatically generated but the use statements won't be regenerated

Skipping tests

  • You can add the #[ignore] macro to skip the test.
#[trdelnik_test]
#[ignore]
async fn test() {}

Testing programs with associated token accounts

  • Trdelnik does not export anchor-spl and spl-associated-token-account, so you have to add it manually.
# <my-project>/trdelnik-tests/Cargo.toml
# import the correct versions manually
anchor-spl = "0.28.0"
spl-associated-token-account = "2.0.0"
// <my-project>/trdelnik-tests/tests/test.rs
use anchor_spl::token::Token;
use spl_associated_token_account;

async fn init_fixture() -> Fixture {
  // ...
  let account = keypair(1);
  let mint = keypair(2);
  // constructs a token mint
  client
    .create_token_mint(&mint, mint.pubkey(), None, 0)
    .await?;
  // constructs associated token account
  let token_account = client
    .create_associated_token_account(&account, mint.pubkey())
    .await?;
  let associated_token_program = spl_associated_token_account::id();
  // derives the associated token account address for the given wallet and mint
  let associated_token_address = spl_associated_token_account::get_associated_token_address(&account.pubkey(), mint);
  Fixture {
    // ...
    token_program: Token::id(),
  }
}
  • The trdelnik init command generated a dummy test suite for you.
  • For more details, see the complete test implementation.

Supported versions

  • We support Anchor and Solana versions specified in the table below.
Trdelnik CLI Anchor Solana
latest ~0.28.* =1.16.6
v0.4.0 ~0.27.* >=1.15
v0.3.0 ~0.25.* >=1.10
v0.2.0 ~0.24.* >=1.9
  • We are exploring a new versions of Anchor, please make sure you only use the supported versions. We are working on it 💪

Configuration

The configuration variables can be edited in the Trdelnik.toml file that'll be generated in the root of the project.

Name Default value Description
test.validator_startup_timeout 10 000 Time to wait for the solana-test-validator in milliseconds before failure

Roadmap

  • Q1/22 Trdelnik announcement at Solana Hacker House Prague
    • Trdelnik client available for testing
  • Q2/22 Trdelnik explorer available
  • Q2/22 Trdelnik client and explorer introduced at Solana Hacker House Barcelona
  • Q3/23 Trdelnik fuzz introduced at Solana Hacker House Berlin

Awards

Marinade Community Prize - winner of the Marinade grant for the 2022 Solana Riptide Hackathon.

Contribution

Thank you for your interest in contributing to Trdelník! Please see the CONTRIBUTING.md to learn how.

License

This project is licensed under the MIT license.

University and investment partners

Dependencies

~2MB
~41K SLoC