packages feed

haskoin-node-2.4.0: test/unit/Haskoin/Node/Peer/LockSpec.hs

module Haskoin.Node.Peer.LockSpec (spec) where

import Haskoin.Node.Peer.Lock
import Test.Hspec
import Test.Hspec.QuickCheck (prop)
import Test.QuickCheck

-- | An attempt to reserve or release, by one of a few owners.
data Op = Acquire Int | Release Int
  deriving stock (Show)

instance Arbitrary Op where
  arbitrary = oneof [Acquire <$> owner, Release <$> owner]
    where
      owner = chooseInt (1, 3)

-- | Apply an operation, keeping the lock unchanged when it does not apply.
apply :: Lock Int -> Op -> Lock Int
apply l = \case
  Acquire o -> maybe l id (acquire o l)
  Release o -> maybe l id (release o l)

spec :: Spec
spec = describe "peer lock" $ do
  it "lets an owner reserve a free peer" $
    acquire 1 Free `shouldBe` Just (HeldBy (1 :: Int))

  it "refuses to reserve a held peer, even for its holder" $ do
    acquire 2 (HeldBy (1 :: Int)) `shouldBe` Nothing
    acquire 1 (HeldBy (1 :: Int)) `shouldBe` Nothing

  it "lets the holder release the peer" $
    release 1 (HeldBy (1 :: Int)) `shouldBe` Just Free

  it "refuses to release for anyone but the holder" $ do
    release 2 (HeldBy (1 :: Int)) `shouldBe` Nothing
    release 1 (Free :: Lock Int) `shouldBe` Nothing

  prop "only the last owner to reserve can release" $ \(ops :: [Op]) ->
    let final = foldl' apply Free ops
        model = foldl' step Nothing ops
        step Nothing (Acquire o) = Just o
        step (Just h) (Release o) | h == o = Nothing
        step m _ = m
     in holder final === model

  describe "owners" $ do
    it "are all different" $ do
      a <- newOwner "a"
      b <- newOwner "a"
      a `shouldNotBe` b
      a `shouldBe` a

    it "share the legacy owner" $
      legacyOwner `shouldBe` legacyOwner

    it "keep their names" $ do
      a <- newOwner "chain"
      ownerName a `shouldBe` "chain"
      ownerName legacyOwner `shouldBe` "legacy"