packages feed

haskoin-store-data-1.9.0: test/Haskoin/Store/WebCommonSpec.hs

{-# OPTIONS_GHC -Wno-orphans #-}

module Haskoin.Store.WebCommonSpec (spec) where

import Data.ByteString qualified as B
import Data.Maybe (fromJust)
import Data.Proxy (Proxy (..))
import Data.String.Conversions (cs)
import Data.Text (Text)
import Data.Word (Word64)
import Haskoin
import Haskoin.Store.Data (DeriveType (..))
import Haskoin.Store.Gen (arbitraryDeriveType)
import Haskoin.Store.WebCommon
import Haskoin.Util.Arbitrary
import Test.Hspec
import Test.Hspec.QuickCheck (prop)
import Test.QuickCheck

spec :: Spec
spec = prepareContext $ \ctx -> do
  -- each case on a random network: they differ in their address prefixes
  describe "Parameters" $ do
    roundTrip ctx arbitraryAddress
    roundTrip ctx (listOf . arbitraryAddress)
    roundTrip ctx (const (arbitrary :: Gen StartParam))
    roundTrip ctx (const (arbitrarySizedNatural :: Gen OffsetParam))
    roundTrip ctx (const (arbitrarySizedNatural :: Gen LimitParam))
    roundTrip ctx (const (arbitrarySizedNatural :: Gen HeightParam))
    roundTrip ctx (const (arbitrary :: Gen HeightsParam))
    roundTrip ctx (const (arbitrarySizedNatural :: Gen TimeParam))
    roundTrip ctx (const (arbitraryXPubKey ctx))
    roundTrip ctx arbitraryDeriveType
    roundTrip ctx (const (NoCache <$> arbitrary))
    roundTrip ctx (const (NoTx <$> arbitrary))
    roundTrip ctx (const (listOf arbitraryBlockHash))
    roundTrip ctx (const (listOf arbitraryTxHash))

  describe "Start of a page" $ do
    it "is a transaction hash, a height up to 1230768000, or a later time" $ do
      parse ["1230768000"] `shouldBe` Just (StartParamHeight 1230768000)
      parse ["1230768001"] `shouldBe` Just (StartParamTime 1230768001)
      parse [txHashToHex z] `shouldBe` Just (StartParamHash z.get)
    it "refuses anything else" $
      parse ["yesterday"] `shouldBe` Nothing

  describe "Derivation" $
    it "is only normal on networks without SegWit" $ do
      encodeParam bch ctx DeriveP2WPKH `shouldBe` Nothing
      encodeParam btc ctx DeriveP2WPKH `shouldBe` Just ["segwit"]

  describe "Paths" $ do
    let addr = fromJust (textToAddr btc "1111111111111111111114oLvT2")
        path :: (ApiResource a b) => a -> String
        path = cs . queryPath btc ctx
    it "of a block" $ do
      path (GetBlockBest (NoTx False)) `shouldBe` "/block/best"
      path (GetBlockHeight (HeightParam 5) (NoTx False)) `shouldBe` "/block/height/5"
    it "of a transaction" $
      path (GetTx z) `shouldBe` "/transaction/" <> cs (txHashToHex z)
    it "of an address" $
      path (GetAddrBalance addr) `shouldBe` "/address/1111111111111111111114oLvT2/balance"
    prop "of an xpub" $
      forAll (arbitraryXPubKey ctx) $ \x ->
        path (GetXPubBalances x DeriveNormal (NoCache False))
          `shouldBe` "/xpub/" <> cs (xPubExport btc ctx x) <> "/balances"
  where
    z = TxHash (sha256 B.empty)
    parse = parseParam btc undefined :: [Text] -> Maybe StartParam

-- | A parameter comes back from its encoding in a URL, on a random network.
roundTrip :: forall a. (Eq a, Show a, Param a) => Ctx -> (Network -> Gen a) -> Spec
roundTrip ctx g =
  prop (cs (proxyLabel (Proxy :: Proxy a))) . forAll (elements allNets) $ \net ->
    counterexample net.name . forAll (g net) $ \p ->
      (encodeParam net ctx p >>= parseParam net ctx) `shouldBe` Just p

instance Arbitrary StartParam where
  arbitrary =
    oneof
      [ StartParamHash <$> arbitraryHash256,
        StartParamHeight . fromIntegral <$> (choose (0, 1230768000) :: Gen Word64),
        StartParamTime <$> choose (1230768001, maxBound)
      ]

instance Arbitrary HeightsParam where
  arbitrary = HeightsParam <$> listOf arbitrarySizedNatural