651 lines
22 KiB
Haskell
651 lines
22 KiB
Haskell
{-# LANGUAGE OverloadedStrings #-}
|
|
|
|
import Control.Concurrent (forkIO, threadDelay)
|
|
import Control.Exception (SomeException, throwIO, try)
|
|
import Control.Monad (when)
|
|
import Control.Monad.Logger (runNoLoggingT)
|
|
import Data.Aeson
|
|
import qualified Data.ByteString as BS
|
|
import Data.Configurator
|
|
import Data.Maybe (fromMaybe)
|
|
import qualified Data.Text as T
|
|
import qualified Data.Text.Encoding as E
|
|
import Data.Time.Clock (getCurrentTime)
|
|
import qualified Data.UUID as U
|
|
import Network.HTTP.Simple
|
|
import Network.Wai.Handler.Warp (run)
|
|
import Servant
|
|
import System.Directory
|
|
import Test.HUnit hiding (State)
|
|
import Test.Hspec
|
|
import ZcashHaskell.Orchard (isValidUnifiedAddress)
|
|
import ZcashHaskell.Types
|
|
( ZcashNet(..)
|
|
, ZebraGetBlockChainInfo(..)
|
|
, ZebraGetInfo(..)
|
|
)
|
|
import Zenith.Core (checkBlockChain, checkZebra)
|
|
import Zenith.DB (Operation(..), initDb, initPool, saveOperation)
|
|
import Zenith.RPC
|
|
( RpcCall(..)
|
|
, State(..)
|
|
, ZenithInfo(..)
|
|
, ZenithMethod(..)
|
|
, ZenithParams(..)
|
|
, ZenithRPC(..)
|
|
, ZenithResponse(..)
|
|
, authenticate
|
|
, zenithServer
|
|
)
|
|
import Zenith.Types
|
|
( Config(..)
|
|
, ZcashAccountAPI(..)
|
|
, ZcashAddressAPI(..)
|
|
, ZcashWalletAPI(..)
|
|
, ZenithStatus(..)
|
|
, ZenithUuid(..)
|
|
)
|
|
|
|
main :: IO ()
|
|
main = do
|
|
config <- load ["$(HOME)/Zenith/zenith.cfg"]
|
|
let dbFilePath = "test.db"
|
|
nodeUser <- require config "nodeUser"
|
|
nodePwd <- require config "nodePwd"
|
|
zebraPort <- require config "zebraPort"
|
|
zebraHost <- require config "zebraHost"
|
|
nodePort <- require config "nodePort"
|
|
let myConfig = Config dbFilePath zebraHost zebraPort nodeUser nodePwd nodePort
|
|
hspec $ do
|
|
describe "RPC methods" $ do
|
|
beforeAll_ (startAPI myConfig) $ do
|
|
describe "getinfo" $ do
|
|
it "bad credentials" $ do
|
|
res <-
|
|
makeZenithCall
|
|
"127.0.0.1"
|
|
nodePort
|
|
"baduser"
|
|
"idontknow"
|
|
GetInfo
|
|
BlankParams
|
|
res `shouldBe` Left "Invalid credentials"
|
|
it "correct credentials" $ do
|
|
res <-
|
|
makeZenithCall
|
|
"127.0.0.1"
|
|
nodePort
|
|
nodeUser
|
|
nodePwd
|
|
GetInfo
|
|
BlankParams
|
|
case res of
|
|
Left e -> assertFailure e
|
|
Right r ->
|
|
r `shouldBe`
|
|
InfoResponse "zh" (ZenithInfo "0.7.0.0-beta" TestNet "v1.9.0")
|
|
describe "Wallets" $ do
|
|
describe "listwallet" $ do
|
|
it "bad credentials" $ do
|
|
res <-
|
|
makeZenithCall
|
|
"127.0.0.1"
|
|
nodePort
|
|
"baduser"
|
|
"idontknow"
|
|
ListWallets
|
|
BlankParams
|
|
res `shouldBe` Left "Invalid credentials"
|
|
it "correct credentials, no wallet" $ do
|
|
res <-
|
|
makeZenithCall
|
|
"127.0.0.1"
|
|
nodePort
|
|
nodeUser
|
|
nodePwd
|
|
ListWallets
|
|
BlankParams
|
|
case res of
|
|
Left e -> assertFailure e
|
|
Right r ->
|
|
r `shouldBe`
|
|
ErrorResponse
|
|
"zh"
|
|
(-32001)
|
|
"No wallets available. Please create one first"
|
|
describe "getnewwallet" $ do
|
|
it "bad credentials" $ do
|
|
res <-
|
|
makeZenithCall
|
|
"127.0.0.1"
|
|
nodePort
|
|
"baduser"
|
|
"idontknow"
|
|
GetNewWallet
|
|
BlankParams
|
|
res `shouldBe` Left "Invalid credentials"
|
|
describe "correct credentials" $ do
|
|
it "no params" $ do
|
|
res <-
|
|
makeZenithCall
|
|
"127.0.0.1"
|
|
nodePort
|
|
nodeUser
|
|
nodePwd
|
|
GetNewWallet
|
|
BlankParams
|
|
case res of
|
|
Left e -> assertFailure e
|
|
Right r ->
|
|
r `shouldBe` ErrorResponse "zh" (-32602) "Invalid params"
|
|
it "Valid params" $ do
|
|
res <-
|
|
makeZenithCall
|
|
"127.0.0.1"
|
|
nodePort
|
|
nodeUser
|
|
nodePwd
|
|
GetNewWallet
|
|
(NameParams "Main")
|
|
case res of
|
|
Left e -> assertFailure e
|
|
Right r -> r `shouldBe` NewItemResponse "zh" 1
|
|
it "duplicate name" $ do
|
|
res <-
|
|
makeZenithCall
|
|
"127.0.0.1"
|
|
nodePort
|
|
nodeUser
|
|
nodePwd
|
|
GetNewWallet
|
|
(NameParams "Main")
|
|
case res of
|
|
Left e -> assertFailure e
|
|
Right r ->
|
|
r `shouldBe`
|
|
ErrorResponse
|
|
"zh"
|
|
(-32007)
|
|
"Entity with that name already exists."
|
|
describe "listwallet" $ do
|
|
it "wallet exists" $ do
|
|
res <-
|
|
makeZenithCall
|
|
"127.0.0.1"
|
|
nodePort
|
|
nodeUser
|
|
nodePwd
|
|
ListWallets
|
|
BlankParams
|
|
case res of
|
|
Left e -> assertFailure e
|
|
Right (WalletListResponse i k) ->
|
|
zw_name (head k) `shouldBe` "Main"
|
|
Right _ -> assertFailure "Unexpected response"
|
|
describe "Accounts" $ do
|
|
describe "listaccounts" $ do
|
|
it "bad credentials" $ do
|
|
res <-
|
|
makeZenithCall
|
|
"127.0.0.1"
|
|
nodePort
|
|
"baduser"
|
|
"idontknow"
|
|
ListAccounts
|
|
BlankParams
|
|
res `shouldBe` Left "Invalid credentials"
|
|
describe "correct credentials" $ do
|
|
it "invalid wallet" $ do
|
|
res <-
|
|
makeZenithCall
|
|
"127.0.0.1"
|
|
nodePort
|
|
nodeUser
|
|
nodePwd
|
|
ListAccounts
|
|
(AccountsParams 17)
|
|
case res of
|
|
Left e -> assertFailure e
|
|
Right r ->
|
|
r `shouldBe`
|
|
ErrorResponse "zh" (-32008) "Wallet does not exist."
|
|
it "valid wallet, no accounts" $ do
|
|
res <-
|
|
makeZenithCall
|
|
"127.0.0.1"
|
|
nodePort
|
|
nodeUser
|
|
nodePwd
|
|
ListAccounts
|
|
(AccountsParams 1)
|
|
case res of
|
|
Left e -> assertFailure e
|
|
Right r ->
|
|
r `shouldBe`
|
|
ErrorResponse
|
|
"zh"
|
|
(-32002)
|
|
"No accounts available for this wallet. Please create one first"
|
|
describe "getnewaccount" $ do
|
|
it "invalid credentials" $ do
|
|
res <-
|
|
makeZenithCall
|
|
"127.0.0.1"
|
|
nodePort
|
|
"baduser"
|
|
"idontknow"
|
|
GetNewAccount
|
|
BlankParams
|
|
res `shouldBe` Left "Invalid credentials"
|
|
describe "correct credentials" $ do
|
|
it "invalid wallet" $ do
|
|
res <-
|
|
makeZenithCall
|
|
"127.0.0.1"
|
|
nodePort
|
|
nodeUser
|
|
nodePwd
|
|
GetNewAccount
|
|
(NameIdParams "Personal" 17)
|
|
case res of
|
|
Left e -> assertFailure e
|
|
Right r ->
|
|
r `shouldBe`
|
|
ErrorResponse "zh" (-32008) "Wallet does not exist."
|
|
it "valid wallet" $ do
|
|
res <-
|
|
makeZenithCall
|
|
"127.0.0.1"
|
|
nodePort
|
|
nodeUser
|
|
nodePwd
|
|
GetNewAccount
|
|
(NameIdParams "Personal" 1)
|
|
case res of
|
|
Left e -> assertFailure e
|
|
Right r -> r `shouldBe` NewItemResponse "zh" 1
|
|
it "valid wallet, duplicate name" $ do
|
|
res <-
|
|
makeZenithCall
|
|
"127.0.0.1"
|
|
nodePort
|
|
nodeUser
|
|
nodePwd
|
|
GetNewAccount
|
|
(NameIdParams "Personal" 1)
|
|
case res of
|
|
Left e -> assertFailure e
|
|
Right r ->
|
|
r `shouldBe`
|
|
ErrorResponse
|
|
"zh"
|
|
(-32007)
|
|
"Entity with that name already exists."
|
|
describe "listaccounts" $ do
|
|
it "valid wallet" $ do
|
|
res <-
|
|
makeZenithCall
|
|
"127.0.0.1"
|
|
nodePort
|
|
nodeUser
|
|
nodePwd
|
|
ListAccounts
|
|
(AccountsParams 1)
|
|
case res of
|
|
Left e -> assertFailure e
|
|
Right r ->
|
|
r `shouldBe`
|
|
AccountListResponse "zh" [ZcashAccountAPI 1 1 "Personal"]
|
|
describe "Addresses" $ do
|
|
describe "listaddresses" $ do
|
|
it "bad credentials" $ do
|
|
res <-
|
|
makeZenithCall
|
|
"127.0.0.1"
|
|
nodePort
|
|
"baduser"
|
|
"idontknow"
|
|
ListAddresses
|
|
BlankParams
|
|
res `shouldBe` Left "Invalid credentials"
|
|
it "correct credentials, no addresses" $ do
|
|
res <-
|
|
makeZenithCall
|
|
"127.0.0.1"
|
|
nodePort
|
|
nodeUser
|
|
nodePwd
|
|
ListAddresses
|
|
(AddressesParams 1)
|
|
case res of
|
|
Left e -> assertFailure e
|
|
Right r ->
|
|
r `shouldBe`
|
|
ErrorResponse
|
|
"zh"
|
|
(-32003)
|
|
"No addresses available for this account. Please create one first"
|
|
describe "getnewaddress" $ do
|
|
it "bad credentials" $ do
|
|
res <-
|
|
makeZenithCall
|
|
"127.0.0.1"
|
|
nodePort
|
|
"baduser"
|
|
"idontknow"
|
|
GetNewAddress
|
|
BlankParams
|
|
res `shouldBe` Left "Invalid credentials"
|
|
describe "correct credentials" $ do
|
|
it "invalid account" $ do
|
|
res <-
|
|
makeZenithCall
|
|
"127.0.0.1"
|
|
nodePort
|
|
nodeUser
|
|
nodePwd
|
|
GetNewAddress
|
|
(NewAddrParams 17 "Business" False False)
|
|
case res of
|
|
Left e -> assertFailure e
|
|
Right r ->
|
|
r `shouldBe`
|
|
ErrorResponse "zh" (-32006) "Account does not exist."
|
|
it "valid account" $ do
|
|
res <-
|
|
makeZenithCall
|
|
"127.0.0.1"
|
|
nodePort
|
|
nodeUser
|
|
nodePwd
|
|
GetNewAddress
|
|
(NewAddrParams 1 "Business" False False)
|
|
case res of
|
|
Left e -> assertFailure e
|
|
Right (NewAddrResponse i a) -> zd_name a `shouldBe` "Business"
|
|
Right _ -> assertFailure "unexpected response"
|
|
it "valid account, duplicate name" $ do
|
|
res <-
|
|
makeZenithCall
|
|
"127.0.0.1"
|
|
nodePort
|
|
nodeUser
|
|
nodePwd
|
|
GetNewAddress
|
|
(NewAddrParams 1 "Business" False False)
|
|
case res of
|
|
Left e -> assertFailure e
|
|
Right r ->
|
|
r `shouldBe`
|
|
ErrorResponse
|
|
"zh"
|
|
(-32007)
|
|
"Entity with that name already exists."
|
|
it "valid account, no sapling" $ do
|
|
res <-
|
|
makeZenithCall
|
|
"127.0.0.1"
|
|
nodePort
|
|
nodeUser
|
|
nodePwd
|
|
GetNewAddress
|
|
(NewAddrParams 1 "NoSapling" True False)
|
|
case res of
|
|
Left e -> assertFailure e
|
|
Right (NewAddrResponse i a) -> zd_legacy a `shouldBe` Nothing
|
|
Right _ -> assertFailure "unexpected response"
|
|
it "valid account, no transparent" $ do
|
|
res <-
|
|
makeZenithCall
|
|
"127.0.0.1"
|
|
nodePort
|
|
nodeUser
|
|
nodePwd
|
|
GetNewAddress
|
|
(NewAddrParams 1 "NoTransparent" False True)
|
|
case res of
|
|
Left e -> assertFailure e
|
|
Right (NewAddrResponse i a) ->
|
|
zd_transparent a `shouldBe` Nothing
|
|
Right _ -> assertFailure "unexpected response"
|
|
it "valid account, orchard only" $ do
|
|
res <-
|
|
makeZenithCall
|
|
"127.0.0.1"
|
|
nodePort
|
|
nodeUser
|
|
nodePwd
|
|
GetNewAddress
|
|
(NewAddrParams 1 "OrchOnly" True True)
|
|
case res of
|
|
Left e -> assertFailure e
|
|
Right (NewAddrResponse i a) ->
|
|
a `shouldSatisfy`
|
|
(\b ->
|
|
(zd_transparent b == Nothing) && (zd_legacy b == Nothing))
|
|
Right _ -> assertFailure "unexpected response"
|
|
describe "listaddresses" $ do
|
|
it "correct credentials, addresses exist" $ do
|
|
res <-
|
|
makeZenithCall
|
|
"127.0.0.1"
|
|
nodePort
|
|
nodeUser
|
|
nodePwd
|
|
ListAddresses
|
|
(AddressesParams 1)
|
|
case res of
|
|
Left e -> assertFailure e
|
|
Right (AddressListResponse i a) -> length a `shouldBe` 4
|
|
describe "Notes" $ do
|
|
describe "listreceived" $ do
|
|
it "bad credentials" $ do
|
|
res <-
|
|
makeZenithCall
|
|
"127.0.0.1"
|
|
nodePort
|
|
"baduser"
|
|
"idontknow"
|
|
ListReceived
|
|
BlankParams
|
|
res `shouldBe` Left "Invalid credentials"
|
|
describe "correct credentials" $ do
|
|
it "no parameters" $ do
|
|
res <-
|
|
makeZenithCall
|
|
"127.0.0.1"
|
|
nodePort
|
|
nodeUser
|
|
nodePwd
|
|
ListReceived
|
|
BlankParams
|
|
case res of
|
|
Left e -> assertFailure e
|
|
Right (ErrorResponse i c m) -> c `shouldBe` (-32602)
|
|
it "unknown index" $ do
|
|
res <-
|
|
makeZenithCall
|
|
"127.0.0.1"
|
|
nodePort
|
|
nodeUser
|
|
nodePwd
|
|
ListReceived
|
|
(NotesParams "17")
|
|
case res of
|
|
Left e -> assertFailure e
|
|
Right (ErrorResponse i c m) -> c `shouldBe` (-32004)
|
|
describe "Balance" $ do
|
|
describe "getbalance" $ do
|
|
it "bad credentials" $ do
|
|
res <-
|
|
makeZenithCall
|
|
"127.0.0.1"
|
|
nodePort
|
|
"baduser"
|
|
"idontknow"
|
|
GetBalance
|
|
BlankParams
|
|
res `shouldBe` Left "Invalid credentials"
|
|
describe "correct credentials" $ do
|
|
it "no parameters" $ do
|
|
res <-
|
|
makeZenithCall
|
|
"127.0.0.1"
|
|
nodePort
|
|
nodeUser
|
|
nodePwd
|
|
GetBalance
|
|
BlankParams
|
|
case res of
|
|
Left e -> assertFailure e
|
|
Right (ErrorResponse i c m) -> c `shouldBe` (-32602)
|
|
it "unknown index" $ do
|
|
res <-
|
|
makeZenithCall
|
|
"127.0.0.1"
|
|
nodePort
|
|
nodeUser
|
|
nodePwd
|
|
GetBalance
|
|
(BalanceParams 17)
|
|
case res of
|
|
Left e -> assertFailure e
|
|
Right (ErrorResponse i c m) -> c `shouldBe` (-32006)
|
|
describe "Operations" $ do
|
|
describe "getoperationstatus" $ do
|
|
it "bad credentials" $ do
|
|
res <-
|
|
makeZenithCall
|
|
"127.0.0.1"
|
|
nodePort
|
|
"baduser"
|
|
"idontknow"
|
|
GetOperationStatus
|
|
BlankParams
|
|
res `shouldBe` Left "Invalid credentials"
|
|
describe "correct credentials" $ do
|
|
it "invalid ID" $ do
|
|
res <-
|
|
makeZenithCall
|
|
"127.0.0.1"
|
|
nodePort
|
|
nodeUser
|
|
nodePwd
|
|
GetOperationStatus
|
|
(NameParams "badId")
|
|
case res of
|
|
Left e -> assertFailure e
|
|
Right (ErrorResponse i c m) -> c `shouldBe` (-32602)
|
|
it "valid ID" $ do
|
|
res <-
|
|
makeZenithCall
|
|
"127.0.0.1"
|
|
nodePort
|
|
nodeUser
|
|
nodePwd
|
|
GetOperationStatus
|
|
(OpParams
|
|
(ZenithUuid $
|
|
fromMaybe U.nil $
|
|
U.fromText "bd2aa95a-db51-4cc4-9fea-0f9cf79003a4"))
|
|
case res of
|
|
Left e -> assertFailure e
|
|
Right (OpResponse i o) ->
|
|
operationUuid o `shouldBe`
|
|
(ZenithUuid $
|
|
fromMaybe U.nil $
|
|
U.fromText "bd2aa95a-db51-4cc4-9fea-0f9cf79003a4")
|
|
Right _ -> assertFailure "unexpected response"
|
|
it "valid ID not found" $ do
|
|
res <-
|
|
makeZenithCall
|
|
"127.0.0.1"
|
|
nodePort
|
|
nodeUser
|
|
nodePwd
|
|
GetOperationStatus
|
|
(OpParams
|
|
(ZenithUuid $
|
|
fromMaybe U.nil $
|
|
U.fromText "bd2aa95a-db51-4cc4-9fea-0f9cf79003a5"))
|
|
case res of
|
|
Left e -> assertFailure e
|
|
Right (ErrorResponse i c m) -> c `shouldBe` (-32009)
|
|
Right _ -> assertFailure "unexpected response"
|
|
|
|
startAPI :: Config -> IO ()
|
|
startAPI config = do
|
|
putStrLn "Starting test RPC server"
|
|
checkDbFile <- doesFileExist "test.db"
|
|
when checkDbFile $ removeFile "test.db"
|
|
let ctx = authenticate config :. EmptyContext
|
|
w <-
|
|
try $ checkZebra (c_zebraHost config) (c_zebraPort config) :: IO
|
|
(Either IOError ZebraGetInfo)
|
|
case w of
|
|
Right zebra -> do
|
|
bc <-
|
|
try $ checkBlockChain (c_zebraHost config) (c_zebraPort config) :: IO
|
|
(Either IOError ZebraGetBlockChainInfo)
|
|
case bc of
|
|
Left e1 -> throwIO e1
|
|
Right chainInfo -> do
|
|
x <- initDb "test.db"
|
|
case x of
|
|
Left e2 -> throwIO $ userError e2
|
|
Right x' -> do
|
|
pool <- runNoLoggingT $ initPool "test.db"
|
|
ts <- getCurrentTime
|
|
y <-
|
|
saveOperation
|
|
pool
|
|
(Operation
|
|
(ZenithUuid $
|
|
fromMaybe U.nil $
|
|
U.fromText "bd2aa95a-db51-4cc4-9fea-0f9cf79003a4")
|
|
ts
|
|
Nothing
|
|
Processing
|
|
Nothing)
|
|
let myState =
|
|
State
|
|
(zgb_net chainInfo)
|
|
(c_zebraHost config)
|
|
(c_zebraPort config)
|
|
"test.db"
|
|
(zgi_build zebra)
|
|
(zgb_blocks chainInfo)
|
|
forkIO $
|
|
run (c_zenithPort config) $
|
|
serveWithContext
|
|
(Servant.Proxy :: Servant.Proxy ZenithRPC)
|
|
ctx
|
|
(zenithServer myState)
|
|
threadDelay 1000000
|
|
putStrLn "Test server is up!"
|
|
|
|
-- | Make a Zebra RPC call
|
|
makeZenithCall ::
|
|
T.Text -- ^ Hostname for `zebrad`
|
|
-> Int -- ^ Port for `zebrad`
|
|
-> BS.ByteString
|
|
-> BS.ByteString
|
|
-> ZenithMethod -- ^ RPC method to call
|
|
-> ZenithParams -- ^ List of parameters
|
|
-> IO (Either String ZenithResponse)
|
|
makeZenithCall host port usr pwd m params = do
|
|
let payload = RpcCall "2.0" "zh" m params
|
|
let myRequest =
|
|
setRequestBodyJSON payload $
|
|
setRequestPort port $
|
|
setRequestHost (E.encodeUtf8 host) $
|
|
setRequestBasicAuth usr pwd $ setRequestMethod "POST" defaultRequest
|
|
r <- httpJSONEither myRequest
|
|
case getResponseStatusCode r of
|
|
403 -> return $ Left "Invalid credentials"
|
|
200 ->
|
|
case getResponseBody r of
|
|
Left e -> return $ Left $ show e
|
|
Right r' -> return $ Right r'
|
|
e -> return $ Left $ show e ++ show (getResponseBody r)
|