packages feed

langchain-hs-0.0.6.0: test/Test/Langchain/Tool/ProcessSpec.hs

{-# LANGUAGE OverloadedStrings #-}

module Test.Langchain.Tool.ProcessSpec (tests) where

import Data.Aeson (object, (.=))
import Data.Text (Text)
import qualified Data.Text as T
import Langchain.Tool (toolExecute)
import Langchain.Tool.Process
  ( commandKillTool
  , commandRunTool
  , commandStatusTool
  , newProcessManager
  )
import Langchain.Tool.Security (defaultSecurityPolicy)
import Test.Tasty
import Test.Tasty.HUnit

tests :: TestTree
tests =
  testGroup
    "Langchain.Tool.Process"
    [ testCase "commandRunTool executes short command synchronously" $ do
        pm <- newProcessManager
        let policy = defaultSecurityPolicy
        res <-
          toolExecute
            (commandRunTool pm policy)
            (object ["command" .= ("echo 'Hello from Process!'" :: Text), "timeout_ms" .= (2000 :: Int)])
        case res of
          Left err -> assertFailure $ "Command failed: " ++ show err
          Right txt -> do
            assertBool "Contains command output" ("Hello from Process!" `T.isInfixOf` txt)
            assertBool "Indicates exit code 0" ("exit code 0" `T.isInfixOf` txt)
    , testCase "commandRunTool transitions long command to background after timeout" $ do
        pm <- newProcessManager
        let policy = defaultSecurityPolicy
        -- Run sleep 2 with 200ms timeout
        res <-
          toolExecute
            (commandRunTool pm policy)
            (object ["command" .= ("sleep 2" :: Text), "timeout_ms" .= (200 :: Int)])
        case res of
          Left err -> assertFailure $ "Command launch failed: " ++ show err
          Right txt -> do
            assertBool
              "Mentions background execution"
              ("background" `T.isInfixOf` txt || "proc-" `T.isInfixOf` txt)

            -- Check status using commandStatusTool
            sRes <-
              toolExecute
                (commandStatusTool pm policy)
                (object ["process_id" .= ("proc-1" :: Text)])
            case sRes of
              Left err -> assertFailure $ "Status check failed: " ++ show err
              Right sTxt -> assertBool "Has process status output" ("proc-1" `T.isInfixOf` sTxt)

            -- Kill background process
            kRes <-
              toolExecute
                (commandKillTool pm policy)
                (object ["process_id" .= ("proc-1" :: Text)])
            case kRes of
              Left err -> assertFailure $ "Kill failed: " ++ show err
              Right kTxt -> assertBool "Terminated successfully" ("terminated" `T.isInfixOf` T.toLower kTxt)
    ]