Skip to content

program: rewrite crate tests in mollusk #14

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Dec 19, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4,143 changes: 540 additions & 3,603 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion program/Cargo.toml
Original file line number Diff line number Diff line change
@@ -22,9 +22,9 @@ thiserror = "2.0"

[dev-dependencies]
lazy_static = "1.5.0"
mollusk-svm = "0.0.13"
proptest = "1.5"
serial_test = "3.2.0"
solana-program-test = "2.1.0"
solana-sdk = "2.1.0"

[lib]
140 changes: 0 additions & 140 deletions program/tests/action.rs

This file was deleted.

423 changes: 146 additions & 277 deletions program/tests/assert_instruction_count.rs
Original file line number Diff line number Diff line change
@@ -1,332 +1,201 @@
#![cfg(feature = "test-sbf")]

mod action;
mod setup;

use {
solana_program_test::{processor, tokio, ProgramTest},
mollusk_svm::{result::Check, Mollusk},
solana_sdk::{
account::{AccountSharedData, ReadableAccount},
program_pack::Pack,
pubkey::Pubkey,
signature::{Keypair, Signer},
system_instruction,
transaction::Transaction,
},
spl_token::{
id, instruction,
processor::Processor,
state::{Account, Mint},
},
};

const TRANSFER_AMOUNT: u64 = 1_000_000_000_000_000;

