mcp-server-0.1.0.9: test/Spec/AdvancedDerivation.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
module Spec.AdvancedDerivation (spec) where
import Control.Monad (forM_)
import Data.Text (Text)
import qualified Data.Text as T
import MCP.Server
import MCP.Server.Derive
import Test.Hspec
import TestTypes
import TestData
-- Generate advanced handlers for separate parameter types
testSeparateParamsToolHandlers :: (ToolListHandler IO, ToolCallHandler IO)
testSeparateParamsToolHandlers = $(deriveToolHandler ''SeparateParamsTool 'handleSeparateParamsTool)
testRecursiveToolHandlers :: (ToolListHandler IO, ToolCallHandler IO)
testRecursiveToolHandlers = $(deriveToolHandler ''RecursiveTool 'handleRecursiveTool)
testSeparateParamsToolHandlersWithDescriptions :: (ToolListHandler IO, ToolCallHandler IO)
testSeparateParamsToolHandlersWithDescriptions = $(deriveToolHandlerWithDescription ''SeparateParamsTool 'handleSeparateParamsTool separateParamsDescriptions)
testRecursiveToolHandlersWithDescriptions :: (ToolListHandler IO, ToolCallHandler IO)
testRecursiveToolHandlersWithDescriptions = $(deriveToolHandlerWithDescription ''RecursiveTool 'handleRecursiveTool separateParamsDescriptions)
-- Helper functions for advanced testing
findToolByName :: Text -> [ToolDefinition] -> ToolDefinition
findToolByName toolName toolDefs =
case filter (\def -> toolDefinitionName def == toolName) toolDefs of
[def] -> def
[] -> error $ "Tool not found: " ++ T.unpack toolName
_ -> error $ "Multiple tools found with name: " ++ T.unpack toolName
assertSchemaHasProperties :: [Text] -> ToolDefinition -> IO ()
assertSchemaHasProperties expectedProps toolDef = do
case toolDefinitionInputSchema toolDef of
InputSchemaDefinitionObject props _ ->
let propNames = map fst props
in all (`elem` propNames) expectedProps `shouldBe` True
other -> expectationFailure $ "Expected InputSchemaDefinitionObject but got: " ++ show other
assertToolCallResult :: (ToolCallHandler IO) -> Text -> [(Text, Text)] -> Text -> IO ()
assertToolCallResult handler toolName args expectedContent = do
result <- handler toolName args
case result of
Right (ContentText content) -> content `shouldBe` expectedContent
other -> expectationFailure $ "Expected ContentText but got: " ++ show other
assertToolHasDescription :: Text -> Text -> ToolDefinition -> IO ()
assertToolHasDescription toolName expectedDesc toolDef = do
toolDefinitionName toolDef `shouldBe` toolName
toolDefinitionDescription toolDef `shouldBe` expectedDesc
assertPropertyHasDescription :: Text -> Text -> ToolDefinition -> IO ()
assertPropertyHasDescription propName expectedDesc toolDef = do
case toolDefinitionInputSchema toolDef of
InputSchemaDefinitionObject props _ ->
case lookup propName props of
Just prop -> propertyDescription prop `shouldBe` expectedDesc
Nothing -> expectationFailure $ "Property not found: " ++ T.unpack propName
other -> expectationFailure $ "Expected InputSchemaDefinitionObject but got: " ++ show other
spec :: Spec
spec = describe "Advanced Template Haskell Derivation" $ do
describe "Separate Parameter Types" $ do
it "generates correct schema for separate parameter tools" $ do
let (listHandler, _) = testSeparateParamsToolHandlers
toolDefs <- listHandler
-- Test GetValue tool schema
let getValueDef = findToolByName "get_value" toolDefs
assertSchemaHasProperties ["_gvpKey"] getValueDef
-- Test SetValue tool schema
let setValueDef = findToolByName "set_value" toolDefs
assertSchemaHasProperties ["_svpKey", "_svpValue"] setValueDef
forM_ separateParamsTestCases $ \testCase ->
it (T.unpack $ sepTestDescription testCase) $ do
let (_, callHandler) = testSeparateParamsToolHandlers
assertToolCallResult callHandler (sepToolName testCase) (sepArgs testCase) (sepExpectedResult testCase)
describe "Recursive Parameter Types" $ do
it "generates correct schema for recursive parameter tools" $ do
let (listHandler, _) = testRecursiveToolHandlers
toolDefs <- listHandler
let processDataDef = findToolByName "process_data" toolDefs
assertSchemaHasProperties ["_ipName", "_ipAge"] processDataDef
forM_ recursiveParamsTestCases $ \testCase ->
it (T.unpack $ recTestDescription testCase) $ do
let (_, callHandler) = testRecursiveToolHandlers
assertToolCallResult callHandler (recToolName testCase) (recArgs testCase) (recExpectedResult testCase)
describe "Custom Descriptions with Separate Parameters" $ do
it "applies correct tool descriptions for separate parameter tools" $ do
let (listHandler, _) = testSeparateParamsToolHandlersWithDescriptions
toolDefs <- listHandler
let getValueDef = findToolByName "get_value" toolDefs
assertToolHasDescription "get_value" "Retrieves a value from the key-value store" getValueDef
let setValueDef = findToolByName "set_value" toolDefs
assertToolHasDescription "set_value" "Sets a value in the key-value store" setValueDef
it "applies correct field descriptions for separate parameter tools" $ do
let (listHandler, _) = testSeparateParamsToolHandlersWithDescriptions
toolDefs <- listHandler
let getValueDef = findToolByName "get_value" toolDefs
assertPropertyHasDescription "_gvpKey" "The key to retrieve the value for" getValueDef
let setValueDef = findToolByName "set_value" toolDefs
assertPropertyHasDescription "_svpKey" "The key to set the value for" setValueDef
assertPropertyHasDescription "_svpValue" "The value to store" setValueDef
describe "Recursive Tool Descriptions" $ do
it "applies correct descriptions for recursive parameter tools" $ do
let (listHandler, _) = testRecursiveToolHandlersWithDescriptions
toolDefs <- listHandler
let processDataDef = findToolByName "process_data" toolDefs
assertToolHasDescription "process_data" "Processes user data with age validation" processDataDef
it "applies correct field descriptions for recursive parameters" $ do
let (listHandler, _) = testRecursiveToolHandlersWithDescriptions
toolDefs <- listHandler
let processDataDef = findToolByName "process_data" toolDefs
assertPropertyHasDescription "_ipName" "The person's full name" processDataDef
assertPropertyHasDescription "_ipAge" "The person's age in years" processDataDef