From abdd6ceaf1bf009ff2539071b1bebe0261931467 Mon Sep 17 00:00:00 2001 From: Pawel Jakubas Date: Fri, 24 Aug 2018 01:51:51 +0200 Subject: [PATCH] [CBR-366] Adding test for partial getters --- wallet-new/integration/AccountSpecs.hs | 57 +++++- wallet-new/test/unit/Test/Spec/Accounts.hs | 198 ++++++++++++++++++++- 2 files changed, 247 insertions(+), 8 deletions(-) diff --git a/wallet-new/integration/AccountSpecs.hs b/wallet-new/integration/AccountSpecs.hs index e13a58b6acd..0f22c96dbec 100644 --- a/wallet-new/integration/AccountSpecs.hs +++ b/wallet-new/integration/AccountSpecs.hs @@ -7,9 +7,11 @@ import Universum import Cardano.Wallet.API.Indices (accessIx) import Cardano.Wallet.Client.Http +import Control.Concurrent (threadDelay) import Control.Lens import Pos.Core.Common (mkCoin) import Test.Hspec +import Test.QuickCheck (arbitrary, generate, shuffle) import Util import qualified Pos.Core as Core @@ -17,7 +19,7 @@ import qualified Prelude accountSpecs :: WalletRef -> WalletClient IO -> Spec -accountSpecs _ wc = +accountSpecs wRef wc = describe "Accounts" $ do it "can retrieve only an account's balance" $ do let zero = V1 (mkCoin 0) @@ -43,6 +45,59 @@ accountSpecs _ wc = forM_ tests $ \PaginationTest{..} -> do eresp <- getAccountAddresses wc walId accIndex page perPage filters expectations . acaAddresses . wrData =<< eresp `shouldPrism` _Right + it "can retrieve initial and updated balances of several account from getAccountBalances that are equivalent to what is obtained from getAccount" $ do + genesis <- genesisWallet wc + (fromAcct, _) <- firstAccountAndId wc genesis + + wallet <- sampleWallet wRef wc + -- We create 4 accounts, plus one is created automatically + -- by the 'sampleWallet', for a total of 5. + randomNewAccount <- forM [1..4] $ \(_i :: Int) -> + generate arbitrary :: IO NewAccount + forM_ randomNewAccount $ \(rAcc :: NewAccount) -> + postAccount wc (walId wallet) rAcc + + accResp' <- getAccounts wc (walId wallet) + accs <- wrData <$> accResp' `shouldPrism` _Right + + balancesPartialResp' <- forM (map accIndex accs) $ \(accIndex :: AccountIndex) -> + getAccountBalance wc (walId wallet) accIndex + + balancesPartial <- mapM (\resp -> wrData <$> resp `shouldPrism` _Right) balancesPartialResp' + + map (AccountBalance . accAmount) accs `shouldBe` balancesPartial + + -- Now transfering money to 5 accounts from genesis wallet and checking balances once again + let payment amount toAddr = Payment + { pmtSource = PaymentSource + { psWalletId = walId genesis + , psAccountIndex = accIndex fromAcct + } + , pmtDestinations = pure PaymentDistribution + { pdAddress = addrId toAddr + , pdAmount = V1 (Core.mkCoin amount) + } + , pmtGroupingPolicy = Nothing + , pmtSpendingPassword = Nothing + } + amounts <- generate $ shuffle [1..5] + let addrAndAmount = zip (map (\(addr : _) -> addr) $ map accAddresses accs) amounts + forM_ addrAndAmount $ \(addr, amount) -> + postTransaction wc (payment amount addr) + + threadDelay 120000000 + + accUpdatedResp' <- getAccounts wc (walId wallet) + accsUpdated <- wrData <$> accUpdatedResp' `shouldPrism` _Right + + balancesPartialUpdatedResp' <- forM (map accIndex accsUpdated) $ + \(accIndex :: AccountIndex) -> getAccountBalance wc (walId wallet) accIndex + + balancesPartialUpdated <- + mapM (\resp -> wrData <$> resp `shouldPrism` _Right) balancesPartialUpdatedResp' + + map (AccountBalance . accAmount) accsUpdated `shouldBe` balancesPartialUpdated + where filterByAddress :: WalletAddress -> FilterOperations '[V1 Address] WalletAddress filterByAddress addr = diff --git a/wallet-new/test/unit/Test/Spec/Accounts.hs b/wallet-new/test/unit/Test/Spec/Accounts.hs index 1680d5d330c..db0c022cb6d 100644 --- a/wallet-new/test/unit/Test/Spec/Accounts.hs +++ b/wallet-new/test/unit/Test/Spec/Accounts.hs @@ -12,24 +12,25 @@ import qualified Test.Spec.Wallets as Wallets import Formatting (build, formatToString, (%)) -import Cardano.Wallet.Kernel.Accounts (CreateAccountError (..)) -import qualified Cardano.Wallet.Kernel.DB.HdWallet as Kernel -import qualified Cardano.Wallet.Kernel.Internal as Internal -import qualified Cardano.Wallet.Kernel.Keystore as Keystore -import Cardano.Wallet.WalletLayer (PassiveWalletLayer) -import qualified Cardano.Wallet.WalletLayer as WalletLayer - import qualified Cardano.Wallet.API.Request as API import qualified Cardano.Wallet.API.Request.Pagination as API import qualified Cardano.Wallet.API.Response as API import Cardano.Wallet.API.V1.Handlers.Accounts as Handlers import Cardano.Wallet.API.V1.Types (V1 (..)) import qualified Cardano.Wallet.API.V1.Types as V1 +import Cardano.Wallet.Kernel.Accounts (CreateAccountError (..)) +import qualified Cardano.Wallet.Kernel.DB.HdWallet as Kernel import qualified Cardano.Wallet.Kernel.DB.Util.IxSet as IxSet +import qualified Cardano.Wallet.Kernel.Internal as Internal +import qualified Cardano.Wallet.Kernel.Keystore as Keystore +import Cardano.Wallet.WalletLayer (PassiveWalletLayer) +import qualified Cardano.Wallet.WalletLayer as WalletLayer import qualified Cardano.Wallet.WalletLayer.Kernel.Wallets as Wallets import Control.Monad.Except (runExceptT) import Servant.Server +import Pos.Core.Common (mkCoin) + import Test.Spec.Fixture (GenPassiveWalletFixture, genSpendingPassword, withLayer, withPassiveWalletFixture) import Util.Buildable (ShowThroughBuild (..)) @@ -367,3 +368,186 @@ spec = describe "Accounts" $ do case res of Left e -> fail (show e) Right wr -> (length $ API.wrData wr) `shouldBe` 5 + + + describe "GetAccountAddresses" $ do + + prop "fails if the account doesn't exists" $ withMaxSuccess 50 $ do + monadicIO $ do + withFixture $ \_ layer _ Fixture{..} -> do + let params = API.RequestParams (API.PaginationParams (API.Page 1) (API.PerPage 10)) + let filters = API.NoFilters + res <- WalletLayer.getAccountAddresses layer + (V1.walId fixtureV1Wallet) + (V1.unsafeMkAccountIndex 2147483648) + params + filters + case res of + Left (WalletLayer.GetAccountError (V1 (Kernel.UnknownHdAccount _))) -> + return () + Left unexpectedErr -> + fail $ "expecting different failure than " <> show unexpectedErr + Right _ -> + let errMsg = "expecting account not to be retrieved, but it was. random WalletId " + % build + % " , V1.Wallet " + in fail $ formatToString errMsg (V1.walId fixtureV1Wallet) + + + prop "applied to each newly created accounts gives addresses as obtained from GetAccounts" $ withMaxSuccess 25 $ do + monadicIO $ do + withFixture $ \_ layer _ Fixture{..} -> do + -- We create 4 accounts, plus one is created automatically + -- by the 'createWallet' endpoint, for a total of 5. + forM_ [1..4] $ \(_i :: Int) -> + WalletLayer.createAccount layer (V1.walId fixtureV1Wallet) + fixtureNewAccountRq + accounts <- WalletLayer.getAccounts layer (V1.walId fixtureV1Wallet) + let accountIndices = + case accounts of + Left _ -> [] + Right accs -> map V1.accIndex $ IxSet.toList accs + let params = API.RequestParams (API.PaginationParams (API.Page 1) (API.PerPage 10)) + let filters = API.NoFilters + partialAddresses <- forM accountIndices $ \(ind :: V1.AccountIndex) -> + WalletLayer.getAccountAddresses layer (V1.walId fixtureV1Wallet) ind params filters + case accounts of + Right accs -> (map V1.accAddresses $ IxSet.toList accs) + `shouldBe` + (map (\(Right addr) -> API.wrData addr) partialAddresses) + Left err -> fail (show err) + + + prop "and this also works when called from Servant" $ withMaxSuccess 25 $ do + monadicIO $ do + withFixture $ \_ layer _ Fixture{..} -> do + let create = Handlers.newAccount layer (V1.walId fixtureV1Wallet) fixtureNewAccountRq + -- We create 4 accounts, plus one is created automatically + -- by the 'createWallet' endpoint, for a total of 5. + forM_ [1..4] $ \(_i :: Int) -> runExceptT . runHandler' $ create + let params = API.RequestParams (API.PaginationParams (API.Page 1) (API.PerPage 10)) + let fetchForAccounts = Handlers.listAccounts layer (V1.walId fixtureV1Wallet) params + accounts <- runExceptT . runHandler' $ fetchForAccounts + let accountIndices = + case accounts of + Left _ -> [] + Right accs -> map V1.accIndex $ API.wrData accs + let reqParams = API.RequestParams (API.PaginationParams (API.Page 1) (API.PerPage 10)) + let filters = API.NoFilters + let fetchForAccountAddresses ind = + Handlers.getAccountAddresses layer (V1.walId fixtureV1Wallet) + ind reqParams filters + partialAddresses <- forM accountIndices $ \(ind :: V1.AccountIndex) -> + runExceptT . runHandler' $ fetchForAccountAddresses ind + case accounts of + Right accs -> (map V1.accAddresses $ API.wrData accs) + `shouldBe` + (map (\(Right bal) -> (V1.acaAddresses . API.wrData) bal) partialAddresses) + Left err -> fail (show err) + + + prop "applied to accounts that were just updated via address creation is the same as obtained from GetAccounts" $ withMaxSuccess 25 $ do + monadicIO $ do + withFixture $ \_ layer _ Fixture{..} -> do + -- We create 4 accounts, plus one is created automatically + -- by the 'createWallet' endpoint, for a total of 5. + forM_ [1..4] $ \(_i :: Int) -> + WalletLayer.createAccount layer (V1.walId fixtureV1Wallet) + fixtureNewAccountRq + accountsBefore <- WalletLayer.getAccounts layer (V1.walId fixtureV1Wallet) + let accountIndices = + case accountsBefore of + Left _ -> [] + Right accs -> map V1.accIndex $ IxSet.toList accs + forM_ accountIndices $ \(accIdx :: V1.AccountIndex) -> + WalletLayer.createAddress layer (V1.NewAddress Nothing accIdx (V1.walId fixtureV1Wallet)) + accountsUpdated <- WalletLayer.getAccounts layer (V1.walId fixtureV1Wallet) + let params = API.RequestParams (API.PaginationParams (API.Page 1) (API.PerPage 10)) + let filters = API.NoFilters + partialAddresses <- forM accountIndices $ \(ind :: V1.AccountIndex) -> + WalletLayer.getAccountAddresses layer (V1.walId fixtureV1Wallet) ind params filters + case accountsUpdated of + Right accs -> (map V1.accAddresses $ IxSet.toList accs) + `shouldBe` + (map (\(Right addr) -> API.wrData addr) partialAddresses) + Left err -> fail (show err) + + + describe "GetAccountBalance" $ do + + prop "gives zero balance for newly created account" $ withMaxSuccess 25 $ do + monadicIO $ do + withFixture $ \_ layer _ Fixture{..} -> do + let zero = V1 (mkCoin 0) + (Right V1.Account{..}) <- + WalletLayer.createAccount layer (V1.walId fixtureV1Wallet) + fixtureNewAccountRq + res <- WalletLayer.getAccountBalance layer (V1.walId fixtureV1Wallet) + accIndex + case res of + Left e -> fail (show e) + Right balance -> balance `shouldBe` V1.AccountBalance zero + + prop "fails if the account doesn't exists" $ withMaxSuccess 50 $ do + monadicIO $ do + withFixture $ \_ layer _ Fixture{..} -> do + res <- WalletLayer.getAccountBalance layer + (V1.walId fixtureV1Wallet) + (V1.unsafeMkAccountIndex 2147483648) + case res of + Left (WalletLayer.GetAccountError (V1 (Kernel.UnknownHdAccount _))) -> + return () + Left unexpectedErr -> + fail $ "expecting different failure than " <> show unexpectedErr + Right _ -> + let errMsg = "expecting account not to be retrieved, but it was. random WalletId " + % build + % " , V1.Wallet " + in fail $ formatToString errMsg (V1.walId fixtureV1Wallet) + + + + prop "applied to each newly created account gives balances as obtained from GetAccounts" $ withMaxSuccess 25 $ do + monadicIO $ do + withFixture $ \_ layer _ Fixture{..} -> do + -- We create 4 accounts, plus one is created automatically + -- by the 'createWallet' endpoint, for a total of 5. + forM_ [1..4] $ \(_i :: Int) -> + WalletLayer.createAccount layer (V1.walId fixtureV1Wallet) + fixtureNewAccountRq + accounts <- WalletLayer.getAccounts layer (V1.walId fixtureV1Wallet) + let accountIndices = + case accounts of + Left _ -> [] + Right accs -> map V1.accIndex $ IxSet.toList accs + partialBalances <- forM accountIndices $ \(ind :: V1.AccountIndex) -> + WalletLayer.getAccountBalance layer (V1.walId fixtureV1Wallet) ind + case (accounts, length partialBalances /= 5) of + (Right accs, False) -> (map (V1.AccountBalance . V1.accAmount) $ IxSet.toList accs) + `shouldBe` + (map (\(Right bal) -> bal) partialBalances) + _ -> fail "expecting to get 5 balances from partial getters" + + + prop "and this also works when called from Servant" $ withMaxSuccess 25 $ do + monadicIO $ do + withFixture $ \_ layer _ Fixture{..} -> do + let create = Handlers.newAccount layer (V1.walId fixtureV1Wallet) fixtureNewAccountRq + -- We create 4 accounts, plus one is created automatically + -- by the 'createWallet' endpoint, for a total of 5. + forM_ [1..4] $ \(_i :: Int) -> runExceptT . runHandler' $ create + let params = API.RequestParams (API.PaginationParams (API.Page 1) (API.PerPage 10)) + let fetchForAccounts = Handlers.listAccounts layer (V1.walId fixtureV1Wallet) params + accounts <- runExceptT . runHandler' $ fetchForAccounts + let accountIndices = + case accounts of + Left _ -> [] + Right accs -> map V1.accIndex $ API.wrData accs + let fetchForAccountBalance = Handlers.getAccountBalance layer (V1.walId fixtureV1Wallet) + partialBalances <- forM accountIndices $ \(ind :: V1.AccountIndex) -> + runExceptT . runHandler' $ fetchForAccountBalance ind + case (accounts, length partialBalances /= 5) of + (Right accs, False) -> (map (V1.AccountBalance . V1.accAmount) $ API.wrData accs) + `shouldBe` + (map (\(Right bal) -> API.wrData bal) partialBalances) + _ -> fail "expecting to get 5 balances from partial getters"