#[tokio::test]
async fn initialize_mint() {
let mut pt = ProgramTest::new("spl_token", id(), processor!(Processor::process));
pt.set_compute_max_units(5_000); // last known 2252
let (banks_client, payer, recent_blockhash) = pt.start().await;
#[test]
fn initialize_mint() {
let mut mollusk = Mollusk::new(&id(), "spl_token");
mollusk.compute_budget.compute_unit_limit = 5_000; // last known 2252

let owner_key = Pubkey::new_unique();
let mint = Keypair::new();
let owner = Pubkey::new_unique();
let mint = Pubkey::new_unique();
let decimals = 9;

let rent = banks_client.get_rent().await.unwrap();
let mint_rent = rent.minimum_balance(Mint::LEN);
let transaction = Transaction::new_signed_with_payer(
&[system_instruction::create_account(
&payer.pubkey(),
&mint.pubkey(),
mint_rent,
Mint::LEN as u64,
&id(),
)],
Some(&payer.pubkey()),
&[&payer, &mint],
recent_blockhash,
);
banks_client.process_transaction(transaction).await.unwrap();
let mint_account = {
let space = Mint::LEN;
let lamports = mollusk.sysvars.rent.minimum_balance(space);
AccountSharedData::new(lamports, space, &id())
};

let transaction = Transaction::new_signed_with_payer(
mollusk.process_and_validate_instruction(
&instruction::initialize_mint(&id(), &mint, &owner, None, decimals).unwrap(),
&[
(mint, mint_account),
mollusk.sysvars.keyed_account_for_rent_sysvar(),
],
&[
instruction::initialize_mint(&id(), &mint.pubkey(), &owner_key, None, decimals)
.unwrap(),
Check::success(),
Check::account(&mint)
.data(setup::setup_mint_account(Some(&owner), None, 0, decimals).data())
.build(),
],
Some(&payer.pubkey()),
&[&payer],
recent_blockhash,
);
banks_client.process_transaction(transaction).await.unwrap();
}

#[tokio::test]
async fn initialize_account() {
let mut pt = ProgramTest::new("spl_token", id(), processor!(Processor::process));
pt.set_compute_max_units(6_000); // last known 3284
let (mut banks_client, payer, recent_blockhash) = pt.start().await;
#[test]
fn initialize_account() {
let mut mollusk = Mollusk::new(&id(), "spl_token");
mollusk.compute_budget.compute_unit_limit = 6_000; // last known 3284

let owner = Keypair::new();
let mint = Keypair::new();
let account = Keypair::new();
let owner = Pubkey::new_unique();
let mint = Pubkey::new_unique();
let account = Pubkey::new_unique();
let decimals = 9;

action::create_mint(
&mut banks_client,
&payer,
recent_blockhash,
&mint,
&owner.pubkey(),
decimals,
)
.await
.unwrap();
let rent = banks_client.get_rent().await.unwrap();
let account_rent = rent.minimum_balance(Account::LEN);
let transaction = Transaction::new_signed_with_payer(
&[system_instruction::create_account(
&payer.pubkey(),
&account.pubkey(),
account_rent,
Account::LEN as u64,
&id(),
)],
Some(&payer.pubkey()),
&[&payer, &account],
recent_blockhash,
);
banks_client.process_transaction(transaction).await.unwrap();
let mint_account = setup::setup_mint_account(None, None, 0, decimals);
let token_account = {
let space = Account::LEN;
let lamports = mollusk.sysvars.rent.minimum_balance(space);
AccountSharedData::new(lamports, space, &id())
};

let transaction = Transaction::new_signed_with_payer(
&[instruction::initialize_account(
&id(),
&account.pubkey(),
&mint.pubkey(),
&owner.pubkey(),
)
.unwrap()],
Some(&payer.pubkey()),
&[&payer],
recent_blockhash,
mollusk.process_and_validate_instruction(
&instruction::initialize_account(&id(), &account, &mint, &owner).unwrap(),
&[
(account, token_account),
(mint, mint_account),
(owner, AccountSharedData::default()),
mollusk.sysvars.keyed_account_for_rent_sysvar(),
],
&[
Check::success(),
Check::account(&account)
.data(setup::setup_token_account(&mint, &owner, 0).data())
.build(),
],
);
banks_client.process_transaction(transaction).await.unwrap();
}

#[tokio::test]
async fn mint_to() {
let mut pt = ProgramTest::new("spl_token", id(), processor!(Processor::process));
pt.set_compute_max_units(6_000); // last known 2668
let (mut banks_client, payer, recent_blockhash) = pt.start().await;
#[test]
fn mint_to() {
let mut mollusk = Mollusk::new(&id(), "spl_token");
mollusk.compute_budget.compute_unit_limit = 6_000; // last known 2668

let owner = Keypair::new();
let mint = Keypair::new();
let account = Keypair::new();
let owner = Pubkey::new_unique();
let mint = Pubkey::new_unique();
let account = Pubkey::new_unique();
let decimals = 9;

action::create_mint(
&mut banks_client,
&payer,
recent_blockhash,
&mint,
&owner.pubkey(),
decimals,
)
.await
.unwrap();
action::create_account(
&mut banks_client,
&payer,
recent_blockhash,
&account,
&mint.pubkey(),
&owner.pubkey(),
)
.await
.unwrap();
let mint_account = setup::setup_mint_account(Some(&owner), None, 0, decimals);
let token_account = setup::setup_token_account(&mint, &owner, 0);

let transaction = Transaction::new_signed_with_payer(
&[instruction::mint_to(
&id(),
&mint.pubkey(),
&account.pubkey(),
&owner.pubkey(),
&[],
TRANSFER_AMOUNT,
)
.unwrap()],
Some(&payer.pubkey()),
&[&payer, &owner],
recent_blockhash,
mollusk.process_and_validate_instruction(
&instruction::mint_to(&id(), &mint, &account, &owner, &[], TRANSFER_AMOUNT).unwrap(),
&[
(mint, mint_account),
(account, token_account),
(owner, AccountSharedData::default()),
],
&[
Check::success(),
Check::account(&mint)
.data(
setup::setup_mint_account(Some(&owner), None, TRANSFER_AMOUNT, decimals).data(),
)
.build(),
Check::account(&account)
.data(setup::setup_token_account(&mint, &owner, TRANSFER_AMOUNT).data())
.build(),
],
);
banks_client.process_transaction(transaction).await.unwrap();
}

#[tokio::test]
async fn transfer() {
let mut pt = ProgramTest::new("spl_token", id(), processor!(Processor::process));
pt.set_compute_max_units(7_000); // last known 2972
let (mut banks_client, payer, recent_blockhash) = pt.start().await;

let owner = Keypair::new();
let mint = Keypair::new();
let source = Keypair::new();
let destination = Keypair::new();
let decimals = 9;
#[test]
fn transfer() {
let mut mollusk = Mollusk::new(&id(), "spl_token");
mollusk.compute_budget.compute_unit_limit = 7_000; // last known 2972

action::create_mint(
&mut banks_client,
&payer,
recent_blockhash,
&mint,
&owner.pubkey(),
decimals,
)
.await
.unwrap();
action::create_account(
&mut banks_client,
&payer,
recent_blockhash,
&source,
&mint.pubkey(),
&owner.pubkey(),
)
.await
.unwrap();
action::create_account(
&mut banks_client,
&payer,
recent_blockhash,
&destination,
&mint.pubkey(),
&owner.pubkey(),
)
.await
.unwrap();
let owner = Pubkey::new_unique();
let mint = Pubkey::new_unique();
let source = Pubkey::new_unique();
let destination = Pubkey::new_unique();

action::mint_to(
&mut banks_client,
&payer,
recent_blockhash,
&mint.pubkey(),
&source.pubkey(),
&owner,
TRANSFER_AMOUNT,
)
.await
.unwrap();
let source_token_account = setup::setup_token_account(&mint, &owner, TRANSFER_AMOUNT);
let destination_token_account = setup::setup_token_account(&mint, &owner, 0);

action::transfer(
&mut banks_client,
&payer,
recent_blockhash,
&source.pubkey(),
&destination.pubkey(),
&owner,
TRANSFER_AMOUNT,
)
.await
.unwrap();
mollusk.process_and_validate_instruction(
&instruction::transfer(&id(), &source, &destination, &owner, &[], TRANSFER_AMOUNT).unwrap(),
&[
(source, source_token_account),
(destination, destination_token_account),
(owner, AccountSharedData::default()),
],
&[
Check::success(),
Check::account(&source)
.data(setup::setup_token_account(&mint, &owner, 0).data())
.build(),
Check::account(&destination)
.data(setup::setup_token_account(&mint, &owner, TRANSFER_AMOUNT).data())
.build(),
],
);
}

#[tokio::test]
async fn burn() {
let mut pt = ProgramTest::new("spl_token", id(), processor!(Processor::process));
pt.set_compute_max_units(6_000); // last known 2655
let (mut banks_client, payer, recent_blockhash) = pt.start().await;
#[test]
fn burn() {
let mut mollusk = Mollusk::new(&id(), "spl_token");
mollusk.compute_budget.compute_unit_limit = 6_000; // last known 2655

let owner = Keypair::new();
let mint = Keypair::new();
let account = Keypair::new();
let owner = Pubkey::new_unique();
let mint = Pubkey::new_unique();
let account = Pubkey::new_unique();
let decimals = 9;

action::create_mint(
&mut banks_client,
&payer,
recent_blockhash,
&mint,
&owner.pubkey(),
decimals,
)
.await
.unwrap();
action::create_account(
&mut banks_client,
&payer,
recent_blockhash,
&account,
&mint.pubkey(),
&owner.pubkey(),
)
.await
.unwrap();

action::mint_to(
&mut banks_client,
&payer,
recent_blockhash,
&mint.pubkey(),
&account.pubkey(),
&owner,
TRANSFER_AMOUNT,
)
.await
.unwrap();
let mint_account = setup::setup_mint_account(None, None, TRANSFER_AMOUNT, decimals);
let token_account = setup::setup_token_account(&mint, &owner, TRANSFER_AMOUNT);

action::burn(
&mut banks_client,
&payer,
recent_blockhash,
&mint.pubkey(),
&account.pubkey(),
&owner,
TRANSFER_AMOUNT,
)
.await
.unwrap();
mollusk.process_and_validate_instruction(
&instruction::burn(&id(), &account, &mint, &owner, &[], TRANSFER_AMOUNT).unwrap(),
&[
(mint, mint_account),
(account, token_account),
(owner, AccountSharedData::default()),
],
&[
Check::success(),
Check::account(&account)
.data(setup::setup_token_account(&mint, &owner, 0).data())
.build(),
],
);
}

#[tokio::test]
async fn close_account() {
let mut pt = ProgramTest::new("spl_token", id(), processor!(Processor::process));
pt.set_compute_max_units(6_000); // last known 1783
let (mut banks_client, payer, recent_blockhash) = pt.start().await;
#[test]
fn close_account() {
let mut mollusk = Mollusk::new(&id(), "spl_token");
mollusk.compute_budget.compute_unit_limit = 6_000; // last known 1783

let owner = Keypair::new();
let mint = Keypair::new();
let account = Keypair::new();
let owner = Pubkey::new_unique();
let mint = Pubkey::new_unique();
let account = Pubkey::new_unique();
let decimals = 9;

action::create_mint(
&mut banks_client,
&payer,
recent_blockhash,
&mint,
&owner.pubkey(),
decimals,
)
.await
.unwrap();
action::create_account(
&mut banks_client,
&payer,
recent_blockhash,
&account,
&mint.pubkey(),
&owner.pubkey(),
)
.await
.unwrap();
let mint_account = setup::setup_mint_account(None, None, 0, decimals);
let token_account = setup::setup_token_account(&mint, &owner, 0);

let transaction = Transaction::new_signed_with_payer(
&[instruction::close_account(
&id(),
&account.pubkey(),
&owner.pubkey(),
&owner.pubkey(),
&[],
)
.unwrap()],
Some(&payer.pubkey()),
&[&payer, &owner],
recent_blockhash,
mollusk.process_and_validate_instruction(
&instruction::close_account(&id(), &account, &owner, &owner, &[]).unwrap(),
&[
(mint, mint_account),
(account, token_account),
(owner, AccountSharedData::default()),
],
&[Check::success(), Check::account(&account).closed().build()],
);
banks_client.process_transaction(transaction).await.unwrap();
}
257 changes: 84 additions & 173 deletions program/tests/close_account.rs
Original file line number Diff line number Diff line change
@@ -1,200 +1,111 @@
#![cfg(feature = "test-sbf")]

mod setup;

use {
solana_program_test::{processor, tokio, ProgramTest, ProgramTestContext},
mollusk_svm::{result::Check, Mollusk},
solana_sdk::{
instruction::InstructionError,
account::{AccountSharedData, ReadableAccount},
program_error::ProgramError,
program_pack::Pack,
pubkey::Pubkey,
signature::Signer,
signer::keypair::Keypair,
system_instruction,
transaction::{Transaction, TransactionError},
},
spl_token::{
instruction,
processor::Processor,
state::{Account, Mint},
system_instruction, system_program,
},
spl_token::{instruction, state::Account},
};

async fn setup_mint_and_account(
context: &mut ProgramTestContext,
mint: &Keypair,
token_account: &Keypair,
owner: &Pubkey,
token_program_id: &Pubkey,
) {
let rent = context.banks_client.get_rent().await.unwrap();
let mint_authority_pubkey = Pubkey::new_unique();
#[test]
fn success_init_after_close_account() {
let mollusk = Mollusk::new(&spl_token::id(), "spl_token");

let space = Mint::LEN;
let tx = Transaction::new_signed_with_payer(
&[
system_instruction::create_account(
&context.payer.pubkey(),
&mint.pubkey(),
rent.minimum_balance(space),
space as u64,
token_program_id,
),
instruction::initialize_mint(
token_program_id,
&mint.pubkey(),
&mint_authority_pubkey,
None,
9,
)
.unwrap(),
],
Some(&context.payer.pubkey()),
&[&context.payer, mint],
context.last_blockhash,
);
context.banks_client.process_transaction(tx).await.unwrap();
let space = Account::LEN;
let tx = Transaction::new_signed_with_payer(
&[
system_instruction::create_account(
&context.payer.pubkey(),
&token_account.pubkey(),
rent.minimum_balance(space),
space as u64,
token_program_id,
),
instruction::initialize_account(
token_program_id,
&token_account.pubkey(),
&mint.pubkey(),
owner,
)
.unwrap(),
],
Some(&context.payer.pubkey()),
&[&context.payer, token_account],
context.last_blockhash,
);
context.banks_client.process_transaction(tx).await.unwrap();
}
let owner = Pubkey::new_unique();
let mint = Pubkey::new_unique();
let account = Pubkey::new_unique();
let destination = Pubkey::new_unique();
let decimals = 9;

#[tokio::test]
async fn success_init_after_close_account() {
let program_test =
ProgramTest::new("spl_token", spl_token::id(), processor!(Processor::process));
let mut context = program_test.start_with_context().await;
let mint = Keypair::new();
let token_account = Keypair::new();
let owner = Keypair::new();
let token_program_id = spl_token::id();
setup_mint_and_account(
&mut context,
&mint,
&token_account,
&owner.pubkey(),
&token_program_id,
)
.await;
let owner_account = AccountSharedData::new(1_000_000_000, 0, &system_program::id());
let mint_account = setup::setup_mint_account(None, None, 0, decimals);
let token_account = setup::setup_token_account(&mint, &owner, 0);

let destination = Pubkey::new_unique();
let tx = Transaction::new_signed_with_payer(
let expected_destination_lamports = token_account.lamports();

mollusk.process_and_validate_instruction_chain(
&[
instruction::close_account(
&token_program_id,
&token_account.pubkey(),
&destination,
&owner.pubkey(),
&[],
)
.unwrap(),
instruction::close_account(&spl_token::id(), &account, &destination, &owner, &[])
.unwrap(),
system_instruction::create_account(
&context.payer.pubkey(),
&token_account.pubkey(),
&owner,
&account,
1_000_000_000,
Account::LEN as u64,
&token_program_id,
&spl_token::id(),
),
instruction::initialize_account(
&token_program_id,
&token_account.pubkey(),
&mint.pubkey(),
&owner.pubkey(),
)
.unwrap(),
instruction::initialize_account(&spl_token::id(), &account, &mint, &owner).unwrap(),
],
&[
(mint, mint_account),
(account, token_account),
(owner, owner_account),
(destination, AccountSharedData::default()),
mollusk.sysvars.keyed_account_for_rent_sysvar(),
],
&[
Check::success(),
// Account successfully re-initialized.
Check::account(&account)
.data(setup::setup_token_account(&mint, &owner, 0).data())
.owner(&spl_token::id())
.build(),
// The destination should have the lamports from the closed account.
Check::account(&destination)
.lamports(expected_destination_lamports)
.build(),
],
Some(&context.payer.pubkey()),
&[&context.payer, &owner, &token_account],
context.last_blockhash,
);
context.banks_client.process_transaction(tx).await.unwrap();
let destination = context
.banks_client
.get_account(destination)
.await
.unwrap()
.unwrap();
assert!(destination.lamports > 0);
}

#[tokio::test]
async fn fail_init_after_close_account() {
let program_test =
ProgramTest::new("spl_token", spl_token::id(), processor!(Processor::process));
let mut context = program_test.start_with_context().await;
let mint = Keypair::new();
let token_account = Keypair::new();
let owner = Keypair::new();
let token_program_id = spl_token::id();
setup_mint_and_account(
&mut context,
&mint,
&token_account,
&owner.pubkey(),
&token_program_id,
)
.await;
#[test]
fn fail_init_after_close_account() {
let mollusk = Mollusk::new(&spl_token::id(), "spl_token");

let owner = Pubkey::new_unique();
let mint = Pubkey::new_unique();
let account = Pubkey::new_unique();
let destination = Pubkey::new_unique();
let tx = Transaction::new_signed_with_payer(
let decimals = 9;

let owner_account = AccountSharedData::new(1_000_000_000, 0, &system_program::id());
let mint_account = setup::setup_mint_account(None, None, 0, decimals);
let token_account = setup::setup_token_account(&mint, &owner, 0);

let expected_destination_lamports = token_account.lamports();

mollusk.process_and_validate_instruction_chain(
&[
instruction::close_account(
&token_program_id,
&token_account.pubkey(),
&destination,
&owner.pubkey(),
&[],
)
.unwrap(),
system_instruction::transfer(
&context.payer.pubkey(),
&token_account.pubkey(),
1_000_000_000,
),
instruction::initialize_account(
&token_program_id,
&token_account.pubkey(),
&mint.pubkey(),
&owner.pubkey(),
)
.unwrap(),
instruction::close_account(&spl_token::id(), &account, &destination, &owner, &[])
.unwrap(),
system_instruction::transfer(&owner, &account, 1_000_000_000),
instruction::initialize_account(&spl_token::id(), &account, &mint, &owner).unwrap(),
],
&[
(mint, mint_account),
(account, token_account),
(owner, owner_account),
(destination, AccountSharedData::default()),
mollusk.sysvars.keyed_account_for_rent_sysvar(),
],
&[
Check::err(ProgramError::InvalidAccountData),
// Account not re-initialized.
Check::account(&account)
.lamports(1_000_000_000)
.owner(&system_program::id())
.build(),
// The destination should have the lamports from the closed account.
Check::account(&destination)
.lamports(expected_destination_lamports)
.build(),
],
Some(&context.payer.pubkey()),
&[&context.payer, &owner],
context.last_blockhash,
);
let error = context
.banks_client
.process_transaction(tx)
.await
.unwrap_err()
.unwrap();
assert_eq!(
error,
TransactionError::InstructionError(2, InstructionError::InvalidAccountData)
);
assert!(context
.banks_client
.get_account(destination)
.await
.unwrap()
.is_none());
}
69 changes: 69 additions & 0 deletions program/tests/setup.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#![cfg(feature = "test-sbf")]

use {
solana_sdk::{
account::{Account as SolanaAccount, AccountSharedData},
program_pack::Pack,
pubkey::Pubkey,
rent::Rent,
},
spl_token::state::{Account, AccountState, Mint},
};

pub fn setup_mint_account(
mint_authority: Option<&Pubkey>,
freeze_authority: Option<&Pubkey>,
supply: u64,
decimals: u8,
) -> AccountSharedData {
let data = {
let mut data = vec![0; Mint::LEN];
let state = Mint {
mint_authority: mint_authority.cloned().into(),
supply,
decimals,
is_initialized: true,
freeze_authority: freeze_authority.cloned().into(),
};
state.pack_into_slice(&mut data);
data
};

let space = data.len();
let lamports = Rent::default().minimum_balance(space);

AccountSharedData::from(SolanaAccount {
lamports,
data,
owner: spl_token::id(),
..Default::default()
})
}

pub fn setup_token_account(mint: &Pubkey, owner: &Pubkey, amount: u64) -> AccountSharedData {
let data = {
let mut data = vec![0; Account::LEN];
let state = Account {
mint: *mint,
owner: *owner,
amount,
delegate: None.into(),
state: AccountState::Initialized,
is_native: None.into(),
delegated_amount: 0,
close_authority: None.into(),
};
state.pack_into_slice(&mut data);
data
};

let space = data.len();
let lamports = Rent::default().minimum_balance(space);

AccountSharedData::from(SolanaAccount {
lamports,
data,
owner: spl_token::id(),
..Default::default()
})
}