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:
kevlubkcm 2021-04-30 18:38:24 -04:00 committed by GitHub
parent 734e9500d7
commit b4e360f037
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 210 additions and 67 deletions

View File

@ -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)
```

View File

@ -7,6 +7,7 @@ edition = "2018"
[features]
no-entrypoint = []
devnet = []
test-bpf = []
[dependencies]
solana-program = "^1.6.4"

View File

@ -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
}

View File

@ -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);
}