Test harness refactor and DepositSrm test (#17)
* refactoring test harness into TestMangoGroup * move mango group init code into helper * add deposit srm test * test flags and readme update Co-authored-by: Kevin Lu <kevin@bkcm.co>
This commit is contained in:
parent
734e9500d7
commit
b4e360f037
|
@ -76,5 +76,6 @@ Rework devnet_deploy.sh and use cli/mainnet.env to deploy to mainnet
|
|||
Regression and integration tests are in progress. To run them
|
||||
```
|
||||
cd program
|
||||
cargo test
|
||||
cargo test # run non-solana VM tests (none at the moment but would include simple unit tests in the future)
|
||||
cargo test-bpf --features test-bpf # run tests that use the solana VM (ie the smart contract tests)
|
||||
```
|
|
@ -7,6 +7,7 @@ edition = "2018"
|
|||
[features]
|
||||
no-entrypoint = []
|
||||
devnet = []
|
||||
test-bpf = []
|
||||
|
||||
[dependencies]
|
||||
solana-program = "^1.6.4"
|
||||
|
|
|
@ -1,22 +1,30 @@
|
|||
use std::mem::size_of;
|
||||
use std::convert::TryInto;
|
||||
use safe_transmute::{self, to_bytes::transmute_one_to_bytes};
|
||||
|
||||
use fixed::types::U64F64;
|
||||
use common::create_signer_key_and_nonce;
|
||||
use flux_aggregator::borsh_utils;
|
||||
use flux_aggregator::borsh_state::BorshState;
|
||||
use flux_aggregator::state::{Aggregator, AggregatorConfig, Answer};
|
||||
use solana_program::program_option::COption;
|
||||
use solana_program::program_pack::Pack;
|
||||
use solana_program::pubkey::Pubkey;
|
||||
use solana_program_test::{ProgramTest, BanksClient};
|
||||
|
||||
use solana_sdk::account_info::IntoAccountInfo;
|
||||
use solana_sdk::account::Account;
|
||||
use solana_sdk::signature::{Keypair, Signer};
|
||||
use solana_sdk::{
|
||||
account_info::IntoAccountInfo,
|
||||
account::Account,
|
||||
instruction::Instruction,
|
||||
signature::{Keypair, Signer}
|
||||
};
|
||||
|
||||
use spl_token::state::{Mint, Account as Token, AccountState};
|
||||
use serum_dex::state::{MarketState, AccountFlag, ToAlignedBytes};
|
||||
|
||||
use solana_program_test::ProgramTest;
|
||||
use mango::processor::srm_token;
|
||||
use serum_dex::state::{MarketState, AccountFlag};
|
||||
use serum_dex::state::ToAlignedBytes;
|
||||
use mango::instruction::init_mango_group;
|
||||
use mango::state::MangoGroup;
|
||||
|
||||
|
||||
trait AddPacked {
|
||||
|
@ -44,14 +52,14 @@ impl AddPacked for ProgramTest {
|
|||
}
|
||||
|
||||
|
||||
pub struct TestQuoteMint {
|
||||
pub struct TestMint {
|
||||
pub pubkey: Pubkey,
|
||||
pub authority: Keypair,
|
||||
pub decimals: u8,
|
||||
}
|
||||
|
||||
|
||||
pub fn add_mint(test: &mut ProgramTest, decimals: u8) -> TestQuoteMint {
|
||||
pub fn add_mint(test: &mut ProgramTest, decimals: u8) -> TestMint {
|
||||
let authority = Keypair::new();
|
||||
let pubkey = Pubkey::new_unique();
|
||||
test.add_packable_account(
|
||||
|
@ -65,14 +73,14 @@ pub fn add_mint(test: &mut ProgramTest, decimals: u8) -> TestQuoteMint {
|
|||
},
|
||||
&spl_token::id(),
|
||||
);
|
||||
TestQuoteMint {
|
||||
TestMint {
|
||||
pubkey,
|
||||
authority,
|
||||
decimals,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_mint_srm(test: &mut ProgramTest) -> TestQuoteMint {
|
||||
pub fn add_mint_srm(test: &mut ProgramTest) -> TestMint {
|
||||
let authority = Keypair::new();
|
||||
let pubkey = srm_token::ID;
|
||||
let decimals = 6;
|
||||
|
@ -87,7 +95,7 @@ pub fn add_mint_srm(test: &mut ProgramTest) -> TestQuoteMint {
|
|||
},
|
||||
&spl_token::id(),
|
||||
);
|
||||
TestQuoteMint {
|
||||
TestMint {
|
||||
pubkey,
|
||||
authority,
|
||||
decimals,
|
||||
|
@ -145,7 +153,7 @@ pub struct TestTokenAccount {
|
|||
pub pubkey: Pubkey,
|
||||
}
|
||||
|
||||
pub fn add_token_account(test: &mut ProgramTest, owner: Pubkey, mint: Pubkey) -> TestTokenAccount {
|
||||
pub fn add_token_account(test: &mut ProgramTest, owner: Pubkey, mint: Pubkey, initial_balance: u64) -> TestTokenAccount {
|
||||
let pubkey = Pubkey::new_unique();
|
||||
test.add_packable_account(
|
||||
pubkey,
|
||||
|
@ -153,7 +161,7 @@ pub fn add_token_account(test: &mut ProgramTest, owner: Pubkey, mint: Pubkey) ->
|
|||
&Token {
|
||||
mint: mint,
|
||||
owner: owner,
|
||||
amount: 0,
|
||||
amount: initial_balance,
|
||||
state: AccountState::Initialized,
|
||||
..Token::default()
|
||||
},
|
||||
|
@ -205,3 +213,108 @@ pub fn add_aggregator(test: &mut ProgramTest, name: &str, decimals: u8, price: u
|
|||
price,
|
||||
}
|
||||
}
|
||||
|
||||
// Holds all of the dependencies for a MangoGroup
|
||||
pub struct TestMangoGroup {
|
||||
pub program_id: Pubkey,
|
||||
pub mango_group_pk: Pubkey,
|
||||
pub signer_pk: Pubkey,
|
||||
pub signer_nonce: u64,
|
||||
|
||||
// Mints and Vaults must ordered with base assets first, quote asset last
|
||||
// They must be ordered in the same way
|
||||
pub mints: Vec<TestMint>,
|
||||
pub vaults: Vec<TestTokenAccount>,
|
||||
|
||||
pub srm_mint: TestMint,
|
||||
pub srm_vault: TestTokenAccount,
|
||||
|
||||
pub dex_prog_id: Pubkey,
|
||||
// Dexes and Oracles must be sorted in the same way as the first n-1 mints
|
||||
// mints[x] / mints[-1]
|
||||
pub dexes: Vec<TestDex>,
|
||||
pub oracles: Vec<TestAggregator>,
|
||||
|
||||
pub borrow_limits: Vec<u64>,
|
||||
}
|
||||
|
||||
|
||||
// This should probably go into the main code at some point when we remove the hard-coded market sizes
|
||||
fn to_fixed_array<T, const N: usize>(v: Vec<T>) -> [T; N] {
|
||||
v.try_into().unwrap_or_else(|v: Vec<T>| panic!("Expected a Vec of length {} but it was {}", N, v.len()))
|
||||
}
|
||||
|
||||
impl TestMangoGroup {
|
||||
pub fn init_mango_group(&self, payer: &Pubkey) -> Instruction {
|
||||
init_mango_group(
|
||||
&self.program_id,
|
||||
&self.mango_group_pk,
|
||||
&self.signer_pk,
|
||||
&self.dex_prog_id,
|
||||
&self.srm_vault.pubkey,
|
||||
payer,
|
||||
self.mints.iter().map(|m| m.pubkey).collect::<Vec<Pubkey>>().as_slice(),
|
||||
self.vaults.iter().map(|m| m.pubkey).collect::<Vec<Pubkey>>().as_slice(),
|
||||
self.dexes.iter().map(|m| m.pubkey).collect::<Vec<Pubkey>>().as_slice(),
|
||||
self.oracles.iter().map(|m| m.pubkey).collect::<Vec<Pubkey>>().as_slice(),
|
||||
self.signer_nonce,
|
||||
U64F64::from_num(1.1),
|
||||
U64F64::from_num(1.2),
|
||||
to_fixed_array(self.borrow_limits.clone()),
|
||||
).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_mango_group_prodlike(test: &mut ProgramTest, program_id: Pubkey) -> TestMangoGroup {
|
||||
let mango_group_pk = Pubkey::new_unique();
|
||||
let (signer_pk, signer_nonce) = create_signer_key_and_nonce(&program_id, &mango_group_pk);
|
||||
test.add_account(mango_group_pk, Account::new(u32::MAX as u64, size_of::<MangoGroup>(), &program_id));
|
||||
|
||||
let btc_mint = add_mint(test, 6);
|
||||
let eth_mint = add_mint(test, 6);
|
||||
let usdt_mint = add_mint(test, 6);
|
||||
|
||||
let btc_vault = add_token_account(test, signer_pk, btc_mint.pubkey, 0);
|
||||
let eth_vault = add_token_account(test, signer_pk, eth_mint.pubkey, 0);
|
||||
let usdt_vault = add_token_account(test, signer_pk, usdt_mint.pubkey, 0);
|
||||
|
||||
let srm_mint = add_mint_srm(test);
|
||||
let srm_vault = add_token_account(test, signer_pk, srm_mint.pubkey, 0);
|
||||
|
||||
let dex_prog_id = Pubkey::new_unique();
|
||||
let btc_usdt_dex = add_dex_empty(test, btc_mint.pubkey, usdt_mint.pubkey, dex_prog_id);
|
||||
let eth_usdt_dex = add_dex_empty(test, eth_mint.pubkey, usdt_mint.pubkey, dex_prog_id);
|
||||
|
||||
let unit = 10u64.pow(6);
|
||||
let btc_usdt = add_aggregator(test, "BTC:USDT", 6, 50_000 * unit, &program_id);
|
||||
let eth_usdt = add_aggregator(test, "ETH:USDT", 6, 2_000 * unit, &program_id);
|
||||
|
||||
let mints = vec![btc_mint, eth_mint, usdt_mint];
|
||||
let vaults = vec![btc_vault, eth_vault, usdt_vault];
|
||||
let dexes = vec![btc_usdt_dex, eth_usdt_dex];
|
||||
let oracles = vec![btc_usdt, eth_usdt];
|
||||
let borrow_limits = vec![100, 100, 100];
|
||||
|
||||
TestMangoGroup {
|
||||
program_id,
|
||||
mango_group_pk,
|
||||
signer_pk,
|
||||
signer_nonce,
|
||||
mints,
|
||||
vaults,
|
||||
srm_mint,
|
||||
srm_vault,
|
||||
dex_prog_id,
|
||||
dexes,
|
||||
oracles,
|
||||
borrow_limits,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_token_balance(banks_client: &mut BanksClient, pubkey: Pubkey) -> u64 {
|
||||
let token: Account = banks_client.get_account(pubkey).await.unwrap().unwrap();
|
||||
|
||||
spl_token::state::Account::unpack(&token.data[..])
|
||||
.unwrap()
|
||||
.amount
|
||||
}
|
|
@ -1,24 +1,28 @@
|
|||
#![cfg(feature="test-bpf")]
|
||||
|
||||
mod helpers;
|
||||
|
||||
use std::mem::size_of;
|
||||
|
||||
use fixed::types::U64F64;
|
||||
use helpers::*;
|
||||
use solana_program_test::*;
|
||||
use solana_sdk::{
|
||||
pubkey::Pubkey,
|
||||
signature::Signer,
|
||||
signature::{Signer, Keypair},
|
||||
transaction::Transaction,
|
||||
account::Account,
|
||||
};
|
||||
use solana_program::account_info::AccountInfo;
|
||||
|
||||
use mango::entrypoint::process_instruction;
|
||||
use mango::instruction::init_mango_group;
|
||||
use mango::state::MangoGroup;
|
||||
use common::create_signer_key_and_nonce;
|
||||
use mango::{
|
||||
entrypoint::process_instruction,
|
||||
instruction::deposit_srm,
|
||||
state::MangoSrmAccount,
|
||||
};
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_init() {
|
||||
async fn test_init_mango_group() {
|
||||
// Mostly a test to ensure we can successfully create the testing harness
|
||||
// Also gives us an alert if the InitMangoGroup tx ends up using too much gas
|
||||
let program_id = Pubkey::new_unique();
|
||||
|
||||
let mut test = ProgramTest::new(
|
||||
|
@ -28,57 +32,15 @@ async fn test_init() {
|
|||
);
|
||||
|
||||
// limit to track compute unit increase
|
||||
test.set_bpf_compute_max_units(51_000);
|
||||
test.set_bpf_compute_max_units(20_000);
|
||||
|
||||
let mango_group_pk = Pubkey::new_unique();
|
||||
let (signer_pk, signer_nonce) = create_signer_key_and_nonce(&program_id, &mango_group_pk);
|
||||
test.add_account(mango_group_pk, Account::new(u32::MAX as u64, size_of::<MangoGroup>(), &program_id));
|
||||
|
||||
let btc_mint = add_mint(&mut test, 6);
|
||||
let eth_mint = add_mint(&mut test, 6);
|
||||
let usdt_mint = add_mint(&mut test, 6);
|
||||
let mints = vec![btc_mint.pubkey, eth_mint.pubkey, usdt_mint.pubkey];
|
||||
|
||||
let btc_vault = add_token_account(&mut test, signer_pk, btc_mint.pubkey);
|
||||
let eth_vault = add_token_account(&mut test, signer_pk, eth_mint.pubkey);
|
||||
let usdt_vault = add_token_account(&mut test, signer_pk, usdt_mint.pubkey);
|
||||
let vaults = vec![btc_vault.pubkey, eth_vault.pubkey, usdt_vault.pubkey];
|
||||
|
||||
let srm_mint = add_mint_srm(&mut test);
|
||||
let srm_vault = add_token_account(&mut test, signer_pk, srm_mint.pubkey);
|
||||
|
||||
let dex_prog_id = Pubkey::new_unique();
|
||||
let btc_usdt_dex = add_dex_empty(&mut test, btc_mint.pubkey, usdt_mint.pubkey, dex_prog_id);
|
||||
let eth_usdt_dex = add_dex_empty(&mut test, eth_mint.pubkey, usdt_mint.pubkey, dex_prog_id);
|
||||
let dexes = vec![btc_usdt_dex.pubkey, eth_usdt_dex.pubkey];
|
||||
|
||||
let unit = 10u64.pow(6);
|
||||
let btc_usdt = add_aggregator(&mut test, "BTC:USDT", 6, 50_000 * unit, &program_id);
|
||||
let eth_usdt = add_aggregator(&mut test, "ETH:USDT", 6, 2_000 * unit, &program_id);
|
||||
let oracles = vec![btc_usdt.pubkey, eth_usdt.pubkey];
|
||||
let mango_group = add_mango_group_prodlike(&mut test, program_id);
|
||||
|
||||
let (mut banks_client, payer, recent_blockhash) = test.start().await;
|
||||
|
||||
let borrow_limits = [100, 100, 100];
|
||||
|
||||
let mut transaction = Transaction::new_with_payer(
|
||||
&[
|
||||
init_mango_group(
|
||||
&program_id,
|
||||
&mango_group_pk,
|
||||
&signer_pk,
|
||||
&dex_prog_id,
|
||||
&srm_vault.pubkey,
|
||||
&payer.pubkey(),
|
||||
&mints,
|
||||
&vaults,
|
||||
&dexes,
|
||||
&oracles,
|
||||
signer_nonce,
|
||||
U64F64::from_num(1.1),
|
||||
U64F64::from_num(1.2),
|
||||
borrow_limits,
|
||||
).unwrap(),
|
||||
mango_group.init_mango_group(&payer.pubkey()),
|
||||
],
|
||||
Some(&payer.pubkey()),
|
||||
);
|
||||
|
@ -88,4 +50,70 @@ async fn test_init() {
|
|||
recent_blockhash,
|
||||
);
|
||||
assert!(banks_client.process_transaction(transaction).await.is_ok());
|
||||
}
|
||||
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_deposit_srm() {
|
||||
// Test that the DepositSrm instruction succeeds in the simple case
|
||||
let program_id = Pubkey::new_unique();
|
||||
|
||||
let mut test = ProgramTest::new(
|
||||
"mango",
|
||||
program_id,
|
||||
processor!(process_instruction),
|
||||
);
|
||||
|
||||
// limit to track compute unit increase
|
||||
test.set_bpf_compute_max_units(20_000);
|
||||
|
||||
let initial_amount = 500;
|
||||
let deposit_amount = 100;
|
||||
|
||||
let user = Keypair::new();
|
||||
let user_pk = user.pubkey();
|
||||
let mango_group = add_mango_group_prodlike(&mut test, program_id);
|
||||
let mango_srm_account_pk = Pubkey::new_unique();
|
||||
test.add_account(mango_srm_account_pk, Account::new(u32::MAX as u64, size_of::<MangoSrmAccount>(), &program_id));
|
||||
let user_srm_account = add_token_account(&mut test, user_pk, mango_group.srm_mint.pubkey, initial_amount);
|
||||
|
||||
let (mut banks_client, payer, recent_blockhash) = test.start().await;
|
||||
|
||||
let mut transaction = Transaction::new_with_payer(
|
||||
&[
|
||||
mango_group.init_mango_group(&payer.pubkey()),
|
||||
|
||||
deposit_srm(
|
||||
&program_id,
|
||||
&mango_group.mango_group_pk,
|
||||
&mango_srm_account_pk,
|
||||
&user_pk,
|
||||
&user_srm_account.pubkey,
|
||||
&mango_group.srm_vault.pubkey,
|
||||
deposit_amount,
|
||||
).unwrap(),
|
||||
],
|
||||
Some(&payer.pubkey()),
|
||||
);
|
||||
|
||||
transaction.sign(
|
||||
&[&payer, &user],
|
||||
recent_blockhash,
|
||||
);
|
||||
assert!(banks_client.process_transaction(transaction).await.is_ok());
|
||||
|
||||
let final_user_balance = get_token_balance(&mut banks_client, user_srm_account.pubkey).await;
|
||||
assert_eq!(final_user_balance, initial_amount - deposit_amount);
|
||||
let mango_vault_srm_balance = get_token_balance(&mut banks_client, mango_group.srm_vault.pubkey).await;
|
||||
assert_eq!(mango_vault_srm_balance, deposit_amount);
|
||||
|
||||
let mut mango_srm_account = banks_client.get_account(mango_srm_account_pk).await.unwrap().unwrap();
|
||||
let account_info: AccountInfo = (&mango_srm_account_pk, &mut mango_srm_account).into();
|
||||
|
||||
let mango_srm_account = MangoSrmAccount::load_mut_checked(
|
||||
&program_id,
|
||||
&account_info,
|
||||
&mango_group.mango_group_pk,
|
||||
).unwrap();
|
||||
assert_eq!(mango_srm_account.amount, deposit_amount);
|
||||
}
|
Loading…
Reference in New Issue