packages feed

adhoc-fixtures-hspec-0.1.0.0: test/Data/Fixtures/Adhoc/HspecSpec.hs

{-# OPTIONS_GHC -Wno-redundant-constraints #-}

module Data.Fixtures.Adhoc.HspecSpec (main, spec) where

import Control.Exception.Safe (bracket)
import Data.Fixtures.Adhoc
import Data.Fixtures.Adhoc.Hspec
import Data.IORef
import GHC.Exts (IsString (..))
import Test.Hspec

main :: IO ()
main = hspec spec

spec :: Spec
spec =
  describe "Adhoc fixtures" $
    aroundAll (withIORef @[(Key, Int)] mempty) $ do
      it "Tracker should be empty (start blank)" $ \tracker ->
        readIORef tracker `shouldReturn` []
      aroundAllWith (withFixtureAppendLift @"tracker" @_ @"box" boxFixture) $ do
        it "Tracker should have one key (added)" $ \fixture ->
          readIORef fixture.tracker `shouldReturn` [("box00", 42)]
        aroundAllWith (withFixtureAppend @"book" bookFixture) $ do
          it "Tracker should have two keys (added)" $ \fixture ->
            readIORef fixture.tracker `shouldReturn` [("book00", 42), ("box00", 42)]
        it "Tracker should have one key (cleaned)" $ \fixture ->
          readIORef fixture.tracker `shouldReturn` [("box00", 42)]
      it "Tracker should be empty (start cleaned)" $ \tracker ->
        readIORef tracker `shouldReturn` []

newtype Key = Key {getKey :: String}
  deriving stock (Eq, Ord, Show)
  deriving newtype (IsString)

data Box = Box
  { boxId :: Int,
    boxKey :: Key
  }
  deriving stock (Eq, Show)

data Book = Book
  { bookId :: Int,
    bookBox :: Int,
    bookKey :: Key
  }
  deriving stock (Eq, Show)

type Tracker = IORef [(Key, Int)]

addId :: Key -> Int -> Tracker -> IO ()
addId k i t = modifyIORef' t ((k, i) :)

rmId :: Key -> Tracker -> IO ()
rmId k t = modifyIORef' t $ filter ((/= k) . fst)

unsafeGetId :: Key -> Tracker -> IO Int
unsafeGetId k t = snd . head . filter ((== k) . fst) <$> readIORef t

boxFixture ::
  HasFixture items "tracker" Tracker =>
  BuilderWith items IO "box" Box
boxFixture =
  buildWithClean
    (\prev -> let box = Box 42 "box00" in addId box.boxKey box.boxId prev.tracker >> return box)
    (\prev box -> rmId box.boxKey prev.tracker)

bookFixture ::
  (HasFixture items "tracker" Tracker, HasFixture items "box" Box) =>
  BuilderWith items IO "book" Book
bookFixture =
  buildWithClean
    ( \prev -> do
        box <- unsafeGetId "book00" prev.tracker
        let book = Book 42 box "book00"
        addId book.bookKey book.bookId prev.tracker
        return book
    )
    (\prev book -> rmId book.bookKey prev.tracker)

withIORef :: a -> (IORef a -> IO ()) -> IO ()
withIORef x =
  bracket
    (newIORef x)
    (const $ return ())