haskoin-store-0.23.0: test/Haskoin/Store/CommonSpec.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Haskoin.Store.CommonSpec
( spec
) where
import Control.Monad (join)
import Data.Aeson (FromJSON, ToJSON)
import qualified Data.Aeson as A
import qualified Data.ByteString.Short as BSS
import Data.Serialize (Serialize (..), decode, encode)
import Data.String.Conversions (cs)
import Haskoin (Network, bch, bchRegTest, bchTest,
btc, btcRegTest, btcTest)
import Haskoin.Store.Common (Balance (..), BlockData (..),
BlockRef (..), BlockTx (..),
DeriveType (..), Event (..),
HealthCheck (..), NetWrap (..),
PeerInformation (..), Prev (..),
PubExcept (..), Spender (..),
StoreInput (..), StoreOutput (..),
Transaction (..), TxAfterHeight (..),
TxData (..), TxId (..), Unspent (..),
XPubBal (..), XPubSpec (..),
XPubSummary (..), XPubUnspent (..))
import Network.Haskoin.Test (arbitraryAddress, arbitraryBlockHash,
arbitraryBlockHeader,
arbitraryOutPoint,
arbitraryRejectCode, arbitraryScript,
arbitraryTx, arbitraryTxHash,
arbitraryXPubKey)
import NQE ()
import Test.Hspec (Expectation, Spec, describe, shouldBe)
import Test.Hspec.QuickCheck (prop)
import Test.QuickCheck (Arbitrary (..), Gen,
arbitraryPrintableChar,
arbitraryUnicodeChar, elements,
forAll, listOf, listOf1, oneof)
spec :: Spec
spec = do
describe "Binary serialization" $ do
prop "identity for derivation type" $ \x -> testSerial (x :: DeriveType)
prop "identity for xpub spec" $ \x -> testSerial (x :: XPubSpec)
prop "identity for block ref" $ \x -> testSerial (x :: BlockRef)
prop "identity for block tx" $ \x -> testSerial (x :: BlockTx)
prop "identity for balance" $ \x -> testSerial (x :: Balance)
prop "identity for unspent" $ \x -> testSerial (x :: Unspent)
prop "identity for block data" $ \x -> testSerial (x :: BlockData)
prop "identity for input" $ \x -> testSerial (x :: StoreInput)
prop "identity for spender" $ \x -> testSerial (x :: Spender)
prop "identity for output" $ \x -> testSerial (x :: StoreOutput)
prop "identity for previous output" $ \x -> testSerial (x :: Prev)
prop "identity for tx data" $ \x -> testSerial (x :: TxData)
prop "identity for transaction" $ \x -> testSerial (x :: Transaction)
prop "identity for xpub balance" $ \x -> testSerial (x :: XPubBal)
prop "identity for xpub unspent" $ \x -> testSerial (x :: XPubUnspent)
prop "identity for xpub summary" $ \x -> testSerial (x :: XPubSummary)
prop "identity for health check" $ \x -> testSerial (x :: HealthCheck)
prop "identity for event" $ \x -> testSerial (x :: Event)
prop "identity for tx after height" $ \x ->
testSerial (x :: TxAfterHeight)
prop "identity for txid" $ \x -> testSerial (x :: TxId)
prop "identity for publish exception" $ \x ->
testSerial (x :: PubExcept)
prop "identity for peer info" $ \x -> testSerial (x :: PeerInformation)
describe "JSON serialization" $ do
prop "identity for balance" . forAll arbitraryNetData $ \(net, x) ->
testNetJSON2 net (x :: Balance)
prop "identity for block tx" $ \x -> testJSON (x :: BlockTx)
prop "identity for block ref" $ \x -> testJSON (x :: BlockRef)
prop "identity for unspent" $ \x -> testJSON (x :: Unspent)
prop "identity for block data" $ \x -> testJSON (x :: BlockData)
prop "identity for spender" $ \x -> testJSON (x :: Spender)
prop "identity for transaction" . forAll arbitraryNetData $ \(net, x) ->
testNetJSON1 net (x :: Transaction)
prop "identity for xpub summary" $ \x -> testJSON (x :: XPubSummary)
prop "identity for health check" $ \x -> testJSON (x :: HealthCheck)
prop "identity for event" $ \x -> testJSON (x :: Event)
prop "identity for txid" $ \x -> testJSON (x :: TxId)
prop "identity for tx after height" $ \x ->
testJSON (x :: TxAfterHeight)
prop "identity for peer information" $ \x ->
testJSON (x :: PeerInformation)
arbitraryNetData :: Arbitrary a => Gen (Network, a)
arbitraryNetData = do
net <- arbitraryNetwork
x <- arbitrary
return (net, x)
testJSON :: (Eq a, Show a, ToJSON a, FromJSON a) => a -> Expectation
testJSON input = (A.decode . A.encode) input `shouldBe` Just input
testNetJSON1 ::
(Eq a, Show a, ToJSON (NetWrap a), FromJSON a) => Network -> a -> Expectation
testNetJSON1 net x =
let encoded = A.encode (NetWrap net x)
decoded = A.decode encoded
in decoded `shouldBe` Just x
testNetJSON2 ::
(Eq a, Show a, ToJSON (NetWrap a), FromJSON (Network -> Maybe a))
=> Network
-> a
-> Expectation
testNetJSON2 net x =
let encoded = A.encode (NetWrap net x)
decoder = A.decode encoded
in join (($ net) <$> decoder) `shouldBe` Just x
testSerial :: (Eq a, Show a, Serialize a) => a -> Expectation
testSerial input = (decode . encode) input `shouldBe` Right input
instance Arbitrary TxAfterHeight where
arbitrary = TxAfterHeight <$> arbitrary
instance Arbitrary DeriveType where
arbitrary = elements [DeriveNormal, DeriveP2SH, DeriveP2WPKH]
instance Arbitrary XPubSpec where
arbitrary = do
(_, k) <- arbitraryXPubKey
t <- arbitrary
return XPubSpec {xPubSpecKey = k, xPubDeriveType = t}
instance Arbitrary XPubBal where
arbitrary = XPubBal <$> arbitrary <*> arbitrary
instance Arbitrary XPubUnspent where
arbitrary = XPubUnspent <$> arbitrary <*> arbitrary
instance Arbitrary Event where
arbitrary =
oneof [EventBlock <$> arbitraryBlockHash, EventTx <$> arbitraryTxHash]
instance Arbitrary XPubSummary where
arbitrary =
XPubSummary
<$> arbitrary
<*> arbitrary
<*> arbitrary
<*> arbitrary
<*> arbitrary
<*> arbitrary
instance Arbitrary BlockRef where
arbitrary = oneof [br, mr]
where
br = BlockRef <$> arbitrary <*> arbitrary
mr = MemRef <$> arbitrary
instance Arbitrary BlockTx where
arbitrary = do
br <- arbitrary
th <- arbitraryTxHash
return BlockTx { blockTxBlock = br, blockTxHash = th}
arbitraryNetwork :: Gen Network
arbitraryNetwork = elements [bch, btc, bchTest, btcTest, bchRegTest, btcRegTest]
instance Arbitrary Balance where
arbitrary =
Balance
<$> arbitraryAddress
<*> arbitrary
<*> arbitrary
<*> arbitrary
<*> arbitrary
<*> arbitrary
instance Arbitrary Unspent where
arbitrary =
Unspent
<$> arbitrary
<*> arbitraryOutPoint
<*> arbitrary
<*> (BSS.toShort . encode <$> arbitraryScript)
<*> arbitrary
instance Arbitrary BlockData where
arbitrary =
BlockData
<$> arbitrary
<*> arbitrary
<*> arbitrary
<*> arbitraryBlockHeader
<*> arbitrary
<*> arbitrary
<*> listOf1 arbitraryTxHash
<*> arbitrary
<*> arbitrary
<*> arbitrary
instance Arbitrary StoreInput where
arbitrary = oneof [cb, si]
where
cb = do
st <- map encode <$> listOf arbitraryScript
ws <- elements [Just st, Nothing]
StoreCoinbase
<$> arbitraryOutPoint
<*> arbitrary
<*> (encode <$> arbitraryScript)
<*> pure ws
si = do
st <- map encode <$> listOf arbitraryScript
ws <- elements [Just st, Nothing]
StoreInput
<$> arbitraryOutPoint
<*> arbitrary
<*> (encode <$> arbitraryScript)
<*> (encode <$> arbitraryScript)
<*> arbitrary
<*> pure ws
instance Arbitrary Spender where
arbitrary = Spender <$> arbitraryTxHash <*> arbitrary
instance Arbitrary Prev where
arbitrary = Prev <$> (encode <$> arbitraryScript) <*> arbitrary
instance Arbitrary StoreOutput where
arbitrary =
StoreOutput
<$> arbitrary
<*> (encode <$> arbitraryScript)
<*> arbitrary
instance Arbitrary TxData where
arbitrary =
TxData
<$> arbitrary
<*> (arbitraryTx =<< arbitraryNetwork)
<*> arbitrary
<*> arbitrary
<*> arbitrary
<*> arbitrary
instance Arbitrary Transaction where
arbitrary =
Transaction
<$> arbitrary
<*> arbitrary
<*> arbitrary
<*> arbitrary
<*> arbitrary
<*> arbitrary
<*> arbitrary
<*> arbitrary
instance Arbitrary PeerInformation where
arbitrary = do
PeerInformation
<$> (cs <$> listOf arbitraryUnicodeChar)
<*> listOf arbitraryPrintableChar
<*> arbitrary
<*> arbitrary
<*> arbitrary
instance Arbitrary HealthCheck where
arbitrary = do
bh <- arbitraryBlockHash
hh <- arbitraryBlockHash
let mb = elements [Nothing, Just bh]
mh = elements [Nothing, Just hh]
HealthCheck
<$> mb
<*> arbitrary
<*> mh
<*> arbitrary
<*> arbitrary
<*> arbitrary
<*> arbitrary
<*> arbitrary
<*> arbitrary
<*> arbitrary
instance Arbitrary PubExcept where
arbitrary =
oneof
[ pure PubNoPeers
, PubReject <$> arbitraryRejectCode
, pure PubTimeout
, pure PubPeerDisconnected
]
instance Arbitrary TxId where
arbitrary = TxId <$> arbitraryTxHash