Files
wehub-resource-sync e04ed9c211
CF: Deploy Dev Docs / deploy (push) Waiting to run
Sync Labels / build (push) Waiting to run
tests / unit tests (macos-latest) (push) Waiting to run
tests / unit tests (ubuntu-latest) (push) Waiting to run
tests / unit tests (windows-latest) (push) Waiting to run
chore: import upstream snapshot with attribution
2026-07-13 13:32:45 +08:00

208 lines
6.4 KiB
Go

// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package testutils
import (
"bufio"
"context"
"fmt"
"io"
"os"
"regexp"
"strings"
"testing"
"github.com/googleapis/mcp-toolbox/internal/log"
"github.com/googleapis/mcp-toolbox/internal/prompts"
"github.com/googleapis/mcp-toolbox/internal/tools"
"github.com/googleapis/mcp-toolbox/internal/util"
"github.com/googleapis/mcp-toolbox/internal/util/parameters"
)
// MockVersionString is used as a temporary version string in tests
const MockVersionString = "0.0.0"
// formatYaml is a utility function for stripping out tabs in multiline strings
func FormatYaml(in string) []byte {
// removes any leading indentation(tabs)
in = strings.ReplaceAll(in, "\n\t", "\n ")
// converts remaining indentation
in = strings.ReplaceAll(in, "\t", " ")
return []byte(in)
}
// ContextWithNewLogger create a new context with new logger
func ContextWithNewLogger() (context.Context, error) {
ctx := context.Background()
logger, err := log.NewStdLogger(os.Stdout, os.Stderr, "info")
if err != nil {
return nil, fmt.Errorf("unable to create logger: %s", err)
}
return util.WithLogger(ctx, logger), nil
}
// ContextWithUserAgent creates a new context with a specified user agent string.
func ContextWithUserAgent(ctx context.Context, userAgent string) context.Context {
return util.WithUserAgent(ctx, userAgent)
}
// WaitForString waits until the server logs a single line that matches the provided regex.
// returns the output of whatever the server sent so far.
func WaitForString(ctx context.Context, re *regexp.Regexp, pr io.ReadCloser) (string, error) {
in := bufio.NewReader(pr)
ctx, cancel := context.WithCancel(ctx)
defer cancel()
// read lines in background, sending result of each read over a channel
// this allows us to use in.ReadString without blocking
type result struct {
s string
err error
}
output := make(chan result)
go func() {
defer close(output)
for {
select {
case <-ctx.Done():
// if the context is canceled, the orig thread will send back the error
// so we can just exit the goroutine here
return
default:
// otherwise read a line from the output
s, err := in.ReadString('\n')
if err != nil {
output <- result{err: err}
return
}
output <- result{s: s}
// if that last string matched, exit the goroutine
if re.MatchString(s) {
return
}
}
}
}()
// collect the output until the ctx is canceled, an error was hit,
// or match was found (which is indicated the channel is closed)
var sb strings.Builder
for {
select {
case <-ctx.Done():
// if ctx is done, return that error
return sb.String(), ctx.Err()
case o, ok := <-output:
if !ok {
// match was found!
return sb.String(), nil
}
if o.err != nil {
// error was found!
return sb.String(), o.err
}
sb.WriteString(o.s)
}
}
}
var MockTool1 = NewMockTool("no_params", "", []parameters.Parameter{}, false, false)
var MockTool2 = NewMockTool(
"some_params",
"",
parameters.Parameters{
parameters.NewIntParameter("param1", "This is the first parameter."),
parameters.NewIntParameter("param2", "This is the second parameter."),
}, false, false)
var MockTool3 = NewMockTool(
"array_param", "some description",
parameters.Parameters{
parameters.NewArrayParameter("my_array", "this param is an array of strings", parameters.NewStringParameter("my_string", "string item")),
}, false, false)
var MockTool4 = NewMockTool("unauthorized_tool", "", []parameters.Parameter{}, true, false)
var MockTool5 = NewMockTool("require_client_auth_tool", "", []parameters.Parameter{}, false, true)
var MockToolUrlBinding = func() MockTool {
t := NewMockTool(
"url_binding_tool",
"A tool for testing URL param binding",
parameters.Parameters{
parameters.NewStringParameter("param1", "A bound string param"),
parameters.NewIntParameter("param2", "A bound int param"),
parameters.NewBooleanParameter("param3", "A bound bool param"),
parameters.NewFloatParameter("param4", "A bound float param"),
parameters.NewStringParameter("param5", "An unbound string param"),
parameters.NewArrayParameter("param6", "A bound array param", parameters.NewStringParameter("item", "item")),
parameters.NewMapParameter("param7", "A bound map param", parameters.TypeString),
}, false, false)
t.ReturnParamsInInvoke = true
return t
}()
var MockPrompt1 = NewMockPrompt("prompt1", "", prompts.Arguments{})
var MockPrompt2 = NewMockPrompt("prompt2", "", prompts.Arguments{
{Parameter: parameters.NewStringParameter("arg1", "This is the first argument.")},
})
// SetUpResources setups resources to test against
func SetUpResources(t *testing.T, mockTools []MockTool, mockPrompts []MockPrompt) (map[string]tools.Tool, map[string]tools.Toolset, map[string]prompts.Prompt, map[string]prompts.Promptset) {
toolsMap := make(map[string]tools.Tool)
var allTools []string
for _, tool := range mockTools {
toolsMap[tool.Name] = tool
allTools = append(allTools, tool.Name)
}
toolsets := make(map[string]tools.Toolset)
if len(allTools) > 0 {
for name, l := range map[string][]string{
"": allTools,
"tool1_only": {allTools[0]},
"tool2_only": {allTools[1]},
} {
tc := tools.ToolsetConfig{Name: name, ToolNames: l}
m, err := tc.Initialize(MockVersionString, toolsMap)
if err != nil {
t.Fatalf("unable to initialize toolset %q: %s", name, err)
}
toolsets[name] = m
}
}
promptsMap := make(map[string]prompts.Prompt)
var allPrompts []string
for _, prompt := range mockPrompts {
promptsMap[prompt.Name] = prompt
allPrompts = append(allPrompts, prompt.Name)
}
promptsets := make(map[string]prompts.Promptset)
if len(allPrompts) > 0 {
psc := prompts.PromptsetConfig{Name: "", PromptNames: allPrompts}
ps, err := psc.Initialize(MockVersionString, promptsMap)
if err != nil {
t.Fatalf("unable to initialize default promptset: %s", err)
}
promptsets[""] = ps
}
return toolsMap, toolsets, promptsMap, promptsets
}