packages feed

langchain-hs-0.0.5.0: test/Test/Langchain/Resilience/CircuitBreakerSpec.hs

{-# LANGUAGE OverloadedStrings #-}

module Test.Langchain.Resilience.CircuitBreakerSpec (tests) where

import Control.Monad.Except (runExceptT, throwError)
import Test.Tasty
import Test.Tasty.HUnit

import Langchain.Core.Error (internalError)
import Langchain.Resilience.CircuitBreaker

tests :: TestTree
tests =
  testGroup
    "Langchain.Resilience.CircuitBreakerSpec"
    [ testCase "CircuitBreaker starts in Closed state and passes successful requests" $ do
        cb <- newCircuitBreaker "test-cb" defaultCircuitConfig
        st <- getCircuitState cb
        st @?= CircuitClosed
        res <- runExceptT $ withCircuitBreaker cb (pure ("ok" :: String))
        res @?= Right "ok"
    , testCase "CircuitBreaker transitions to Open after exceeding failure threshold" $ do
        let cfg = CircuitBreakerConfig {failureThreshold = 2, resetTimeoutSec = 0.1}
        cb <- newCircuitBreaker "failing-cb" cfg
        -- First failure
        _ <- runExceptT $ withCircuitBreaker cb (throwError (internalError "fail 1" Nothing Nothing))
        st1 <- getCircuitState cb
        st1 @?= CircuitClosed
        -- Second failure -> should open
        _ <- runExceptT $ withCircuitBreaker cb (throwError (internalError "fail 2" Nothing Nothing))
        st2 <- getCircuitState cb
        case st2 of
          CircuitOpen _ -> pure ()
          _ -> assertFailure "Expected CircuitOpen state"
        -- Third request fast-fails without executing action
        resFastFail <- runExceptT $ withCircuitBreaker cb (pure ("should not execute" :: String))
        case resFastFail of
          Left _ -> pure ()
          Right _ -> assertFailure "Expected circuit breaker fast-fail error"
    ]