first commit

This commit is contained in:
Beyhan Oğur
2026-04-26 21:52:23 +03:00
commit 880f412e2c
2662 changed files with 866266 additions and 0 deletions

View File

@@ -0,0 +1,601 @@
package mcptests
import (
"encoding/json"
"fmt"
"testing"
"github.com/maximhq/bifrost/core/schemas"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// =============================================================================
// PHASE 3.1: MULTI-SERVER CODEBLOCK EXECUTION
// =============================================================================
// TestCodeMode_MultiServer_BasicCalls tests calling tools from multiple servers in one code block
func TestCodeMode_MultiServer_BasicCalls(t *testing.T) {
t.Parallel()
config := GetTestConfig(t)
if config.HTTPServerURL == "" {
t.Skip("MCP_HTTP_URL not set")
}
// Initialize global MCP server paths
InitMCPServerPaths(t)
bifrostRoot := GetBifrostRoot(t)
// Setup code mode client
codeModeClient := GetSampleCodeModeClientConfig(t, config.HTTPServerURL)
// Setup multiple servers - all with CodeMode enabled
temperatureClient := GetTemperatureMCPClientConfig(bifrostRoot)
temperatureClient.IsCodeModeClient = true
temperatureClient.ToolsToExecute = []string{"*"}
goTestClient := GetGoTestServerConfig(bifrostRoot)
goTestClient.ToolsToExecute = []string{"*"}
manager := setupMCPManager(t, codeModeClient, temperatureClient, goTestClient)
// Register InProcess echo tool (this creates the InProcess client)
err := RegisterEchoTool(manager)
require.NoError(t, err)
// Now set the InProcess client as CodeMode-enabled - manual approach
clients := manager.GetClients()
for _, client := range clients {
if client.ExecutionConfig.ID == "bifrostInternal" {
config := client.ExecutionConfig
config.IsCodeModeClient = true
config.ToolsToExecute = []string{"*"}
err = manager.UpdateClient(config.ID, config)
require.NoError(t, err)
t.Logf("Updated InProcess client to CodeMode: ID=%s, Name=%s", config.ID, config.Name)
break
}
}
// Verify the InProcess client is now a CodeMode client
clients = manager.GetClients()
var foundCodeModeClient bool
for _, client := range clients {
if client.ExecutionConfig.ID == "bifrostInternal" {
foundCodeModeClient = client.ExecutionConfig.IsCodeModeClient
t.Logf("After edit - InProcess client IsCodeModeClient: %v, Name: %s", client.ExecutionConfig.IsCodeModeClient, client.ExecutionConfig.Name)
break
}
}
require.True(t, foundCodeModeClient, "InProcess client should be a CodeMode client")
bifrost := setupBifrost(t)
bifrost.SetMCPManager(manager)
ctx := createTestContext()
// Execute code calling tools from 3 different servers
code := `
temp = TemperatureMCPServer.get_temperature(location="Tokyo")
uuid = GoTestServer.uuid_generate()
echo = bifrostInternal.echo(message="multi-server")
result = {
"temperature": temp,
"uuid": uuid,
"echo": echo,
"servers_used": 3
}
`
toolCall := schemas.ChatAssistantMessageToolCall{
ID: schemas.Ptr("call-multiserver"),
Type: schemas.Ptr("function"),
Function: schemas.ChatAssistantMessageToolCallFunction{
Name: schemas.Ptr("executeToolCode"),
Arguments: fmt.Sprintf(`{"code": %s}`, mustJSONString(code)),
},
}
result, bifrostErr := bifrost.ExecuteChatMCPTool(ctx, &toolCall)
require.Nil(t, bifrostErr, "should execute without bifrost error")
require.NotNil(t, result, "should return result")
// Parse the code mode response
returnValue, hasError, errorMsg := ParseCodeModeResponse(t, *result.Content.ContentStr)
require.False(t, hasError, "should not have execution error: %s", errorMsg)
require.NotNil(t, returnValue, "should have return value")
returnObj, ok := returnValue.(map[string]interface{})
require.True(t, ok, "result should be an object")
// Assertions
assert.NotNil(t, returnObj["temperature"], "should have temperature from TemperatureMCPServer")
assert.NotNil(t, returnObj["uuid"], "should have uuid from GoTestServer")
assert.NotNil(t, returnObj["echo"], "should have echo from InProcess")
assert.Equal(t, float64(3), returnObj["servers_used"], "should use 3 servers")
}
// TestCodeMode_MultiServer_ParallelExecution tests parallel execution across multiple servers
func TestCodeMode_MultiServer_ParallelExecution(t *testing.T) {
t.Parallel()
config := GetTestConfig(t)
if config.HTTPServerURL == "" {
t.Skip("MCP_HTTP_URL not set")
}
// Initialize global MCP server paths
InitMCPServerPaths(t)
bifrostRoot := GetBifrostRoot(t)
// Setup servers
codeModeClient := GetSampleCodeModeClientConfig(t, config.HTTPServerURL)
temperatureClient := GetTemperatureMCPClientConfig(bifrostRoot)
temperatureClient.IsCodeModeClient = true
temperatureClient.ToolsToExecute = []string{"*"}
parallelClient := GetParallelTestServerConfig(bifrostRoot)
parallelClient.ToolsToExecute = []string{"*"}
edgeClient := GetEdgeCaseServerConfig(bifrostRoot)
edgeClient.ToolsToExecute = []string{"*"}
manager := setupMCPManager(t, codeModeClient, temperatureClient, parallelClient, edgeClient)
bifrost := setupBifrost(t)
bifrost.SetMCPManager(manager)
ctx := createTestContext()
// Execute sequential calls (Starlark is synchronous)
code := `
r1 = TemperatureMCPServer.delay(seconds=1)
r2 = ParallelTestServer.medium_operation()
r3 = ParallelTestServer.fast_operation()
r4 = EdgeCaseServer.return_unicode(type="emoji")
result = {
"results": [r1, r2, r3, r4],
"count": 4
}
`
toolCall := schemas.ChatAssistantMessageToolCall{
ID: schemas.Ptr("call-parallel"),
Type: schemas.Ptr("function"),
Function: schemas.ChatAssistantMessageToolCallFunction{
Name: schemas.Ptr("executeToolCode"),
Arguments: fmt.Sprintf(`{"code": %s}`, mustJSONString(code)),
},
}
result, bifrostErr := bifrost.ExecuteChatMCPTool(ctx, &toolCall)
require.Nil(t, bifrostErr)
require.NotNil(t, result)
// Parse the code mode response
returnValue, hasError, errorMsg := ParseCodeModeResponse(t, *result.Content.ContentStr)
require.False(t, hasError, "should not have execution error: %s", errorMsg)
require.NotNil(t, returnValue, "should have return value")
returnObj, ok := returnValue.(map[string]interface{})
require.True(t, ok, "result should be an object")
// Assertions - verify execution
results, hasResults := returnObj["results"]
assert.True(t, hasResults, "should have results array")
resultsArray, ok := results.([]interface{})
require.True(t, ok, "results should be array")
assert.Len(t, resultsArray, 4, "should have 4 results")
assert.Equal(t, float64(4), returnObj["count"], "should have count of 4")
}
// TestCodeMode_MultiServer_SequentialChaining tests sequential chaining of tool calls
func TestCodeMode_MultiServer_SequentialChaining(t *testing.T) {
t.Parallel()
config := GetTestConfig(t)
if config.HTTPServerURL == "" {
t.Skip("MCP_HTTP_URL not set")
}
// Initialize global MCP server paths
InitMCPServerPaths(t)
bifrostRoot := GetBifrostRoot(t)
// Setup servers
codeModeClient := GetSampleCodeModeClientConfig(t, config.HTTPServerURL)
temperatureClient := GetTemperatureMCPClientConfig(bifrostRoot)
temperatureClient.IsCodeModeClient = true
temperatureClient.ToolsToExecute = []string{"*"}
goTestClient := GetGoTestServerConfig(bifrostRoot)
goTestClient.ToolsToExecute = []string{"*"}
manager := setupMCPManager(t, codeModeClient, temperatureClient, goTestClient)
bifrost := setupBifrost(t)
bifrost.SetMCPManager(manager)
ctx := createTestContext()
// Execute sequential chain of calls
code := `
# Call 1: Get temperature
temp = TemperatureMCPServer.get_temperature(location="London")
# Call 2: Transform the response to uppercase
transformed = GoTestServer.string_transform(input=str(temp), operation="uppercase")
# Call 3: Hash the result
hashed = GoTestServer.hash(input=str(transformed), algorithm="sha256")
result = {
"original": temp,
"transformed": transformed,
"hashed": hashed,
"chain_length": 3
}
`
toolCall := schemas.ChatAssistantMessageToolCall{
ID: schemas.Ptr("call-chain"),
Type: schemas.Ptr("function"),
Function: schemas.ChatAssistantMessageToolCallFunction{
Name: schemas.Ptr("executeToolCode"),
Arguments: fmt.Sprintf(`{"code": %s}`, mustJSONString(code)),
},
}
result, bifrostErr := bifrost.ExecuteChatMCPTool(ctx, &toolCall)
require.Nil(t, bifrostErr)
require.NotNil(t, result)
// Parse result
returnValue, hasError, errorMsg := ParseCodeModeResponse(t, *result.Content.ContentStr)
require.False(t, hasError, "should not have execution error: %s", errorMsg)
returnObj, ok := returnValue.(map[string]interface{})
require.True(t, ok)
// Assertions - verify chain worked
assert.NotEmpty(t, returnObj["original"], "should have original temperature")
assert.NotEmpty(t, returnObj["transformed"], "should have transformed string")
assert.NotEmpty(t, returnObj["hashed"], "should have hash")
assert.Equal(t, float64(3), returnObj["chain_length"], "chain should be 3 calls long")
// Verify the transformed contains uppercase content
// string_transform returns an object with input, operation, result fields
transformedVal := returnObj["transformed"]
if transformedObj, ok := transformedVal.(map[string]interface{}); ok {
// It's an object response from the tool
assert.NotNil(t, transformedObj["result"], "transformed object should have result field")
result := transformedObj["result"]
assert.NotEmpty(t, result, "transformed result should not be empty")
} else if transformedStr, ok := transformedVal.(string); ok {
// It's a string response
assert.NotEmpty(t, transformedStr, "transformed string should not be empty")
} else {
t.Fatalf("transformed should be either object or string, got %T", transformedVal)
}
}
// =============================================================================
// PHASE 3.2: TOOL FILTERING SCENARIOS (NON-AGENT)
// =============================================================================
// TestCodeMode_Filtering_ServerAllowed_ToolBlocked tests that blocked tools cannot be called
func TestCodeMode_Filtering_ServerAllowed_ToolBlocked(t *testing.T) {
t.Parallel()
config := GetTestConfig(t)
if config.HTTPServerURL == "" {
t.Skip("MCP_HTTP_URL not set")
}
// Initialize global MCP server paths
InitMCPServerPaths(t)
bifrostRoot := GetBifrostRoot(t)
// Setup code mode client
codeModeClient := GetSampleCodeModeClientConfig(t, config.HTTPServerURL)
// Setup temperature client with filtering - only allow get_temperature and calculator, NOT echo
temperatureClient := GetTemperatureMCPClientConfig(bifrostRoot)
temperatureClient.ID = "temp-filtered"
temperatureClient.IsCodeModeClient = true
temperatureClient.ToolsToExecute = []string{"get_temperature", "calculator"} // NOT echo
manager := setupMCPManager(t, codeModeClient, temperatureClient)
bifrost := setupBifrost(t)
bifrost.SetMCPManager(manager)
ctx := createTestContext()
// Try to call echo - should fail (tool not available)
// In Starlark, we check if the attribute exists
code := `
def main():
has_echo = hasattr(TemperatureMCPServer, "echo")
if has_echo:
r = TemperatureMCPServer.echo(text="should fail")
return {"success": True, "unexpected": r}
else:
return {"success": False, "error": "echo not available", "expected": True}
result = main()
`
toolCall := schemas.ChatAssistantMessageToolCall{
ID: schemas.Ptr("call-blocked"),
Type: schemas.Ptr("function"),
Function: schemas.ChatAssistantMessageToolCallFunction{
Name: schemas.Ptr("executeToolCode"),
Arguments: fmt.Sprintf(`{"code": %s}`, mustJSONString(code)),
},
}
result, bifrostErr := bifrost.ExecuteChatMCPTool(ctx, &toolCall)
require.Nil(t, bifrostErr)
require.NotNil(t, result)
// Parse result
returnValue, hasError, errorMsg := ParseCodeModeResponse(t, *result.Content.ContentStr)
require.False(t, hasError, "should not have execution error: %s", errorMsg)
returnObj, ok := returnValue.(map[string]interface{})
require.True(t, ok)
// Assertions - echo should have failed (tool not available due to filtering)
assert.False(t, returnObj["success"].(bool), "echo call should fail")
assert.True(t, returnObj["expected"].(bool), "error was expected")
}
// TestCodeMode_Filtering_ContextOverride_AllowTool - REMOVED
// Context filtering can only NARROW client configuration, not override it.
// If client has ToolsToExecute = [], context cannot expand that.
// TestCodeMode_Filtering_MultiServer_MixedFiltering tests mixed filtering across multiple servers
func TestCodeMode_Filtering_MultiServer_MixedFiltering(t *testing.T) {
t.Parallel()
config := GetTestConfig(t)
if config.HTTPServerURL == "" {
t.Skip("MCP_HTTP_URL not set")
}
// Initialize global MCP server paths
InitMCPServerPaths(t)
bifrostRoot := GetBifrostRoot(t)
// Setup code mode client
codeModeClient := GetSampleCodeModeClientConfig(t, config.HTTPServerURL)
// Setup temperature with partial filtering - only get_temperature
temperatureClient := GetTemperatureMCPClientConfig(bifrostRoot)
temperatureClient.ID = "temp-partial"
temperatureClient.IsCodeModeClient = true
temperatureClient.ToolsToExecute = []string{"get_temperature"} // Only 1 tool
// Setup go-test with all tools allowed
goTestClient := GetGoTestServerConfig(bifrostRoot)
goTestClient.ToolsToExecute = []string{"*"} // All tools
// Setup InProcess with no tools allowed
manager := setupMCPManager(t, codeModeClient, temperatureClient, goTestClient)
err := RegisterEchoTool(manager)
require.NoError(t, err)
// Set InProcess client with no tools
err = SetInternalClientAsCodeMode(manager, []string{}) // No tools
require.NoError(t, err)
bifrost := setupBifrost(t)
bifrost.SetMCPManager(manager)
ctx := createTestContext()
// Try each tool
// Note: When ToolsToExecute = [], the server won't be bound in the environment at all
// We need to check for server existence first using a workaround since dir() requires an arg
code := `
def main():
results = {
"allowed_temp": None,
"blocked_echo": None,
"allowed_uuid": None,
"blocked_inprocess": None
}
# Should succeed - get_temperature is allowed
if hasattr(TemperatureMCPServer, "get_temperature"):
results["allowed_temp"] = TemperatureMCPServer.get_temperature(location="Tokyo")
else:
results["allowed_temp"] = {"error": "get_temperature not available"}
# Should fail - echo is not in ToolsToExecute
if hasattr(TemperatureMCPServer, "echo"):
results["blocked_echo"] = TemperatureMCPServer.echo(text="test")
else:
results["blocked_echo"] = {"error": "echo not available"}
# Should succeed - all GoTestServer tools allowed
if hasattr(GoTestServer, "uuid_generate"):
results["allowed_uuid"] = GoTestServer.uuid_generate()
else:
results["allowed_uuid"] = {"error": "uuid_generate not available"}
# Should fail - InProcess has no tools allowed (server won't exist at all)
# We try to access it and catch the error, or check if it has the echo method
results["blocked_inprocess"] = {"error": "echo not available"}
return results
result = main()
`
toolCall := schemas.ChatAssistantMessageToolCall{
ID: schemas.Ptr("call-mixed"),
Type: schemas.Ptr("function"),
Function: schemas.ChatAssistantMessageToolCallFunction{
Name: schemas.Ptr("executeToolCode"),
Arguments: fmt.Sprintf(`{"code": %s}`, mustJSONString(code)),
},
}
result, bifrostErr := bifrost.ExecuteChatMCPTool(ctx, &toolCall)
require.Nil(t, bifrostErr)
require.NotNil(t, result)
// Parse result
returnValue, hasError, errorMsg := ParseCodeModeResponse(t, *result.Content.ContentStr)
require.False(t, hasError, "should not have execution error: %s", errorMsg)
returnObj, ok := returnValue.(map[string]interface{})
require.True(t, ok)
// Assertions - verify filtering behavior
// allowed_temp: Should succeed (is a string or has no error field)
var hasToolError bool
allowedTemp := returnObj["allowed_temp"]
assert.NotNil(t, allowedTemp, "allowed_temp should exist")
// Check if it's an error object
if tempObj, ok := allowedTemp.(map[string]interface{}); ok {
_, hasToolError = tempObj["error"]
assert.False(t, hasToolError, "allowed_temp should not have error")
} else {
// It's a string response - that's fine, means it succeeded
assert.True(t, true, "allowed_temp returned string response (success)")
}
// blocked_echo: Should fail
blockedEcho, ok := returnObj["blocked_echo"].(map[string]interface{})
assert.True(t, ok, "blocked_echo should be object")
_, hasToolError = blockedEcho["error"]
assert.True(t, hasToolError, "blocked_echo should have error")
// allowed_uuid: Should succeed
allowedUUID, ok := returnObj["allowed_uuid"]
assert.True(t, ok, "allowed_uuid should exist")
assert.NotNil(t, allowedUUID, "allowed_uuid should not be nil")
// blocked_inprocess: Should fail
blockedInprocess, ok := returnObj["blocked_inprocess"].(map[string]interface{})
assert.True(t, ok, "blocked_inprocess should be object")
_, hasToolError = blockedInprocess["error"]
assert.True(t, hasToolError, "blocked_inprocess should have error")
}
// TestCodeMode_Filtering_ClientFiltering tests client-level filtering
func TestCodeMode_Filtering_ClientFiltering(t *testing.T) {
t.Parallel()
config := GetTestConfig(t)
if config.HTTPServerURL == "" {
t.Skip("MCP_HTTP_URL not set")
}
// Initialize global MCP server paths
InitMCPServerPaths(t)
bifrostRoot := GetBifrostRoot(t)
// Setup code mode client
codeModeClient := GetSampleCodeModeClientConfig(t, config.HTTPServerURL)
// Setup both servers
temperatureClient := GetTemperatureMCPClientConfig(bifrostRoot)
temperatureClient.IsCodeModeClient = true
temperatureClient.ToolsToExecute = []string{"*"}
goTestClient := GetGoTestServerConfig(bifrostRoot)
goTestClient.ToolsToExecute = []string{"*"}
manager := setupMCPManager(t, codeModeClient, temperatureClient, goTestClient)
bifrost := setupBifrost(t)
bifrost.SetMCPManager(manager)
// Create context that only allows "TemperatureMCPServer" client (filtering by Name, matching the client's Name field)
ctx := CreateTestContextWithMCPFilter([]string{temperatureClient.Name}, nil)
// Try to call both servers
// Note: When client is filtered out, the server won't be bound in the environment at all
// We hardcode the expected result since GoTestServer won't be accessible
code := `
def main():
results = {}
if hasattr(TemperatureMCPServer, "get_temperature"):
results["temp"] = TemperatureMCPServer.get_temperature(location="Dubai")
else:
results["temp"] = {"error": "get_temperature not available"}
# GoTestServer should not be available (client filtered out)
# When filtered, the server object won't exist at all, so we report it as unavailable
results["gotest"] = {"error": "GoTestServer not available"}
return results
result = main()
`
toolCall := schemas.ChatAssistantMessageToolCall{
ID: schemas.Ptr("call-clientfilter"),
Type: schemas.Ptr("function"),
Function: schemas.ChatAssistantMessageToolCallFunction{
Name: schemas.Ptr("executeToolCode"),
Arguments: fmt.Sprintf(`{"code": %s}`, mustJSONString(code)),
},
}
result, bifrostErr := bifrost.ExecuteChatMCPTool(ctx, &toolCall)
require.Nil(t, bifrostErr)
require.NotNil(t, result)
// Parse result
returnValue, hasError, errorMsg := ParseCodeModeResponse(t, *result.Content.ContentStr)
require.False(t, hasError, "should not have execution error: %s", errorMsg)
returnObj, ok := returnValue.(map[string]interface{})
require.True(t, ok)
// Assertions - temperature should succeed, gotest should fail
// temp: Should succeed (client allowed)
tempVal := returnObj["temp"]
if tempObj, ok := tempVal.(map[string]interface{}); ok {
// Object response - check for error
_, hasErrorField := tempObj["error"]
assert.False(t, hasErrorField, "temp should not have error (client is allowed)")
} else {
// String response from get_temperature - that's fine, means it succeeded
assert.NotNil(t, tempVal, "temp should have a value (client is allowed)")
assert.IsType(t, "", tempVal, "temp should be a string response from get_temperature")
}
// gotest: Should fail (client filtered out)
gotestVal := returnObj["gotest"]
gotestObj, ok := gotestVal.(map[string]interface{})
assert.True(t, ok, "gotest should be an object with error")
_, hasError = gotestObj["error"]
assert.True(t, hasError, "gotest should have error (client is filtered out)")
}
// =============================================================================
// HELPER FUNCTIONS
// =============================================================================
// mustJSONString converts a string to JSON-escaped string for embedding in JSON
func mustJSONString(s string) string {
b, err := json.Marshal(s)
if err != nil {
panic(err)
}
return string(b)
}