Skip to content
This repository has been archived by the owner on Mar 1, 2019. It is now read-only.

Commit

Permalink
Merge pull request input-output-hk/cardano-sl#3563 from input-output-…
Browse files Browse the repository at this point in the history
…hk/kderme/CBR-371

[CBR-371] unit tests for listing addresses
  • Loading branch information
adinapoli-iohk authored Sep 17, 2018
2 parents ce03fdb + 6171fda commit 9f30cef
Show file tree
Hide file tree
Showing 3 changed files with 184 additions and 6 deletions.
8 changes: 4 additions & 4 deletions src/Cardano/Wallet/WalletLayer/Kernel/Addresses.hs
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ createAddress wallet
-- 1 2 .. 100
-- <deletion of an account happens, leaving a hole in the indices>
--
-- / /
-- +-----+-----+----------/---------------/-------------------+------+
-- | A1 | A2 | ... X \/ | A100 |
-- / /
-- +-----+-----+---------/--------------/---------------------+------+
-- | A1 | A2 | ... X X | A100 |
-- | | | \ \ | |
-- +-----+-----+----------\--------------\--------------------+------+
-- 1 2 .. 10 \ \ 30 .. 100
Expand Down Expand Up @@ -149,7 +149,7 @@ takeIndexed db n acc (currentIndex, (a:as))
new = map (toV1 a) . sortBy autoKey . IxSet.toList $ slice
in -- For the next iterations, the index will always be 0 as we
-- are hopping from one ixset to the other, collecting addresses.
takeIndexed db (n - IxSet.size slice) (new <> acc) (0, as)
takeIndexed db (n - IxSet.size slice) (acc <> new) (0, as)
where
toV1 :: HD.HdAccount -> Indexed HD.HdAddress -> V1.WalletAddress
toV1 hdAccount ixed = toAddress hdAccount (ixed ^. ixedIndexed)
Expand Down
180 changes: 179 additions & 1 deletion test/unit/Test/Spec/Addresses.hs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Control.Monad.Except (runExceptT)
import Data.Acid (update)
import qualified Data.ByteString as B
import qualified Data.Map.Strict as M
import qualified Data.Set as S
import Formatting (build, sformat)
import Servant.Server

Expand All @@ -16,7 +17,7 @@ import Test.QuickCheck (arbitrary, choose, elements, withMaxSuccess,
(===))
import Test.QuickCheck.Monadic (PropertyM, monadicIO, pick)

import Pos.Core (Address)
import Pos.Core (Address, addrRoot)
import Pos.Crypto (EncryptedSecretKey, emptyPassphrase, firstHardened,
safeDeterministicKeyGen)

Expand All @@ -37,13 +38,15 @@ import Cardano.Wallet.Kernel.DB.HdWallet.Create (initHdRoot)
import Cardano.Wallet.Kernel.DB.HdWallet.Derivation
(HardeningMode (..), deriveIndex)
import Cardano.Wallet.Kernel.DB.InDb (InDb (..), fromDb)
import qualified Cardano.Wallet.Kernel.DB.Util.IxSet as IxSet
import Cardano.Wallet.Kernel.Internal (PassiveWallet, wallets)
import qualified Cardano.Wallet.Kernel.Keystore as Keystore
import qualified Cardano.Wallet.Kernel.Read as Kernel
import Cardano.Wallet.Kernel.Types (AccountId (..), WalletId (..))
import qualified Cardano.Wallet.Kernel.Wallets as Kernel
import Cardano.Wallet.WalletLayer (PassiveWalletLayer)
import qualified Cardano.Wallet.WalletLayer as WalletLayer
import qualified Cardano.Wallet.WalletLayer.Kernel.Accounts as Accounts
import qualified Cardano.Wallet.WalletLayer.Kernel.Addresses as Addresses
import qualified Cardano.Wallet.WalletLayer.Kernel.Conv as Kernel.Conv
import qualified Cardano.Wallet.WalletLayer.Kernel.Wallets as Wallets
Expand Down Expand Up @@ -123,6 +126,38 @@ prepareAddressFixture n = do
let SliceOf{..} = Addresses.getAddresses (RequestParams pp) db'
return . map AddressFixture $ paginatedSlice

prepareAddressesFixture
:: Int -- ^ Number of Accounts to create.
-> Int -- ^ Number of 'Address per account to create.
-> Fixture.GenPassiveWalletFixture (M.Map V1.AccountIndex [V1.WalletAddress])
prepareAddressesFixture acn adn = do
spendingPassword <- Fixture.genSpendingPassword
newWalletRq <- WalletLayer.CreateWallet <$> Wallets.genNewWalletRq spendingPassword
return $ \pw -> do
let newAcc (n :: Int) = (V1.NewAccount spendingPassword ("My Account " <> show n))
Right v1Wallet <- Wallets.createWallet pw newWalletRq
forM_ [1..acn] $ \n ->
Accounts.createAccount pw (V1.walId v1Wallet) (newAcc n)
-- Get all the available accounts
db <- Kernel.getWalletSnapshot pw
let Right accs = Accounts.getAccounts (V1.walId v1Wallet) db
let accounts = IxSet.toList accs
length accounts `shouldBe` (acn + 1)
let insertAddresses :: V1.Account -> IO (V1.AccountIndex, [V1.WalletAddress])
insertAddresses acc = do
let accId = V1.accIndex acc
let newAddressRq = V1.NewAddress spendingPassword accId (V1.walId v1Wallet)
res <- replicateM adn (Addresses.createAddress pw newAddressRq)
case sequence res of
Left e -> error (show e)
Right addr -> return (accId, addr)
res <- mapM insertAddresses accounts
return $ M.fromList res

