packages feed

tower-hs-0.2.0.0: test/Tower/Middleware/HedgeSpec.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE NumericUnderscores #-}

module Tower.Middleware.HedgeSpec (spec) where

import Control.Concurrent (threadDelay)
import Data.IORef
import Test.Hspec

import Tower.Service
import Tower.Error
import Tower.Error.Testing ()
import Data.Function ((&))
import Tower.Middleware.Hedge

spec :: Spec
spec = describe "Hedge middleware" $ do
  it "returns result from fast primary without hedging" $ do
    let svc :: Service () String
        svc = Service $ \_ -> pure (Right "fast")
        hedged = svc & withHedge 1000
    result <- runService hedged ()
    result `shouldBe` Right "fast"

  it "returns a result when primary is slow (hedge may win)" $ do
    callCount <- newIORef (0 :: Int)
    let svc :: Service () String
        svc = Service $ \_ -> do
          n <- atomicModifyIORef' callCount (\c -> (c + 1, c))
          if n == 0
            then do
              threadDelay 500_000  -- primary: slow
              pure (Right "primary")
            else pure (Right "hedge")  -- hedge: instant
        hedged = svc & withHedge 50
    result <- runService hedged ()
    case result of
      Right _ -> pure ()
      Left err -> expectationFailure $ "Expected Right, got: " ++ show err

  it "calls service at most twice" $ do
    callCount <- newIORef (0 :: Int)
    let svc :: Service () String
        svc = Service $ \_ -> do
          modifyIORef' callCount (+ 1)
          threadDelay 100_000
          pure (Right "done")
        hedged = svc & withHedge 50
    _ <- runService hedged ()
    threadDelay 200_000
    count <- readIORef callCount
    count `shouldSatisfy` (<= 2)