Skip to content
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

tests: Update processor tests to use mollusk #16

Closed
wants to merge 11 commits into from
Closed
Changes from 1 commit
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
Next Next commit
[wip]: Switch processor tests to mollusk
febo committed Dec 20, 2024

Verified

This commit was signed with the committer’s verified signature.
febo Fernando Otero
commit 0a64761435d4584386848dde4efdb5c12b876e9d
9 changes: 3 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

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

[dev-dependencies]
lazy_static = "1.5.0"
mollusk-svm = "0.0.13"
mollusk-svm = { version = "0.0.13", git = "https://github.com/febo/mollusk.git", branch = "data-slice-check" }
febo marked this conversation as resolved.
Show resolved Hide resolved
proptest = "1.5"
serial_test = "3.2.0"
solana-sdk = "2.1.0"
146 changes: 146 additions & 0 deletions program/tests/processor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
#![cfg(feature = "test-sbf")]

//! Program state processor tests

use std::collections::HashSet;

use mollusk_svm::{
result::{Check, InstructionResult},
Mollusk,
};
use solana_sdk::{
account::{create_account_for_test, Account as SolanaAccount, AccountSharedData},
instruction::Instruction,
program_pack::Pack,
pubkey::Pubkey,
rent::Rent,
};
use spl_token::{error::TokenError, instruction::initialize_mint, state::Mint};

type InstructionPack<'a> = (Instruction, Vec<&'a SolanaAccount>);

fn do_process_instruction(
instruction: Instruction,
accounts: Vec<&SolanaAccount>,
checks: &[Check],
) -> InstructionResult {
let accounts = instruction
.accounts
.iter()
.zip(accounts)
.map(|(account_meta, account)| {
(
account_meta.pubkey,
AccountSharedData::from(account.clone()),
)
})
.collect::<Vec<_>>();

let mollusk = Mollusk::new(&spl_token::id(), "spl_token");
mollusk.process_and_validate_instruction(&instruction, accounts.as_slice(), checks)
}

fn do_process_instructions(
instructions: &[InstructionPack],
checks: &[Check],
) -> InstructionResult {
let mut present = HashSet::new();
let mut tx_instructions = Vec::new();
let mut tx_accounts = Vec::new();

instructions.iter().for_each(|(instruction, accounts)| {
instruction
.accounts
.iter()
.zip(accounts)
.map(|(account_meta, account)| {
(
account_meta.pubkey,
AccountSharedData::from((*account).clone()),
)
})
.for_each(|(pubkey, account)| {
if !present.contains(&pubkey) {
present.insert(pubkey);
tx_accounts.push((pubkey, account));
}
});
tx_instructions.push(instruction.clone());
});

let mollusk = Mollusk::new(&spl_token::id(), "spl_token");
mollusk.process_and_validate_instruction_chain(
tx_instructions.as_slice(),
tx_accounts.as_slice(),
checks,
)
}

fn mint_minimum_balance() -> u64 {
Rent::default().minimum_balance(Mint::get_packed_len())
}

fn rent_sysvar() -> SolanaAccount {
create_account_for_test(&Rent::default())
}

#[test]
fn test_initialize_mint() {
let program_id = spl_token::id();
let owner_key = Pubkey::new_unique();
let mint_key = Pubkey::new_unique();
let mut mint_account = SolanaAccount::new(42, Mint::get_packed_len(), &program_id);
let mint2_key = Pubkey::new_unique();
let mint2_account =
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
let rent_sysvar = rent_sysvar();

// mint is not rent exempt
do_process_instruction(
initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
vec![&mint_account, &rent_sysvar],
&[Check::err(TokenError::NotRentExempt.into())],
);

mint_account.lamports = mint_minimum_balance();

// create new mint
do_process_instruction(
initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
vec![&mint_account, &rent_sysvar],
&[Check::success()],
);

// create twice
do_process_instructions(
&[
(
initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
vec![&mint_account, &rent_sysvar],
),
(
initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
vec![&mint_account, &rent_sysvar],
),
],
&[Check::err(TokenError::AlreadyInUse.into())],
);

// create another mint that can freeze
do_process_instruction(
initialize_mint(&program_id, &mint2_key, &owner_key, Some(&owner_key), 2).unwrap(),
vec![&mint2_account, &rent_sysvar],
&[
// Account successfully re-initialized.
Check::success(),
// mint authority is set
Check::account(&mint2_key)
.data_slice(46, &[1, 0, 0, 0])
.build(),
// mint authority matches owner
Check::account(&mint2_key)
.data_slice(50, owner_key.as_ref())
.build(),
],
);
}