expectedNumber :: Int -> Int -> Int
expectedNumber acc adr = (acc + 1)*(adr + 1) - acc


withFixture :: ( Keystore.Keystore
-> PassiveWalletLayer IO
-> PassiveWallet
Expand All @@ -145,6 +180,18 @@ withAddressFixtures n =
Fixture.withPassiveWalletFixture $ do
prepareAddressFixture n

withAddressesFixtures :: Int -> Int ->
( Keystore.Keystore
-> PassiveWalletLayer IO
-> PassiveWallet
-> M.Map V1.AccountIndex [V1.WalletAddress]
-> IO a
)
-> PropertyM IO a
withAddressesFixtures n m =
Fixture.withPassiveWalletFixture $ do
prepareAddressesFixture n m

spec :: Spec
spec = describe "Addresses" $ do
describe "CreateAddress" $ do
Expand Down Expand Up @@ -348,6 +395,137 @@ spec = describe "Addresses" $ do
slice rNumOfPages rNumPerPage fixtureAddresses
pure (toBeCheckedAddresses === correctAddresses)

describe "Address listing with multiple Accounts (Servant)" $ do
prop "page 0, per page 0" $ withMaxSuccess 20 $ do
monadicIO $
withAddressesFixtures 4 4 $ \_ layer _ _ -> do
let pp = PaginationParams (Page 0) (PerPage 0)
res <- runExceptT $ runHandler' $ do
Handlers.listAddresses layer (RequestParams pp)
case res of
Right wr | null (wrData wr) -> pure ()
_ -> fail ("Got " ++ show res)

prop "it yields the correct number of results" $ withMaxSuccess 20 $ do
monadicIO $
withAddressesFixtures 3 4 $ \_ layer _ _ -> do
let pp = PaginationParams (Page 1) (PerPage 40)
res <- runExceptT $ runHandler' $ do
Handlers.listAddresses layer (RequestParams pp)
case res of
Right wr -> do
-- this takes into account that there is an initial account
-- and each account has an initial address (but not the initial
-- account thus the -1)
length (wrData wr) `shouldBe` expectedNumber 3 4
_ -> fail ("Got " ++ show res)

prop "is deterministic" $ withMaxSuccess 20 $ do
monadicIO $
withAddressesFixtures 3 8 $ \_ layer _ _ -> do
let (expectedTotal :: Int) = expectedNumber 3 8
let pp = PaginationParams (Page 1) (PerPage 40)
let pp1 = PaginationParams (Page 1) (PerPage (quot expectedTotal 3 + 1))
let pp2 = PaginationParams (Page 2) (PerPage (quot expectedTotal 3 + 1))
let pp3 = PaginationParams (Page 3) (PerPage (quot expectedTotal 3 + 1))
res <- runExceptT $ runHandler' $ do
Handlers.listAddresses layer (RequestParams pp)
res' <- runExceptT $ runHandler' $ do
Handlers.listAddresses layer (RequestParams pp)
res1 <- runExceptT $ runHandler' $ do
Handlers.listAddresses layer (RequestParams pp1)
res1' <- runExceptT $ runHandler' $ do
Handlers.listAddresses layer (RequestParams pp1)
res2 <- runExceptT $ runHandler' $ do
Handlers.listAddresses layer (RequestParams pp2)
res2' <- runExceptT $ runHandler' $ do
Handlers.listAddresses layer (RequestParams pp2)
res3 <- runExceptT $ runHandler' $ do
Handlers.listAddresses layer (RequestParams pp3)
res3' <- runExceptT $ runHandler' $ do
Handlers.listAddresses layer (RequestParams pp3)
res `shouldBe` res'
res1 `shouldBe` res1'
res2 `shouldBe` res2'
res3 `shouldBe` res3'

prop "yields the correct set of resutls" $ withMaxSuccess 20 $ do
monadicIO $
withAddressesFixtures 4 8 $ \_ layer _ _ -> do
let (expectedTotal :: Int) = expectedNumber 4 8
let pp = PaginationParams (Page 1) (PerPage 50)
let pp1 = PaginationParams (Page 1) (PerPage (quot expectedTotal 3 + 1))
let pp2 = PaginationParams (Page 2) (PerPage (quot expectedTotal 3 + 1))
let pp3 = PaginationParams (Page 3) (PerPage (quot expectedTotal 3 + 1))
res <- runExceptT $ runHandler' $ do
Handlers.listAddresses layer (RequestParams pp)
res1 <- runExceptT $ runHandler' $ do
Handlers.listAddresses layer (RequestParams pp1)
res2 <- runExceptT $ runHandler' $ do
Handlers.listAddresses layer (RequestParams pp2)
res3 <- runExceptT $ runHandler' $ do
Handlers.listAddresses layer (RequestParams pp3)
case (res, res1, res2, res3) of
(Right wr, Right wr1, Right wr2, Right wr3) -> do
length (wrData wr) `shouldBe` expectedTotal
let con = wrData wr1 <> wrData wr2 <> wrData wr3
length con `shouldBe` expectedTotal
S.fromList con `shouldBe` S.fromList (wrData wr)
(addrRoot . V1.unV1 . V1.addrId <$> con)
`shouldBe` (addrRoot . V1.unV1 . V1.addrId <$> wrData wr)
_ -> fail ("Got " ++ show res)

prop "yields the correct ordered resutls when there is one account" $ withMaxSuccess 20 $ do
monadicIO $
withAddressesFixtures 0 15 $ \_ layer _ _ -> do
let (expectedTotal :: Int) = expectedNumber 0 15
let pp = PaginationParams (Page 1) (PerPage 50)
let pp1 = PaginationParams (Page 1) (PerPage (quot expectedTotal 3 + 1))
let pp2 = PaginationParams (Page 2) (PerPage (quot expectedTotal 3 + 1))
let pp3 = PaginationParams (Page 3) (PerPage (quot expectedTotal 3 + 1))
res <- runExceptT $ runHandler' $ do
Handlers.listAddresses layer (RequestParams pp)
res1 <- runExceptT $ runHandler' $ do
Handlers.listAddresses layer (RequestParams pp1)
res2 <- runExceptT $ runHandler' $ do
Handlers.listAddresses layer (RequestParams pp2)
res3 <- runExceptT $ runHandler' $ do
Handlers.listAddresses layer (RequestParams pp3)
case (res, res1, res2, res3) of
(Right wr, Right wr1, Right wr2, Right wr3) -> do
length (wrData wr) `shouldBe` expectedTotal
let con = wrData wr1 <> wrData wr2 <> wrData wr3
length con `shouldBe` expectedTotal
S.fromList con `shouldBe` S.fromList (wrData wr)
(addrRoot . V1.unV1 . V1.addrId <$> con)
`shouldBe` (addrRoot . V1.unV1 . V1.addrId <$> wrData wr)
_ -> fail ("Got " ++ show res)


prop "yields the correct ordered resutls" $ withMaxSuccess 20 $ do
monadicIO $ do
forM_ [(4,8), (6,6), (5,7)] $ \(acc,adr) ->
withAddressesFixtures acc adr $ \_ layer _ _ -> do
forM_ [2..10] $ \k -> do
let indexes = [1..k]
let (expectedTotal :: Int) = expectedNumber acc adr
let pagesParams = map (\i -> PaginationParams (Page i) (PerPage (quot expectedTotal k + 1)))
indexes
let pp = PaginationParams (Page 1) (PerPage 50)
res <- runExceptT $ runHandler' $ do
Handlers.listAddresses layer (RequestParams pp)
eiResultsArray <- forM pagesParams $ \ppi -> runExceptT $ runHandler' $ do
Handlers.listAddresses layer (RequestParams ppi)
let resultsArray = sequence eiResultsArray
case (res, resultsArray) of
(Right wr, Right wrList) -> do
let con = mconcat $ map wrData wrList
length (wrData wr) `shouldBe` expectedTotal
length con `shouldBe` expectedTotal
S.fromList con `shouldBe` S.fromList (wrData wr)
(addrRoot . V1.unV1 . V1.addrId <$> con)
`shouldBe` (addrRoot . V1.unV1 . V1.addrId <$> wrData wr)
_ -> fail ("Got " ++ show res)

describe "ValidateAddress" $ do
describe "Address validation (wallet layer)" $ do
Expand Down
2 changes: 1 addition & 1 deletion test/unit/WalletUnitTest.hs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ _showContext = do

tests :: Spec
tests = parallel $ describe "Wallet unit tests" $ do
Test.Spec.Addresses.spec
DeltaCompressionSpecs.spec
Test.Spec.Kernel.spec
Test.Spec.GetTransactions.spec
Expand All @@ -80,7 +81,6 @@ tests = parallel $ describe "Wallet unit tests" $ do
txMetaStorageSpecs
Test.Spec.CoinSelection.spec
Test.Spec.Keystore.spec
Test.Spec.Addresses.spec
Test.Spec.Wallets.spec
Test.Spec.NewPayment.spec
Test.Spec.Accounts.spec

0 comments on commit 9f30cef

Please sign in to comment.