Skip to content

Commit

Permalink
Add token account tests
Browse files Browse the repository at this point in the history
  • Loading branch information
febo committed Nov 21, 2023
1 parent a648103 commit 5914b03
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 3 deletions.
2 changes: 1 addition & 1 deletion clients/rust/tests/create.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#[cfg(feature = "test-sbf")]
pub mod setup;
use setup::*;
pub use setup::*;

use solana_program::pubkey::Pubkey;
use solana_program::system_program;
Expand Down
2 changes: 1 addition & 1 deletion clients/rust/tests/lock.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#[cfg(feature = "test-sbf")]
pub mod setup;
use setup::*;
pub use setup::*;

use solana_program::pubkey::Pubkey;
use solana_program_test::*;
Expand Down
120 changes: 119 additions & 1 deletion clients/rust/tests/mint.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
#[cfg(feature = "test-sbf")]
mod setup;
use setup::*;
pub use setup::*;

use mpl_token_metadata::errors::MplTokenMetadataError;
use mpl_token_metadata::types::TokenStandard;
use solana_program::pubkey::Pubkey;
use solana_program_test::*;
Expand Down Expand Up @@ -222,4 +223,121 @@ mod mint_token2022 {
let token = unpack::<Account>(&token_account.data).unwrap().base;
assert_eq!(token.amount, 1);
}

#[test_case::test_case(TokenStandard::Fungible ; "fungible")]
#[test_case::test_case(TokenStandard::FungibleAsset ; "fungible_asset")]
#[test_case::test_case(TokenStandard::NonFungible ; "non_fungible")]
#[test_case::test_case(TokenStandard::ProgrammableNonFungible ; "programmable_non_fungible")]
#[tokio::test]
async fn mint_with_non_transferable_mint_and_invalid_token_account(
token_standard: TokenStandard,
) {
let mut context = program_test().start_with_context().await;

// given a mint account with a non-transferable extension and metadata

let mut asset = DigitalAsset::default();
asset
.create_default_with_mint_extensions(
&mut context,
token_standard,
&[ExtensionType::NonTransferable],
)
.await
.unwrap();

// and an existing token account without the immutable owner extension
let mint = asset.mint.pubkey();
let owner = Keypair::new().pubkey();
let token_account = Keypair::new();

TokenManager::default()
.create_token_account_with_extensions(
&mut context,
&owner,
&token_account,
&mint,
&[ExtensionType::NonTransferableAccount],
)
.await
.unwrap();

asset.token = token_account.pubkey();

// when minting a token

let payer = context.payer.dirty_clone();

let error = asset
.mint(&mut context, &owner, 1, &payer, &payer, spl_token_2022::ID)
.await
.unwrap_err();

// then we expect an error

assert_custom_instruction_error!(
0,
error,
MplTokenMetadataError::MissingImmutableOwnerExtension
);
}

#[test_case::test_case(TokenStandard::Fungible ; "fungible")]
#[test_case::test_case(TokenStandard::FungibleAsset ; "fungible_asset")]
#[test_case::test_case(TokenStandard::NonFungible ; "non_fungible")]
#[test_case::test_case(TokenStandard::ProgrammableNonFungible ; "programmable_non_fungible")]
#[tokio::test]
async fn mint_with_non_transferable_mint(token_standard: TokenStandard) {
let mut context = program_test().start_with_context().await;

// given a mint account with extensions and metadata

let mut asset = DigitalAsset::default();
asset
.create_default_with_mint_extensions(
&mut context,
token_standard,
&[ExtensionType::NonTransferable],
)
.await
.unwrap();

// and an existing token account with extensions
let mint = asset.mint.pubkey();
let owner = Keypair::new().pubkey();
let token_account = Keypair::new();

TokenManager::default()
.create_token_account_with_extensions(
&mut context,
&owner,
&token_account,
&mint,
&[
ExtensionType::NonTransferableAccount,
ExtensionType::ImmutableOwner,
],
)
.await
.unwrap();

asset.token = token_account.pubkey();

// when minting a token

let payer = context.payer.dirty_clone();

asset
.mint(&mut context, &owner, 1, &payer, &payer, spl_token_2022::ID)
.await
.unwrap();

// then the token account is created with the correct balance

let token_account = get_account(&mut context, &asset.token).await;
assert_eq!(token_account.owner, spl_token_2022::ID);

let token = unpack::<Account>(&token_account.data).unwrap().base;
assert_eq!(token.amount, 1);
}
}
51 changes: 51 additions & 0 deletions clients/rust/tests/setup/token_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,4 +250,55 @@ impl TokenManager {

context.banks_client.process_transaction(tx).await
}

pub async fn create_token_account_with_extensions(
&self,
context: &mut ProgramTestContext,
owner: &Pubkey,
token_account: &Keypair,
mint: &Pubkey,
extensions: &[ExtensionType],
) -> Result<(), BanksClientError> {
let length = ExtensionType::try_calculate_account_len::<Account>(extensions).unwrap();
let rent = context.banks_client.get_rent().await.unwrap();

let mut instructions = vec![];

instructions.push(system_instruction::create_account(
&context.payer.pubkey(),
&token_account.pubkey(),
rent.minimum_balance(length),
length as u64,
&self.spl_token_program,
));

if extensions.contains(&ExtensionType::ImmutableOwner) {
instructions.push(
spl_token_2022::instruction::initialize_immutable_owner(
&self.spl_token_program,
&token_account.pubkey(),
)
.unwrap(),
);
}

instructions.push(
spl_token_2022::instruction::initialize_account3(
&self.spl_token_program,
&token_account.pubkey(),
mint,
owner,
)
.unwrap(),
);

let tx = Transaction::new_signed_with_payer(
&instructions,
Some(&context.payer.pubkey()),
&[&context.payer, token_account],
context.last_blockhash,
);

context.banks_client.process_transaction(tx).await
}
}

0 comments on commit 5914b03

Please sign in to comment.