2019-12-03 14:35:27 +00:00
package keeper_test
2019-11-25 19:46:02 +00:00
import (
"fmt"
"testing"
2019-11-28 16:53:59 +00:00
"time"
2019-11-25 19:46:02 +00:00
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
2019-12-03 14:35:27 +00:00
"github.com/kava-labs/kava/app"
"github.com/kava-labs/kava/x/cdp/types"
"github.com/kava-labs/kava/x/pricefeed"
2019-11-25 19:46:02 +00:00
)
// How could one reduce the number of params in the test cases. Create a table driven test for each of the 4 add/withdraw collateral/debt?
// These are more like app level tests - I think this is a symptom of having 'ModifyCDP' do a lot. Could be easier for testing purposes to break it down.
func TestKeeper_ModifyCDP ( t * testing . T ) {
2019-12-03 14:35:27 +00:00
_ , addrs := app . GeneratePrivKeyAddressPairs ( 1 )
2019-11-25 19:46:02 +00:00
ownerAddr := addrs [ 0 ]
2019-12-03 14:35:27 +00:00
type state struct {
2019-11-25 19:46:02 +00:00
CDP types . CDP
OwnerCoins sdk . Coins
GlobalDebt sdk . Int
CollateralState types . CollateralState
}
type args struct {
owner sdk . AccAddress
collateralDenom string
changeInCollateral sdk . Int
changeInDebt sdk . Int
}
tests := [ ] struct {
name string
priorState state
price string
// also missing CDPModuleParams
args args
expectPass bool
expectedState state
} {
{
"addCollateralAndDecreaseDebt" ,
state { types . CDP { ownerAddr , "xrp" , i ( 100 ) , i ( 2 ) } , cs ( c ( "xrp" , 10 ) , c ( "usdx" , 2 ) ) , i ( 2 ) , types . CollateralState { "xrp" , i ( 2 ) } } ,
"10.345" ,
args { ownerAddr , "xrp" , i ( 10 ) , i ( - 1 ) } ,
true ,
state { types . CDP { ownerAddr , "xrp" , i ( 110 ) , i ( 1 ) } , cs ( /* 0xrp */ c ( "usdx" , 1 ) ) , i ( 1 ) , types . CollateralState { "xrp" , i ( 1 ) } } ,
} ,
{
"removeTooMuchCollateral" ,
state { types . CDP { ownerAddr , "xrp" , i ( 1000 ) , i ( 200 ) } , cs ( c ( "xrp" , 10 ) , c ( "usdx" , 10 ) ) , i ( 200 ) , types . CollateralState { "xrp" , i ( 200 ) } } ,
"1.00" ,
args { ownerAddr , "xrp" , i ( - 601 ) , i ( 0 ) } ,
false ,
state { types . CDP { ownerAddr , "xrp" , i ( 1000 ) , i ( 200 ) } , cs ( c ( "xrp" , 10 ) , c ( "usdx" , 10 ) ) , i ( 200 ) , types . CollateralState { "xrp" , i ( 200 ) } } ,
} ,
{
"withdrawTooMuchStableCoin" ,
state { types . CDP { ownerAddr , "xrp" , i ( 1000 ) , i ( 200 ) } , cs ( c ( "xrp" , 10 ) , c ( "usdx" , 10 ) ) , i ( 200 ) , types . CollateralState { "xrp" , i ( 200 ) } } ,
"1.00" ,
args { ownerAddr , "xrp" , i ( 0 ) , i ( 301 ) } ,
false ,
state { types . CDP { ownerAddr , "xrp" , i ( 1000 ) , i ( 200 ) } , cs ( c ( "xrp" , 10 ) , c ( "usdx" , 10 ) ) , i ( 200 ) , types . CollateralState { "xrp" , i ( 200 ) } } ,
} ,
{
"createCDPAndWithdrawStable" ,
state { types . CDP { } , cs ( c ( "xrp" , 10 ) , c ( "usdx" , 10 ) ) , i ( 0 ) , types . CollateralState { "xrp" , i ( 0 ) } } ,
"1.00" ,
args { ownerAddr , "xrp" , i ( 5 ) , i ( 2 ) } ,
true ,
state { types . CDP { ownerAddr , "xrp" , i ( 5 ) , i ( 2 ) } , cs ( c ( "xrp" , 5 ) , c ( "usdx" , 12 ) ) , i ( 2 ) , types . CollateralState { "xrp" , i ( 2 ) } } ,
} ,
{
"emptyCDP" ,
state { types . CDP { ownerAddr , "xrp" , i ( 1000 ) , i ( 200 ) } , cs ( c ( "xrp" , 10 ) , c ( "usdx" , 201 ) ) , i ( 200 ) , types . CollateralState { "xrp" , i ( 200 ) } } ,
"1.00" ,
args { ownerAddr , "xrp" , i ( - 1000 ) , i ( - 200 ) } ,
true ,
state { types . CDP { } , cs ( c ( "xrp" , 1010 ) , c ( "usdx" , 1 ) ) , i ( 0 ) , types . CollateralState { "xrp" , i ( 0 ) } } ,
} ,
{
"invalidCollateralType" ,
state { types . CDP { } , cs ( c ( "shitcoin" , 5000000 ) ) , i ( 0 ) , types . CollateralState { } } ,
"0.000001" ,
args { ownerAddr , "shitcoin" , i ( 5000000 ) , i ( 1 ) } , // ratio of 5:1
false ,
state { types . CDP { } , cs ( c ( "shitcoin" , 5000000 ) ) , i ( 0 ) , types . CollateralState { } } ,
} ,
}
for _ , tc := range tests {
t . Run ( tc . name , func ( t * testing . T ) {
// setup keeper
2019-12-03 14:35:27 +00:00
tApp := app . NewTestApp ( )
2019-11-25 19:46:02 +00:00
// initialize cdp owner account with coins
2019-12-03 14:35:27 +00:00
authGen := tApp . NewAuthGenStateFromAccounts ( [ ] sdk . AccAddress { ownerAddr } , [ ] sdk . Coins { tc . priorState . OwnerCoins } )
tApp . InitializeFromGenesisStates ( authGen )
// create a context for db access
ctx := tApp . NewContext ( false , abci . Header { } )
2019-11-25 19:46:02 +00:00
// setup store state
2019-12-03 14:35:27 +00:00
keeper := tApp . GetCDPKeeper ( )
keeper . SetParams ( ctx , defaultParamsSingle ( ) )
pricefeedKeeper := tApp . GetPriceFeedKeeper ( )
2019-11-28 16:53:59 +00:00
ap := pricefeed . Params {
2019-12-04 16:54:53 +00:00
Markets : [ ] pricefeed . Market {
pricefeed . Market {
MarketID : "xrp" , BaseAsset : "xrp" ,
2019-11-28 16:53:59 +00:00
QuoteAsset : "usd" , Oracles : pricefeed . Oracles { } , Active : true } ,
2019-11-25 19:46:02 +00:00
} ,
}
2019-12-05 15:56:24 +00:00
pricefeedKeeper . SetParams ( ctx , ap )
2019-12-03 14:35:27 +00:00
_ , err := pricefeedKeeper . SetPrice (
2019-11-25 19:46:02 +00:00
ctx , ownerAddr , "xrp" ,
sdk . MustNewDecFromStr ( tc . price ) ,
2019-12-05 15:56:24 +00:00
time . Unix ( 9999999999 , 0 ) ) // some deterministic future date
2019-11-25 19:46:02 +00:00
if err != nil {
t . Log ( "test context height" , ctx . BlockHeight ( ) )
t . Log ( err )
t . Log ( tc . name )
}
2019-12-05 15:56:24 +00:00
err = pricefeedKeeper . SetCurrentPrices ( ctx , "xrp" )
2019-11-25 19:46:02 +00:00
if err != nil {
t . Log ( "test context height" , ctx . BlockHeight ( ) )
t . Log ( err )
t . Log ( tc . name )
}
if tc . priorState . CDP . CollateralDenom != "" { // check if the prior CDP should be created or not (see if an empty one was specified)
keeper . SetCDP ( ctx , tc . priorState . CDP )
}
keeper . SetGlobalDebt ( ctx , tc . priorState . GlobalDebt )
if tc . priorState . CollateralState . Denom != "" {
2019-12-03 14:35:27 +00:00
keeper . SetCollateralState ( ctx , tc . priorState . CollateralState )
2019-11-25 19:46:02 +00:00
}
// call func under test
err = keeper . ModifyCDP ( ctx , tc . args . owner , tc . args . collateralDenom , tc . args . changeInCollateral , tc . args . changeInDebt )
// get new state for verification
actualCDP , found := keeper . GetCDP ( ctx , tc . args . owner , tc . args . collateralDenom )
// check for err
if tc . expectPass {
require . NoError ( t , err , fmt . Sprint ( err ) )
} else {
require . Error ( t , err )
}
actualGDebt := keeper . GetGlobalDebt ( ctx )
actualCstate , _ := keeper . GetCollateralState ( ctx , tc . args . collateralDenom )
// check state
require . Equal ( t , tc . expectedState . CDP , actualCDP )
if tc . expectedState . CDP . CollateralDenom == "" { // if the expected CDP is blank, then expect the CDP to have been deleted (hence not found)
require . False ( t , found )
} else {
require . True ( t , found )
}
require . Equal ( t , tc . expectedState . GlobalDebt , actualGDebt )
require . Equal ( t , tc . expectedState . CollateralState , actualCstate )
// check owner balance
2019-12-05 13:59:20 +00:00
tApp . CheckBalance ( t , ctx , ownerAddr , tc . expectedState . OwnerCoins )
2019-11-25 19:46:02 +00:00
} )
}
}
func TestKeeper_PartialSeizeCDP ( t * testing . T ) {
// Setup
const collateral = "xrp"
2019-12-03 14:35:27 +00:00
_ , addrs := app . GeneratePrivKeyAddressPairs ( 1 )
2019-11-25 19:46:02 +00:00
testAddr := addrs [ 0 ]
2019-12-03 14:35:27 +00:00
tApp := app . NewTestApp ( )
authGenState := tApp . NewAuthGenStateFromAccounts ( addrs , [ ] sdk . Coins { cs ( c ( collateral , 100 ) ) } )
tApp . InitializeFromGenesisStates ( authGenState )
ctx := tApp . NewContext ( false , abci . Header { } )
keeper := tApp . GetCDPKeeper ( )
2019-11-25 19:46:02 +00:00
// setup pricefeed
2019-12-03 14:35:27 +00:00
pricefeedKeeper := tApp . GetPriceFeedKeeper ( )
2019-11-28 16:53:59 +00:00
ap := pricefeed . Params {
2019-12-04 16:54:53 +00:00
Markets : [ ] pricefeed . Market {
pricefeed . Market {
2019-12-05 15:56:24 +00:00
MarketID : "xrp" , BaseAsset : collateral ,
2019-11-28 16:53:59 +00:00
QuoteAsset : "usd" , Oracles : pricefeed . Oracles { } , Active : true } ,
2019-11-25 19:46:02 +00:00
} ,
}
2019-12-05 15:56:24 +00:00
pricefeedKeeper . SetParams ( ctx , ap )
2019-12-03 14:35:27 +00:00
pricefeedKeeper . SetPrice (
2019-11-25 19:46:02 +00:00
ctx , sdk . AccAddress { } , collateral ,
sdk . MustNewDecFromStr ( "1.00" ) ,
2019-12-05 15:56:24 +00:00
time . Unix ( 9999999999 , 0 ) ) // some deterministic future date
pricefeedKeeper . SetCurrentPrices ( ctx , collateral )
2019-12-03 14:35:27 +00:00
2019-11-25 19:46:02 +00:00
// Create CDP
2019-12-03 14:35:27 +00:00
keeper . SetParams ( ctx , defaultParamsSingle ( ) )
2019-11-25 19:46:02 +00:00
err := keeper . ModifyCDP ( ctx , testAddr , collateral , i ( 10 ) , i ( 5 ) )
require . NoError ( t , err )
// Reduce price
2019-12-03 14:35:27 +00:00
pricefeedKeeper . SetPrice (
2019-11-25 19:46:02 +00:00
ctx , sdk . AccAddress { } , collateral ,
sdk . MustNewDecFromStr ( "0.90" ) ,
2019-12-05 15:56:24 +00:00
time . Unix ( 9999999999 , 0 ) ) // some deterministic future date
pricefeedKeeper . SetCurrentPrices ( ctx , collateral )
2019-11-25 19:46:02 +00:00
// Seize entire CDP
err = keeper . PartialSeizeCDP ( ctx , testAddr , collateral , i ( 10 ) , i ( 5 ) )
// Check
require . NoError ( t , err )
_ , found := keeper . GetCDP ( ctx , testAddr , collateral )
require . False ( t , found )
collateralState , found := keeper . GetCollateralState ( ctx , collateral )
require . True ( t , found )
require . Equal ( t , sdk . ZeroInt ( ) , collateralState . TotalDebt )
}
func TestKeeper_GetCDPs ( t * testing . T ) {
// setup keeper
2019-12-03 14:35:27 +00:00
tApp := app . NewTestApp ( )
ctx := tApp . NewContext ( true , abci . Header { } )
keeper := tApp . GetCDPKeeper ( )
2019-11-25 19:46:02 +00:00
keeper . SetParams ( ctx , defaultParamsMulti ( ) )
// setup CDPs
2019-12-03 14:35:27 +00:00
_ , addrs := app . GeneratePrivKeyAddressPairs ( 2 )
2019-11-25 19:46:02 +00:00
cdps := types . CDPs {
{ addrs [ 0 ] , "xrp" , i ( 4000 ) , i ( 5 ) } ,
{ addrs [ 1 ] , "xrp" , i ( 4000 ) , i ( 2000 ) } ,
{ addrs [ 0 ] , "btc" , i ( 10 ) , i ( 20 ) } ,
}
for _ , cdp := range cdps {
keeper . SetCDP ( ctx , cdp )
}
// Check nil params returns all CDPs
returnedCdps , err := keeper . GetCDPs ( ctx , "" , sdk . Dec { } )
require . NoError ( t , err )
require . Equal ( t ,
types . CDPs {
{ addrs [ 0 ] , "btc" , i ( 10 ) , i ( 20 ) } ,
{ addrs [ 1 ] , "xrp" , i ( 4000 ) , i ( 2000 ) } ,
{ addrs [ 0 ] , "xrp" , i ( 4000 ) , i ( 5 ) } } ,
returnedCdps ,
)
// Check correct CDPs filtered by collateral and sorted
returnedCdps , err = keeper . GetCDPs ( ctx , "xrp" , d ( "0.00000001" ) )
require . NoError ( t , err )
require . Equal ( t ,
types . CDPs {
{ addrs [ 1 ] , "xrp" , i ( 4000 ) , i ( 2000 ) } ,
{ addrs [ 0 ] , "xrp" , i ( 4000 ) , i ( 5 ) } } ,
returnedCdps ,
)
returnedCdps , err = keeper . GetCDPs ( ctx , "xrp" , sdk . Dec { } )
require . NoError ( t , err )
require . Equal ( t ,
types . CDPs {
{ addrs [ 1 ] , "xrp" , i ( 4000 ) , i ( 2000 ) } ,
{ addrs [ 0 ] , "xrp" , i ( 4000 ) , i ( 5 ) } } ,
returnedCdps ,
)
returnedCdps , err = keeper . GetCDPs ( ctx , "xrp" , d ( "0.9" ) )
require . NoError ( t , err )
require . Equal ( t ,
types . CDPs {
{ addrs [ 1 ] , "xrp" , i ( 4000 ) , i ( 2000 ) } } ,
returnedCdps ,
)
// Check high price returns no CDPs
returnedCdps , err = keeper . GetCDPs ( ctx , "xrp" , d ( "999999999.99" ) )
require . NoError ( t , err )
require . Equal ( t ,
types . CDPs ( nil ) ,
returnedCdps ,
)
// Check unauthorized collateral denom returns error
_ , err = keeper . GetCDPs ( ctx , "a non existent coin" , d ( "0.34023" ) )
require . Error ( t , err )
// Check price without collateral returns error
_ , err = keeper . GetCDPs ( ctx , "" , d ( "0.34023" ) )
require . Error ( t , err )
// Check deleting a CDP removes it
2019-12-03 14:35:27 +00:00
keeper . DeleteCDP ( ctx , cdps [ 0 ] )
2019-11-25 19:46:02 +00:00
returnedCdps , err = keeper . GetCDPs ( ctx , "" , sdk . Dec { } )
require . NoError ( t , err )
require . Equal ( t ,
types . CDPs {
{ addrs [ 0 ] , "btc" , i ( 10 ) , i ( 20 ) } ,
{ addrs [ 1 ] , "xrp" , i ( 4000 ) , i ( 2000 ) } } ,
returnedCdps ,
)
}
2019-12-03 14:35:27 +00:00
2019-11-25 19:46:02 +00:00
func TestKeeper_GetSetDeleteCDP ( t * testing . T ) {
// setup keeper, create CDP
2019-12-03 14:35:27 +00:00
tApp := app . NewTestApp ( )
ctx := tApp . NewContext ( true , abci . Header { } )
keeper := tApp . GetCDPKeeper ( )
2019-11-25 19:46:02 +00:00
keeper . SetParams ( ctx , defaultParamsSingle ( ) )
2019-12-03 14:35:27 +00:00
_ , addrs := app . GeneratePrivKeyAddressPairs ( 1 )
2019-11-25 19:46:02 +00:00
cdp := types . CDP { addrs [ 0 ] , "xrp" , i ( 412 ) , i ( 56 ) }
// write and read from store
keeper . SetCDP ( ctx , cdp )
readCDP , found := keeper . GetCDP ( ctx , cdp . Owner , cdp . CollateralDenom )
// check before and after match
require . True ( t , found )
require . Equal ( t , cdp , readCDP )
// delete auction
2019-12-03 14:35:27 +00:00
keeper . DeleteCDP ( ctx , cdp )
2019-11-25 19:46:02 +00:00
// check auction does not exist
_ , found = keeper . GetCDP ( ctx , cdp . Owner , cdp . CollateralDenom )
require . False ( t , found )
}
func TestKeeper_GetSetGDebt ( t * testing . T ) {
// setup keeper, create GDebt
2019-12-03 14:35:27 +00:00
tApp := app . NewTestApp ( )
ctx := tApp . NewContext ( true , abci . Header { } )
keeper := tApp . GetCDPKeeper ( )
2019-11-25 19:46:02 +00:00
keeper . SetParams ( ctx , defaultParamsSingle ( ) )
gDebt := i ( 4120000 )
// write and read from store
keeper . SetGlobalDebt ( ctx , gDebt )
readGDebt := keeper . GetGlobalDebt ( ctx )
// check before and after match
require . Equal ( t , gDebt , readGDebt )
}
func TestKeeper_GetSetCollateralState ( t * testing . T ) {
// setup keeper, create CState
2019-12-03 14:35:27 +00:00
tApp := app . NewTestApp ( )
ctx := tApp . NewContext ( true , abci . Header { } )
keeper := tApp . GetCDPKeeper ( )
2019-11-25 19:46:02 +00:00
keeper . SetParams ( ctx , defaultParamsSingle ( ) )
collateralState := types . CollateralState { "xrp" , i ( 15400 ) }
// write and read from store
2019-12-03 14:35:27 +00:00
keeper . SetCollateralState ( ctx , collateralState )
2019-11-25 19:46:02 +00:00
readCState , found := keeper . GetCollateralState ( ctx , collateralState . Denom )
// check before and after match
require . Equal ( t , collateralState , readCState )
require . True ( t , found )
}