mirror of
https://github.com/googleapis/genai-toolbox.git
synced 2026-01-09 07:28:05 -05:00
Merge branch 'main' of https://github.com/googleapis/genai-toolbox into js-quickstart-doc
This commit is contained in:
2
.github/blunderbuss.yml
vendored
2
.github/blunderbuss.yml
vendored
@@ -1,5 +1,4 @@
|
||||
assign_issues:
|
||||
- kurtisvg
|
||||
- Yuan325
|
||||
- duwenxin99
|
||||
- akitsch
|
||||
@@ -11,7 +10,6 @@ assign_issues_by:
|
||||
- shobsi
|
||||
- jiaxunwu
|
||||
assign_prs:
|
||||
- kurtisvg
|
||||
- Yuan325
|
||||
- duwenxin99
|
||||
- akitsch
|
||||
|
||||
@@ -63,7 +63,7 @@ import (
|
||||
_ "github.com/googleapis/genai-toolbox/internal/tools/mssql/mssqlsql"
|
||||
_ "github.com/googleapis/genai-toolbox/internal/tools/mysql/mysqlexecutesql"
|
||||
_ "github.com/googleapis/genai-toolbox/internal/tools/mysql/mysqlsql"
|
||||
_ "github.com/googleapis/genai-toolbox/internal/tools/neo4j"
|
||||
_ "github.com/googleapis/genai-toolbox/internal/tools/neo4j/neo4jcypher"
|
||||
_ "github.com/googleapis/genai-toolbox/internal/tools/postgres/postgresexecutesql"
|
||||
_ "github.com/googleapis/genai-toolbox/internal/tools/postgres/postgressql"
|
||||
_ "github.com/googleapis/genai-toolbox/internal/tools/redis"
|
||||
|
||||
@@ -81,8 +81,9 @@ the parameter.
|
||||
|-------------|:---------------:|:------------:|-----------------------------------------------------------------------------|
|
||||
| name | string | true | Name of the parameter. |
|
||||
| type | string | true | Must be one of "string", "integer", "float", "boolean" "array" |
|
||||
| default | parameter type | false | Default value of the parameter. If provided, the parameter is not required. |
|
||||
| description | string | true | Natural language description of the parameter to describe it to the agent. |
|
||||
| default | parameter type | false | Default value of the parameter. If provided, `required` will be `false`. |
|
||||
| required | bool | false | Indicate if the parameter is required. Default to `true`. |
|
||||
|
||||
### Array Parameters
|
||||
|
||||
@@ -107,12 +108,13 @@ in the list using the items field:
|
||||
|-------------|:----------------:|:------------:|-----------------------------------------------------------------------------|
|
||||
| name | string | true | Name of the parameter. |
|
||||
| type | string | true | Must be "array" |
|
||||
| default | parameter type | false | Default value of the parameter. If provided, the parameter is not required. |
|
||||
| description | string | true | Natural language description of the parameter to describe it to the agent. |
|
||||
| default | parameter type | false | Default value of the parameter. If provided, `required` will be `false`. |
|
||||
| required | bool | false | Indicate if the parameter is required. Default to `true`. |
|
||||
| items | parameter object | true | Specify a Parameter object for the type of the values in the array. |
|
||||
|
||||
{{< notice note >}}
|
||||
Items in array should not have a default value. If provided, it will be ignored.
|
||||
Items in array should not have a `default` or `required` value. If provided, it will be ignored.
|
||||
{{< /notice >}}
|
||||
|
||||
### Map Parameters
|
||||
|
||||
4
go.mod
4
go.mod
@@ -13,7 +13,7 @@ require (
|
||||
cloud.google.com/go/spanner v1.83.0
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace v1.29.0
|
||||
github.com/couchbase/gocb/v2 v2.10.0
|
||||
github.com/couchbase/gocb/v2 v2.10.1
|
||||
github.com/couchbase/tools-common/http v1.0.9
|
||||
github.com/fsnotify/fsnotify v1.9.0
|
||||
github.com/go-chi/chi/v5 v5.2.2
|
||||
@@ -66,7 +66,7 @@ require (
|
||||
github.com/cenkalti/backoff/v5 v5.0.2 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f // indirect
|
||||
github.com/couchbase/gocbcore/v10 v10.7.0 // indirect
|
||||
github.com/couchbase/gocbcore/v10 v10.7.1 // indirect
|
||||
github.com/couchbase/gocbcoreps v0.1.3 // indirect
|
||||
github.com/couchbase/goprotostellar v1.0.2 // indirect
|
||||
github.com/couchbase/tools-common/errors v1.0.0 // indirect
|
||||
|
||||
8
go.sum
8
go.sum
@@ -712,10 +712,10 @@ github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWH
|
||||
github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f h1:C5bqEmzEPLsHm9Mv73lSE9e9bKV23aB1vxOsmZrkl3k=
|
||||
github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
|
||||
github.com/couchbase/gocb/v2 v2.10.0 h1:NNxZ4okToU1Ylqp6F8tE41CEJQPhb2WjufryAkeubOk=
|
||||
github.com/couchbase/gocb/v2 v2.10.0/go.mod h1:OSbMfQkP7ltbKiDZhsT2mGDhkQNmvGXxptKcxAUJQ2Y=
|
||||
github.com/couchbase/gocbcore/v10 v10.7.0 h1:lAEi0PNeEGKOu8pWrPUdtLOT2oGr1J/UTdGHVPC3r/0=
|
||||
github.com/couchbase/gocbcore/v10 v10.7.0/go.mod h1:Q8JWVenMCEOuRgrDQKApHbzzPif38HzefGgRVe9apAI=
|
||||
github.com/couchbase/gocb/v2 v2.10.1 h1:5r1jngGxw3dTZdtq6Xmjq3pdU6hOwRvynvbVIp58T64=
|
||||
github.com/couchbase/gocb/v2 v2.10.1/go.mod h1:GGEJuYjrfnPHCQLcxTcIco+Puy63PS2p8QQd8FRw66I=
|
||||
github.com/couchbase/gocbcore/v10 v10.7.1 h1:6jsNDtqyfoQ8Xg6kv99rzccc3CrHbp7FjeY+ahWXTF4=
|
||||
github.com/couchbase/gocbcore/v10 v10.7.1/go.mod h1:Q8JWVenMCEOuRgrDQKApHbzzPif38HzefGgRVe9apAI=
|
||||
github.com/couchbase/gocbcoreps v0.1.3 h1:fILaKGCjxFIeCgAUG8FGmRDSpdrRggohOMKEgO9CUpg=
|
||||
github.com/couchbase/gocbcoreps v0.1.3/go.mod h1:hBFpDNPnRno6HH5cRXExhqXYRmTsFJlFHQx7vztcXPk=
|
||||
github.com/couchbase/goprotostellar v1.0.2 h1:yoPbAL9sCtcyZ5e/DcU5PRMOEFaJrF9awXYu3VPfGls=
|
||||
|
||||
@@ -125,7 +125,7 @@ func (t Tool) Invoke(ctx context.Context, params tools.ParamValues) (any, error)
|
||||
return nil, fmt.Errorf("unable to get cast %s", sliceParams[0])
|
||||
}
|
||||
|
||||
dryRunJob, err := dryRunQuery(ctx, t.RestService, t.Client.Project(), sql)
|
||||
dryRunJob, err := dryRunQuery(ctx, t.RestService, t.Client.Project(), t.Client.Location, sql)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("query validation failed during dry run: %w", err)
|
||||
}
|
||||
@@ -200,9 +200,13 @@ func (t Tool) Authorized(verifiedAuthServices []string) bool {
|
||||
}
|
||||
|
||||
// dryRunQuery performs a dry run of the SQL query to validate it and get metadata.
|
||||
func dryRunQuery(ctx context.Context, restService *bigqueryrestapi.Service, projectID string, sql string) (*bigqueryrestapi.Job, error) {
|
||||
func dryRunQuery(ctx context.Context, restService *bigqueryrestapi.Service, projectID string, location string, sql string) (*bigqueryrestapi.Job, error) {
|
||||
useLegacySql := false
|
||||
jobToInsert := &bigqueryrestapi.Job{
|
||||
JobReference: &bigqueryrestapi.JobReference{
|
||||
ProjectId: projectID,
|
||||
Location: location,
|
||||
},
|
||||
Configuration: &bigqueryrestapi.JobConfiguration{
|
||||
DryRun: true,
|
||||
Query: &bigqueryrestapi.JobConfigurationQuery{
|
||||
|
||||
@@ -29,10 +29,10 @@ import (
|
||||
|
||||
// Constants for tool configuration
|
||||
const (
|
||||
kind = "firestore-query-collection"
|
||||
defaultLimit = 100
|
||||
defaultAnalyze = false
|
||||
maxFilterLength = 100 // Maximum filters to prevent abuse
|
||||
kind = "firestore-query-collection"
|
||||
defaultLimit = 100
|
||||
defaultAnalyze = false
|
||||
maxFilterLength = 100 // Maximum filters to prevent abuse
|
||||
)
|
||||
|
||||
// Parameter keys
|
||||
@@ -46,16 +46,16 @@ const (
|
||||
|
||||
// Firestore operators
|
||||
var validOperators = map[string]bool{
|
||||
"<": true,
|
||||
"<=": true,
|
||||
">": true,
|
||||
">=": true,
|
||||
"==": true,
|
||||
"!=": true,
|
||||
"<": true,
|
||||
"<=": true,
|
||||
">": true,
|
||||
">=": true,
|
||||
"==": true,
|
||||
"!=": true,
|
||||
"array-contains": true,
|
||||
"array-contains-any": true,
|
||||
"in": true,
|
||||
"not-in": true,
|
||||
"in": true,
|
||||
"not-in": true,
|
||||
}
|
||||
|
||||
// Error messages
|
||||
@@ -128,7 +128,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
|
||||
|
||||
// Create parameters
|
||||
parameters := createParameters()
|
||||
|
||||
|
||||
mcpManifest := tools.McpManifest{
|
||||
Name: cfg.Name,
|
||||
Description: cfg.Description,
|
||||
@@ -151,44 +151,44 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
|
||||
// createParameters creates the parameter definitions for the tool
|
||||
func createParameters() tools.Parameters {
|
||||
collectionPathParameter := tools.NewStringParameter(
|
||||
collectionPathKey,
|
||||
collectionPathKey,
|
||||
"The path to the Firestore collection to query",
|
||||
)
|
||||
|
||||
|
||||
filtersDescription := `Array of filter objects to apply to the query. Each filter is a JSON string with:
|
||||
- field: The field name to filter on
|
||||
- op: The operator to use ("<", "<=", ">", ">=", "==", "!=", "array-contains", "array-contains-any", "in", "not-in")
|
||||
- value: The value to compare against (can be string, number, boolean, or array)
|
||||
Example: {"field": "age", "op": ">", "value": 18}`
|
||||
|
||||
|
||||
filtersParameter := tools.NewArrayParameter(
|
||||
filtersKey,
|
||||
filtersKey,
|
||||
filtersDescription,
|
||||
tools.NewStringParameter("item", "JSON string representation of a filter object"),
|
||||
)
|
||||
|
||||
|
||||
orderByParameter := tools.NewStringParameter(
|
||||
orderByKey,
|
||||
orderByKey,
|
||||
"JSON string specifying the field and direction to order by (e.g., {\"field\": \"name\", \"direction\": \"ASCENDING\"}). Leave empty if not specified",
|
||||
)
|
||||
|
||||
|
||||
limitParameter := tools.NewIntParameterWithDefault(
|
||||
limitKey,
|
||||
defaultLimit,
|
||||
limitKey,
|
||||
defaultLimit,
|
||||
"The maximum number of documents to return",
|
||||
)
|
||||
|
||||
analyzeQueryParameter := tools.NewBooleanParameterWithDefault(
|
||||
analyzeQueryKey,
|
||||
defaultAnalyze,
|
||||
analyzeQueryKey,
|
||||
defaultAnalyze,
|
||||
"If true, returns query explain metrics including execution statistics",
|
||||
)
|
||||
|
||||
return tools.Parameters{
|
||||
collectionPathParameter,
|
||||
filtersParameter,
|
||||
orderByParameter,
|
||||
limitParameter,
|
||||
collectionPathParameter,
|
||||
filtersParameter,
|
||||
orderByParameter,
|
||||
limitParameter,
|
||||
analyzeQueryParameter,
|
||||
}
|
||||
}
|
||||
@@ -220,7 +220,7 @@ func (f *FilterConfig) Validate() error {
|
||||
if f.Field == "" {
|
||||
return fmt.Errorf("filter field cannot be empty")
|
||||
}
|
||||
|
||||
|
||||
if !validOperators[f.Op] {
|
||||
ops := make([]string, 0, len(validOperators))
|
||||
for op := range validOperators {
|
||||
@@ -228,11 +228,11 @@ func (f *FilterConfig) Validate() error {
|
||||
}
|
||||
return fmt.Errorf(errInvalidOperator, f.Op, ops)
|
||||
}
|
||||
|
||||
|
||||
if f.Value == nil {
|
||||
return fmt.Errorf(errMissingFilterValue, f.Field)
|
||||
}
|
||||
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -296,7 +296,7 @@ type queryParameters struct {
|
||||
// parseQueryParameters extracts and validates parameters from the input
|
||||
func (t Tool) parseQueryParameters(params tools.ParamValues) (*queryParameters, error) {
|
||||
mapParams := params.AsMap()
|
||||
|
||||
|
||||
// Get collection path
|
||||
collectionPath, ok := mapParams[collectionPathKey].(string)
|
||||
if !ok || collectionPath == "" {
|
||||
@@ -480,31 +480,31 @@ func (t Tool) getExplainMetrics(docIterator *firestoreapi.DocumentIterator) (map
|
||||
}
|
||||
|
||||
metricsData := make(map[string]any)
|
||||
|
||||
|
||||
// Add plan summary if available
|
||||
if explainMetrics.PlanSummary != nil {
|
||||
planSummary := make(map[string]any)
|
||||
planSummary["indexesUsed"] = explainMetrics.PlanSummary.IndexesUsed
|
||||
metricsData["planSummary"] = planSummary
|
||||
}
|
||||
|
||||
|
||||
// Add execution stats if available
|
||||
if explainMetrics.ExecutionStats != nil {
|
||||
executionStats := make(map[string]any)
|
||||
executionStats["resultsReturned"] = explainMetrics.ExecutionStats.ResultsReturned
|
||||
executionStats["readOperations"] = explainMetrics.ExecutionStats.ReadOperations
|
||||
|
||||
|
||||
if explainMetrics.ExecutionStats.ExecutionDuration != nil {
|
||||
executionStats["executionDuration"] = explainMetrics.ExecutionStats.ExecutionDuration.String()
|
||||
}
|
||||
|
||||
|
||||
if explainMetrics.ExecutionStats.DebugStats != nil {
|
||||
executionStats["debugStats"] = *explainMetrics.ExecutionStats.DebugStats
|
||||
}
|
||||
|
||||
|
||||
metricsData["executionStats"] = executionStats
|
||||
}
|
||||
|
||||
|
||||
return metricsData, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -151,10 +151,10 @@ type SourcePosition struct {
|
||||
|
||||
// ValidationResult represents the result of rules validation
|
||||
type ValidationResult struct {
|
||||
Valid bool `json:"valid"`
|
||||
IssueCount int `json:"issueCount"`
|
||||
FormattedIssues string `json:"formattedIssues,omitempty"`
|
||||
RawIssues []Issue `json:"rawIssues,omitempty"`
|
||||
Valid bool `json:"valid"`
|
||||
IssueCount int `json:"issueCount"`
|
||||
FormattedIssues string `json:"formattedIssues,omitempty"`
|
||||
RawIssues []Issue `json:"rawIssues,omitempty"`
|
||||
}
|
||||
|
||||
func (t Tool) Invoke(ctx context.Context, params tools.ParamValues) (any, error) {
|
||||
@@ -162,7 +162,7 @@ func (t Tool) Invoke(ctx context.Context, params tools.ParamValues) (any, error)
|
||||
|
||||
// Get source parameter
|
||||
source, ok := mapParams[sourceKey].(string)
|
||||
if !ok || source == ""{
|
||||
if !ok || source == "" {
|
||||
return nil, fmt.Errorf("invalid or missing '%s' parameter", sourceKey)
|
||||
}
|
||||
|
||||
@@ -191,15 +191,15 @@ func (t Tool) Invoke(ctx context.Context, params tools.ParamValues) (any, error)
|
||||
|
||||
// Process the response
|
||||
result := t.processValidationResponse(response, source)
|
||||
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (t Tool) processValidationResponse(response *firebaserules.TestRulesetResponse, source string) ValidationResult {
|
||||
if len(response.Issues) == 0 {
|
||||
return ValidationResult{
|
||||
Valid: true,
|
||||
IssueCount: 0,
|
||||
Valid: true,
|
||||
IssueCount: 0,
|
||||
FormattedIssues: "✓ No errors detected. Rules are valid.",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,13 +12,13 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package neo4j
|
||||
package neo4jcypher
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
yaml "github.com/goccy/go-yaml"
|
||||
"github.com/goccy/go-yaml"
|
||||
neo4jsc "github.com/googleapis/genai-toolbox/internal/sources/neo4j"
|
||||
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
|
||||
|
||||
@@ -12,17 +12,16 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package neo4j_test
|
||||
package neo4jcypher
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
yaml "github.com/goccy/go-yaml"
|
||||
"github.com/goccy/go-yaml"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/googleapis/genai-toolbox/internal/server"
|
||||
"github.com/googleapis/genai-toolbox/internal/testutils"
|
||||
"github.com/googleapis/genai-toolbox/internal/tools"
|
||||
"github.com/googleapis/genai-toolbox/internal/tools/neo4j"
|
||||
)
|
||||
|
||||
func TestParseFromYamlNeo4j(t *testing.T) {
|
||||
@@ -54,7 +53,7 @@ func TestParseFromYamlNeo4j(t *testing.T) {
|
||||
description: country parameter description
|
||||
`,
|
||||
want: server.ToolConfigs{
|
||||
"example_tool": neo4j.Config{
|
||||
"example_tool": Config{
|
||||
Name: "example_tool",
|
||||
Kind: "neo4j-cypher",
|
||||
Source: "my-neo4j-instance",
|
||||
@@ -74,7 +73,7 @@ func TestParseFromYamlNeo4j(t *testing.T) {
|
||||
Tools server.ToolConfigs `yaml:"tools"`
|
||||
}{}
|
||||
// Parse contents
|
||||
err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got)
|
||||
err = yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to unmarshal: %s", err)
|
||||
}
|
||||
@@ -135,7 +135,7 @@ func TestAlloyDBPgToolEndpoints(t *testing.T) {
|
||||
tableNameTemplateParam := "template_param_table_" + strings.ReplaceAll(uuid.New().String(), "-", "")
|
||||
|
||||
// set up data for param tool
|
||||
createParamTableStmt, insertParamTableStmt, paramToolStmt, paramToolStmt2, arrayToolStmt, paramTestParams := tests.GetPostgresSQLParamToolInfo(tableNameParam)
|
||||
createParamTableStmt, insertParamTableStmt, paramToolStmt, idParamToolStmt, nameParamToolStmt, arrayToolStmt, paramTestParams := tests.GetPostgresSQLParamToolInfo(tableNameParam)
|
||||
teardownTable1 := tests.SetupPostgresSQLTable(t, ctx, pool, createParamTableStmt, insertParamTableStmt, tableNameParam, paramTestParams)
|
||||
defer teardownTable1(t)
|
||||
|
||||
@@ -145,7 +145,7 @@ func TestAlloyDBPgToolEndpoints(t *testing.T) {
|
||||
defer teardownTable2(t)
|
||||
|
||||
// Write config into a file and pass it to command
|
||||
toolsFile := tests.GetToolsConfig(sourceConfig, AlloyDBPostgresToolKind, paramToolStmt, paramToolStmt2, arrayToolStmt, authToolStmt)
|
||||
toolsFile := tests.GetToolsConfig(sourceConfig, AlloyDBPostgresToolKind, paramToolStmt, idParamToolStmt, nameParamToolStmt, arrayToolStmt, authToolStmt)
|
||||
toolsFile = tests.AddPgExecuteSqlConfig(t, toolsFile)
|
||||
tmplSelectCombined, tmplSelectFilterCombined := tests.GetPostgresSQLTmplToolStatement()
|
||||
toolsFile = tests.AddTemplateParamConfig(t, toolsFile, AlloyDBPostgresToolKind, tmplSelectCombined, tmplSelectFilterCombined, "")
|
||||
@@ -167,8 +167,8 @@ func TestAlloyDBPgToolEndpoints(t *testing.T) {
|
||||
tests.RunToolGetTest(t)
|
||||
|
||||
select1Want, failInvocationWant, createTableStatement := tests.GetPostgresWants()
|
||||
invokeParamWant, invokeParamWantNull, mcpInvokeParamWant := tests.GetNonSpannerInvokeParamWant()
|
||||
tests.RunToolInvokeTest(t, select1Want, invokeParamWant, invokeParamWantNull, true)
|
||||
invokeParamWant, invokeIdNullWant, nullWant, mcpInvokeParamWant := tests.GetNonSpannerInvokeParamWant()
|
||||
tests.RunToolInvokeTest(t, select1Want, invokeParamWant, invokeIdNullWant, nullWant, true, true)
|
||||
tests.RunExecuteSqlToolInvokeTest(t, createTableStatement, select1Want)
|
||||
tests.RunMCPToolCallMethod(t, mcpInvokeParamWant, failInvocationWant)
|
||||
tests.RunToolInvokeWithTemplateParameters(t, tableNameTemplateParam, tests.NewTemplateParameterTestConfig())
|
||||
|
||||
@@ -102,7 +102,7 @@ func TestBigQueryToolEndpoints(t *testing.T) {
|
||||
)
|
||||
|
||||
// set up data for param tool
|
||||
createParamTableStmt, insertParamTableStmt, paramToolStmt, paramToolStmt2, arrayToolStmt, paramTestParams := getBigQueryParamToolInfo(tableNameParam)
|
||||
createParamTableStmt, insertParamTableStmt, paramToolStmt, idParamToolStmt, nameParamToolStmt, arrayToolStmt, paramTestParams := getBigQueryParamToolInfo(tableNameParam)
|
||||
teardownTable1 := setupBigQueryTable(t, ctx, client, createParamTableStmt, insertParamTableStmt, datasetName, tableNameParam, paramTestParams)
|
||||
defer teardownTable1(t)
|
||||
|
||||
@@ -112,7 +112,7 @@ func TestBigQueryToolEndpoints(t *testing.T) {
|
||||
defer teardownTable2(t)
|
||||
|
||||
// Write config into a file and pass it to command
|
||||
toolsFile := tests.GetToolsConfig(sourceConfig, BigqueryToolKind, paramToolStmt, paramToolStmt2, arrayToolStmt, authToolStmt)
|
||||
toolsFile := tests.GetToolsConfig(sourceConfig, BigqueryToolKind, paramToolStmt, idParamToolStmt, nameParamToolStmt, arrayToolStmt, authToolStmt)
|
||||
toolsFile = addBigQueryPrebuiltToolsConfig(t, toolsFile)
|
||||
tmplSelectCombined, tmplSelectFilterCombined := getBigQueryTmplToolStatement()
|
||||
toolsFile = tests.AddTemplateParamConfig(t, toolsFile, BigqueryToolKind, tmplSelectCombined, tmplSelectFilterCombined, "")
|
||||
@@ -138,8 +138,8 @@ func TestBigQueryToolEndpoints(t *testing.T) {
|
||||
failInvocationWant := `{"jsonrpc":"2.0","id":"invoke-fail-tool","result":{"content":[{"type":"text","text":"unable to execute query: googleapi: Error 400: Syntax error: Unexpected identifier \"SELEC\" at [1:1]`
|
||||
datasetInfoWant := "\"Location\":\"US\",\"DefaultTableExpiration\":0,\"Labels\":null,\"Access\":"
|
||||
tableInfoWant := "{\"Name\":\"\",\"Location\":\"US\",\"Description\":\"\",\"Schema\":[{\"Name\":\"id\""
|
||||
invokeParamWant, invokeParamWantNull, mcpInvokeParamWant := tests.GetNonSpannerInvokeParamWant()
|
||||
tests.RunToolInvokeTest(t, select1Want, invokeParamWant, invokeParamWantNull, true)
|
||||
invokeParamWant, invokeIdNullWant, nullWant, mcpInvokeParamWant := tests.GetNonSpannerInvokeParamWant()
|
||||
tests.RunToolInvokeTest(t, select1Want, invokeParamWant, invokeIdNullWant, nullWant, false, true)
|
||||
tests.RunMCPToolCallMethod(t, mcpInvokeParamWant, failInvocationWant)
|
||||
templateParamTestConfig := tests.NewTemplateParameterTestConfig(
|
||||
tests.WithCreateColArray(`["id INT64", "name STRING", "age INT64"]`),
|
||||
@@ -153,14 +153,15 @@ func TestBigQueryToolEndpoints(t *testing.T) {
|
||||
runBigQueryGetTableInfoToolInvokeTest(t, datasetName, tableName, tableInfoWant)
|
||||
}
|
||||
|
||||
// getBigQueryParamToolInfo returns statements and param for my-param-tool for bigquery kind
|
||||
func getBigQueryParamToolInfo(tableName string) (string, string, string, string, string, []bigqueryapi.QueryParameter) {
|
||||
// getBigQueryParamToolInfo returns statements and param for my-tool for bigquery kind
|
||||
func getBigQueryParamToolInfo(tableName string) (string, string, string, string, string, string, []bigqueryapi.QueryParameter) {
|
||||
createStatement := fmt.Sprintf(`
|
||||
CREATE TABLE IF NOT EXISTS %s (id INT64, name STRING);`, tableName)
|
||||
insertStatement := fmt.Sprintf(`
|
||||
INSERT INTO %s (id, name) VALUES (?, ?), (?, ?), (?, ?), (?, NULL);`, tableName)
|
||||
toolStatement := fmt.Sprintf(`SELECT * FROM %s WHERE id = ? OR name = ? ORDER BY id;`, tableName)
|
||||
toolStatement2 := fmt.Sprintf(`SELECT * FROM %s WHERE id = ? ORDER BY id;`, tableName)
|
||||
idToolStatement := fmt.Sprintf(`SELECT * FROM %s WHERE id = ? ORDER BY id;`, tableName)
|
||||
nameToolStatement := fmt.Sprintf(`SELECT * FROM %s WHERE name = ? ORDER BY id;`, tableName)
|
||||
arrayToolStatememt := fmt.Sprintf(`SELECT * FROM %s WHERE id IN UNNEST(@idArray) AND name IN UNNEST(@nameArray) ORDER BY id;`, tableName)
|
||||
params := []bigqueryapi.QueryParameter{
|
||||
{Value: int64(1)}, {Value: "Alice"},
|
||||
@@ -168,7 +169,7 @@ func getBigQueryParamToolInfo(tableName string) (string, string, string, string,
|
||||
{Value: int64(3)}, {Value: "Sid"},
|
||||
{Value: int64(4)},
|
||||
}
|
||||
return createStatement, insertStatement, toolStatement, toolStatement2, arrayToolStatememt, params
|
||||
return createStatement, insertStatement, toolStatement, idToolStatement, nameToolStatement, arrayToolStatememt, params
|
||||
}
|
||||
|
||||
// getBigQueryAuthToolInfo returns statements and param of my-auth-tool for bigquery kind
|
||||
|
||||
@@ -79,7 +79,8 @@ func TestBigtableToolEndpoints(t *testing.T) {
|
||||
// Do not change the shape of statement without checking tests/common_test.go.
|
||||
// The structure and value of seed data has to match https://github.com/googleapis/genai-toolbox/blob/4dba0df12dc438eca3cb476ef52aa17cdf232c12/tests/common_test.go#L200-L251
|
||||
paramTestStatement := fmt.Sprintf("SELECT TO_INT64(cf['id']) as id, CAST(cf['name'] AS string) as name, FROM %s WHERE TO_INT64(cf['id']) = @id OR CAST(cf['name'] AS string) = @name;", tableName)
|
||||
paramTestStatement2 := fmt.Sprintf("SELECT TO_INT64(cf['id']) as id, CAST(cf['name'] AS string) as name, FROM %s WHERE TO_INT64(cf['id']) = @id;", tableName)
|
||||
idParamTestStatement := fmt.Sprintf("SELECT TO_INT64(cf['id']) as id, CAST(cf['name'] AS string) as name, FROM %s WHERE TO_INT64(cf['id']) = @id;", tableName)
|
||||
nameParamTestStatement := fmt.Sprintf("SELECT TO_INT64(cf['id']) as id, CAST(cf['name'] AS string) as name, FROM %s WHERE CAST(cf['name'] AS string) = @name;", tableName)
|
||||
arrayTestStatement := fmt.Sprintf(
|
||||
"SELECT TO_INT64(cf['id']) AS id, CAST(cf['name'] AS string) AS name FROM %s WHERE TO_INT64(cf['id']) IN UNNEST(@idArray) AND CAST(cf['name'] AS string) IN UNNEST(@nameArray);",
|
||||
tableName,
|
||||
@@ -98,7 +99,7 @@ func TestBigtableToolEndpoints(t *testing.T) {
|
||||
defer teardownTableTmpl(t)
|
||||
|
||||
// Write config into a file and pass it to command
|
||||
toolsFile := tests.GetToolsConfig(sourceConfig, BigtableToolKind, paramTestStatement, paramTestStatement2, arrayTestStatement, authToolStatement)
|
||||
toolsFile := tests.GetToolsConfig(sourceConfig, BigtableToolKind, paramTestStatement, idParamTestStatement, nameParamTestStatement, arrayTestStatement, authToolStatement)
|
||||
toolsFile = addTemplateParamConfig(t, toolsFile)
|
||||
|
||||
cmd, cleanup, err := tests.StartCmd(ctx, toolsFile, args...)
|
||||
@@ -120,9 +121,9 @@ func TestBigtableToolEndpoints(t *testing.T) {
|
||||
// Actual test parameters are set in https://github.com/googleapis/genai-toolbox/blob/52b09a67cb40ac0c5f461598b4673136699a3089/tests/tool_test.go#L250
|
||||
select1Want := "[{\"$col1\":1}]"
|
||||
failInvocationWant := `{"jsonrpc":"2.0","id":"invoke-fail-tool","result":{"content":[{"type":"text","text":"unable to prepare statement: rpc error: code = InvalidArgument desc = Syntax error: Unexpected identifier \"SELEC\" [at 1:1]"}],"isError":true}}`
|
||||
invokeParamWant, _, mcpInvokeParamWant := tests.GetNonSpannerInvokeParamWant()
|
||||
invokeParamWantNull := `[{"id":4,"name":""}]`
|
||||
tests.RunToolInvokeTest(t, select1Want, invokeParamWant, invokeParamWantNull, true)
|
||||
invokeParamWant, _, nullWant, mcpInvokeParamWant := tests.GetNonSpannerInvokeParamWant()
|
||||
invokeIdNullWant := `[{"id":4,"name":""}]`
|
||||
tests.RunToolInvokeTest(t, select1Want, invokeParamWant, invokeIdNullWant, nullWant, true, true)
|
||||
tests.RunMCPToolCallMethod(t, mcpInvokeParamWant, failInvocationWant)
|
||||
|
||||
templateParamTestConfig := tests.NewTemplateParameterTestConfig(
|
||||
|
||||
@@ -129,7 +129,7 @@ func TestCloudSQLMSSQLToolEndpoints(t *testing.T) {
|
||||
tableNameTemplateParam := "template_param_table_" + strings.ReplaceAll(uuid.New().String(), "-", "")
|
||||
|
||||
// set up data for param tool
|
||||
createParamTableStmt, insertParamTableStmt, paramToolStmt, paramToolStmt2, arrayToolStmt, paramTestParams := tests.GetMSSQLParamToolInfo(tableNameParam)
|
||||
createParamTableStmt, insertParamTableStmt, paramToolStmt, idParamToolStmt, nameParamToolStmt, arrayToolStmt, paramTestParams := tests.GetMSSQLParamToolInfo(tableNameParam)
|
||||
teardownTable1 := tests.SetupMsSQLTable(t, ctx, db, createParamTableStmt, insertParamTableStmt, tableNameParam, paramTestParams)
|
||||
defer teardownTable1(t)
|
||||
|
||||
@@ -139,7 +139,7 @@ func TestCloudSQLMSSQLToolEndpoints(t *testing.T) {
|
||||
defer teardownTable2(t)
|
||||
|
||||
// Write config into a file and pass it to command
|
||||
toolsFile := tests.GetToolsConfig(sourceConfig, CloudSQLMSSQLToolKind, paramToolStmt, paramToolStmt2, arrayToolStmt, authToolStmt)
|
||||
toolsFile := tests.GetToolsConfig(sourceConfig, CloudSQLMSSQLToolKind, paramToolStmt, idParamToolStmt, nameParamToolStmt, arrayToolStmt, authToolStmt)
|
||||
toolsFile = tests.AddMSSQLExecuteSqlConfig(t, toolsFile)
|
||||
tmplSelectCombined, tmplSelectFilterCombined := tests.GetMSSQLTmplToolStatement()
|
||||
toolsFile = tests.AddTemplateParamConfig(t, toolsFile, CloudSQLMSSQLToolKind, tmplSelectCombined, tmplSelectFilterCombined, "")
|
||||
@@ -161,8 +161,8 @@ func TestCloudSQLMSSQLToolEndpoints(t *testing.T) {
|
||||
tests.RunToolGetTest(t)
|
||||
|
||||
select1Want, failInvocationWant, createTableStatement := tests.GetMSSQLWants()
|
||||
invokeParamWant, invokeParamWantNull, mcpInvokeParamWant := tests.GetNonSpannerInvokeParamWant()
|
||||
tests.RunToolInvokeTest(t, select1Want, invokeParamWant, invokeParamWantNull, false)
|
||||
invokeParamWant, invokeIdNullWant, nullWant, mcpInvokeParamWant := tests.GetNonSpannerInvokeParamWant()
|
||||
tests.RunToolInvokeTest(t, select1Want, invokeParamWant, invokeIdNullWant, nullWant, true, false)
|
||||
tests.RunExecuteSqlToolInvokeTest(t, createTableStatement, select1Want)
|
||||
tests.RunMCPToolCallMethod(t, mcpInvokeParamWant, failInvocationWant)
|
||||
tests.RunToolInvokeWithTemplateParameters(t, tableNameTemplateParam, tests.NewTemplateParameterTestConfig())
|
||||
|
||||
@@ -116,7 +116,7 @@ func TestCloudSQLMySQLToolEndpoints(t *testing.T) {
|
||||
tableNameTemplateParam := "template_param_table_" + strings.ReplaceAll(uuid.New().String(), "-", "")
|
||||
|
||||
// set up data for param tool
|
||||
createParamTableStmt, insertParamTableStmt, paramToolStmt, paramToolStmt2, arrayToolStmt, paramTestParams := tests.GetMySQLParamToolInfo(tableNameParam)
|
||||
createParamTableStmt, insertParamTableStmt, paramToolStmt, idParamToolStmt, nameParamToolStmt, arrayToolStmt, paramTestParams := tests.GetMySQLParamToolInfo(tableNameParam)
|
||||
teardownTable1 := tests.SetupMySQLTable(t, ctx, pool, createParamTableStmt, insertParamTableStmt, tableNameParam, paramTestParams)
|
||||
defer teardownTable1(t)
|
||||
|
||||
@@ -126,7 +126,7 @@ func TestCloudSQLMySQLToolEndpoints(t *testing.T) {
|
||||
defer teardownTable2(t)
|
||||
|
||||
// Write config into a file and pass it to command
|
||||
toolsFile := tests.GetToolsConfig(sourceConfig, CloudSQLMySQLToolKind, paramToolStmt, paramToolStmt2, arrayToolStmt, authToolStmt)
|
||||
toolsFile := tests.GetToolsConfig(sourceConfig, CloudSQLMySQLToolKind, paramToolStmt, idParamToolStmt, nameParamToolStmt, arrayToolStmt, authToolStmt)
|
||||
toolsFile = tests.AddMySqlExecuteSqlConfig(t, toolsFile)
|
||||
tmplSelectCombined, tmplSelectFilterCombined := tests.GetMySQLTmplToolStatement()
|
||||
toolsFile = tests.AddTemplateParamConfig(t, toolsFile, CloudSQLMySQLToolKind, tmplSelectCombined, tmplSelectFilterCombined, "")
|
||||
@@ -148,8 +148,8 @@ func TestCloudSQLMySQLToolEndpoints(t *testing.T) {
|
||||
tests.RunToolGetTest(t)
|
||||
|
||||
select1Want, failInvocationWant, createTableStatement := tests.GetMySQLWants()
|
||||
invokeParamWant, invokeParamWantNull, mcpInvokeParamWant := tests.GetNonSpannerInvokeParamWant()
|
||||
tests.RunToolInvokeTest(t, select1Want, invokeParamWant, invokeParamWantNull, false)
|
||||
invokeParamWant, invokeIdNullWant, nullWant, mcpInvokeParamWant := tests.GetNonSpannerInvokeParamWant()
|
||||
tests.RunToolInvokeTest(t, select1Want, invokeParamWant, invokeIdNullWant, nullWant, true, false)
|
||||
tests.RunExecuteSqlToolInvokeTest(t, createTableStatement, select1Want)
|
||||
tests.RunMCPToolCallMethod(t, mcpInvokeParamWant, failInvocationWant)
|
||||
tests.RunToolInvokeWithTemplateParameters(t, tableNameTemplateParam, tests.NewTemplateParameterTestConfig())
|
||||
|
||||
@@ -120,7 +120,7 @@ func TestCloudSQLPgSimpleToolEndpoints(t *testing.T) {
|
||||
tableNameTemplateParam := "template_param_table_" + strings.ReplaceAll(uuid.New().String(), "-", "")
|
||||
|
||||
// set up data for param tool
|
||||
createParamTableStmt, insertParamTableStmt, paramToolStmt, paramToolStmt2, arrayToolStmt, paramTestParams := tests.GetPostgresSQLParamToolInfo(tableNameParam)
|
||||
createParamTableStmt, insertParamTableStmt, paramToolStmt, idParamToolStmt, nameParamToolStmt, arrayToolStmt, paramTestParams := tests.GetPostgresSQLParamToolInfo(tableNameParam)
|
||||
teardownTable1 := tests.SetupPostgresSQLTable(t, ctx, pool, createParamTableStmt, insertParamTableStmt, tableNameParam, paramTestParams)
|
||||
defer teardownTable1(t)
|
||||
|
||||
@@ -130,7 +130,7 @@ func TestCloudSQLPgSimpleToolEndpoints(t *testing.T) {
|
||||
defer teardownTable2(t)
|
||||
|
||||
// Write config into a file and pass it to command
|
||||
toolsFile := tests.GetToolsConfig(sourceConfig, CloudSQLPostgresToolKind, paramToolStmt, paramToolStmt2, arrayToolStmt, authToolStmt)
|
||||
toolsFile := tests.GetToolsConfig(sourceConfig, CloudSQLPostgresToolKind, paramToolStmt, idParamToolStmt, nameParamToolStmt, arrayToolStmt, authToolStmt)
|
||||
toolsFile = tests.AddPgExecuteSqlConfig(t, toolsFile)
|
||||
tmplSelectCombined, tmplSelectFilterCombined := tests.GetPostgresSQLTmplToolStatement()
|
||||
toolsFile = tests.AddTemplateParamConfig(t, toolsFile, CloudSQLPostgresToolKind, tmplSelectCombined, tmplSelectFilterCombined, "")
|
||||
@@ -152,8 +152,8 @@ func TestCloudSQLPgSimpleToolEndpoints(t *testing.T) {
|
||||
tests.RunToolGetTest(t)
|
||||
|
||||
select1Want, failInvocationWant, createTableStatement := tests.GetPostgresWants()
|
||||
invokeParamWant, invokeParamWantNull, mcpInvokeParamWant := tests.GetNonSpannerInvokeParamWant()
|
||||
tests.RunToolInvokeTest(t, select1Want, invokeParamWant, invokeParamWantNull, true)
|
||||
invokeParamWant, invokeIdNullWant, nullWant, mcpInvokeParamWant := tests.GetNonSpannerInvokeParamWant()
|
||||
tests.RunToolInvokeTest(t, select1Want, invokeParamWant, invokeIdNullWant, nullWant, true, true)
|
||||
tests.RunExecuteSqlToolInvokeTest(t, createTableStatement, select1Want)
|
||||
tests.RunMCPToolCallMethod(t, mcpInvokeParamWant, failInvocationWant)
|
||||
tests.RunToolInvokeWithTemplateParameters(t, tableNameTemplateParam, tests.NewTemplateParameterTestConfig())
|
||||
|
||||
@@ -28,7 +28,7 @@ import (
|
||||
)
|
||||
|
||||
// GetToolsConfig returns a mock tools config file
|
||||
func GetToolsConfig(sourceConfig map[string]any, toolKind, paramToolStatement, paramToolStatement2, arrayToolStatement, authToolStatement string) map[string]any {
|
||||
func GetToolsConfig(sourceConfig map[string]any, toolKind, paramToolStatement, idParamToolStmt, nameParamToolStmt, arrayToolStatement, authToolStatement string) map[string]any {
|
||||
// Write config into a file and pass it to command
|
||||
toolsFile := map[string]any{
|
||||
"sources": map[string]any{
|
||||
@@ -47,7 +47,7 @@ func GetToolsConfig(sourceConfig map[string]any, toolKind, paramToolStatement, p
|
||||
"description": "Simple tool to test end to end functionality.",
|
||||
"statement": "SELECT 1;",
|
||||
},
|
||||
"my-param-tool": map[string]any{
|
||||
"my-tool": map[string]any{
|
||||
"kind": toolKind,
|
||||
"source": "my-instance",
|
||||
"description": "Tool to test invocation with params.",
|
||||
@@ -65,11 +65,11 @@ func GetToolsConfig(sourceConfig map[string]any, toolKind, paramToolStatement, p
|
||||
},
|
||||
},
|
||||
},
|
||||
"my-param-tool2": map[string]any{
|
||||
"my-tool-by-id": map[string]any{
|
||||
"kind": toolKind,
|
||||
"source": "my-instance",
|
||||
"description": "Tool to test invocation with params.",
|
||||
"statement": paramToolStatement2,
|
||||
"statement": idParamToolStmt,
|
||||
"parameters": []any{
|
||||
map[string]any{
|
||||
"name": "id",
|
||||
@@ -78,6 +78,20 @@ func GetToolsConfig(sourceConfig map[string]any, toolKind, paramToolStatement, p
|
||||
},
|
||||
},
|
||||
},
|
||||
"my-tool-by-name": map[string]any{
|
||||
"kind": toolKind,
|
||||
"source": "my-instance",
|
||||
"description": "Tool to test invocation with params.",
|
||||
"statement": nameParamToolStmt,
|
||||
"parameters": []any{
|
||||
map[string]any{
|
||||
"name": "name",
|
||||
"type": "string",
|
||||
"description": "user name",
|
||||
"required": false,
|
||||
},
|
||||
},
|
||||
},
|
||||
"my-array-tool": map[string]any{
|
||||
"kind": toolKind,
|
||||
"source": "my-instance",
|
||||
@@ -301,15 +315,16 @@ func AddMSSQLExecuteSqlConfig(t *testing.T, config map[string]any) map[string]an
|
||||
return config
|
||||
}
|
||||
|
||||
// GetPostgresSQLParamToolInfo returns statements and param for my-param-tool postgres-sql kind
|
||||
func GetPostgresSQLParamToolInfo(tableName string) (string, string, string, string, string, []any) {
|
||||
// GetPostgresSQLParamToolInfo returns statements and param for my-tool postgres-sql kind
|
||||
func GetPostgresSQLParamToolInfo(tableName string) (string, string, string, string, string, string, []any) {
|
||||
createStatement := fmt.Sprintf("CREATE TABLE %s (id SERIAL PRIMARY KEY, name TEXT);", tableName)
|
||||
insertStatement := fmt.Sprintf("INSERT INTO %s (name) VALUES ($1), ($2), ($3), ($4);", tableName)
|
||||
toolStatement := fmt.Sprintf("SELECT * FROM %s WHERE id = $1 OR name = $2;", tableName)
|
||||
toolStatement2 := fmt.Sprintf("SELECT * FROM %s WHERE id = $1;", tableName)
|
||||
idParamStatement := fmt.Sprintf("SELECT * FROM %s WHERE id = $1;", tableName)
|
||||
nameParamStatement := fmt.Sprintf("SELECT * FROM %s WHERE name = $1;", tableName)
|
||||
arrayToolStatement := fmt.Sprintf("SELECT * FROM %s WHERE id = ANY($1) AND name = ANY($2);", tableName)
|
||||
params := []any{"Alice", "Jane", "Sid", nil}
|
||||
return createStatement, insertStatement, toolStatement, toolStatement2, arrayToolStatement, params
|
||||
return createStatement, insertStatement, toolStatement, idParamStatement, nameParamStatement, arrayToolStatement, params
|
||||
}
|
||||
|
||||
// GetPostgresSQLAuthToolInfo returns statements and param of my-auth-tool for postgres-sql kind
|
||||
@@ -328,15 +343,16 @@ func GetPostgresSQLTmplToolStatement() (string, string) {
|
||||
return tmplSelectCombined, tmplSelectFilterCombined
|
||||
}
|
||||
|
||||
// GetMSSQLParamToolInfo returns statements and param for my-param-tool mssql-sql kind
|
||||
func GetMSSQLParamToolInfo(tableName string) (string, string, string, string, string, []any) {
|
||||
// GetMSSQLParamToolInfo returns statements and param for my-tool mssql-sql kind
|
||||
func GetMSSQLParamToolInfo(tableName string) (string, string, string, string, string, string, []any) {
|
||||
createStatement := fmt.Sprintf("CREATE TABLE %s (id INT IDENTITY(1,1) PRIMARY KEY, name VARCHAR(255));", tableName)
|
||||
insertStatement := fmt.Sprintf("INSERT INTO %s (name) VALUES (@alice), (@jane), (@sid), (@nil);", tableName)
|
||||
toolStatement := fmt.Sprintf("SELECT * FROM %s WHERE id = @id OR name = @p2;", tableName)
|
||||
toolStatement2 := fmt.Sprintf("SELECT * FROM %s WHERE id = @id;", tableName)
|
||||
idParamStatement := fmt.Sprintf("SELECT * FROM %s WHERE id = @id;", tableName)
|
||||
nameParamStatement := fmt.Sprintf("SELECT * FROM %s WHERE name = @name;", tableName)
|
||||
arrayToolStatement := fmt.Sprintf("SELECT * FROM %s WHERE id = ANY(@idArray) OR name = ANY(@p2);", tableName)
|
||||
params := []any{sql.Named("alice", "Alice"), sql.Named("jane", "Jane"), sql.Named("sid", "Sid"), sql.Named("nil", nil)}
|
||||
return createStatement, insertStatement, toolStatement, toolStatement2, arrayToolStatement, params
|
||||
return createStatement, insertStatement, toolStatement, idParamStatement, nameParamStatement, arrayToolStatement, params
|
||||
}
|
||||
|
||||
// GetMSSQLAuthToolInfo returns statements and param of my-auth-tool for mssql-sql kind
|
||||
@@ -355,15 +371,16 @@ func GetMSSQLTmplToolStatement() (string, string) {
|
||||
return tmplSelectCombined, tmplSelectFilterCombined
|
||||
}
|
||||
|
||||
// GetMySQLParamToolInfo returns statements and param for my-param-tool mysql-sql kind
|
||||
func GetMySQLParamToolInfo(tableName string) (string, string, string, string, string, []any) {
|
||||
// GetMySQLParamToolInfo returns statements and param for my-tool mysql-sql kind
|
||||
func GetMySQLParamToolInfo(tableName string) (string, string, string, string, string, string, []any) {
|
||||
createStatement := fmt.Sprintf("CREATE TABLE %s (id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255));", tableName)
|
||||
insertStatement := fmt.Sprintf("INSERT INTO %s (name) VALUES (?), (?), (?), (?);", tableName)
|
||||
toolStatement := fmt.Sprintf("SELECT * FROM %s WHERE id = ? OR name = ?;", tableName)
|
||||
toolStatement2 := fmt.Sprintf("SELECT * FROM %s WHERE id = ?;", tableName)
|
||||
idParamStatement := fmt.Sprintf("SELECT * FROM %s WHERE id = ?;", tableName)
|
||||
nameParamStatement := fmt.Sprintf("SELECT * FROM %s WHERE name = ?;", tableName)
|
||||
arrayToolStatement := fmt.Sprintf("SELECT * FROM %s WHERE id = ANY(?) AND name = ANY(?);", tableName)
|
||||
params := []any{"Alice", "Jane", "Sid", nil}
|
||||
return createStatement, insertStatement, toolStatement, toolStatement2, arrayToolStatement, params
|
||||
return createStatement, insertStatement, toolStatement, idParamStatement, nameParamStatement, arrayToolStatement, params
|
||||
}
|
||||
|
||||
// GetMySQLAuthToolInfo returns statements and param of my-auth-tool for mysql-sql kind
|
||||
@@ -382,11 +399,12 @@ func GetMySQLTmplToolStatement() (string, string) {
|
||||
return tmplSelectCombined, tmplSelectFilterCombined
|
||||
}
|
||||
|
||||
func GetNonSpannerInvokeParamWant() (string, string, string) {
|
||||
func GetNonSpannerInvokeParamWant() (string, string, string, string) {
|
||||
invokeParamWant := "[{\"id\":1,\"name\":\"Alice\"},{\"id\":3,\"name\":\"Sid\"}]"
|
||||
invokeParamWantNull := "[{\"id\":4,\"name\":null}]"
|
||||
mcpInvokeParamWant := `{"jsonrpc":"2.0","id":"my-param-tool","result":{"content":[{"type":"text","text":"{\"id\":1,\"name\":\"Alice\"}"},{"type":"text","text":"{\"id\":3,\"name\":\"Sid\"}"}]}}`
|
||||
return invokeParamWant, invokeParamWantNull, mcpInvokeParamWant
|
||||
invokeIdNullWant := "[{\"id\":4,\"name\":null}]"
|
||||
nullWant := "null"
|
||||
mcpInvokeParamWant := `{"jsonrpc":"2.0","id":"my-tool","result":{"content":[{"type":"text","text":"{\"id\":1,\"name\":\"Alice\"}"},{"type":"text","text":"{\"id\":3,\"name\":\"Sid\"}"}]}}`
|
||||
return invokeParamWant, invokeIdNullWant, nullWant, mcpInvokeParamWant
|
||||
}
|
||||
|
||||
// GetPostgresWants return the expected wants for postgres
|
||||
@@ -501,13 +519,14 @@ func SetupMySQLTable(t *testing.T, ctx context.Context, pool *sql.DB, createStat
|
||||
}
|
||||
|
||||
// GetRedisWants return the expected wants for redis
|
||||
func GetRedisValkeyWants() (string, string, string, string, string) {
|
||||
func GetRedisValkeyWants() (string, string, string, string, string, string) {
|
||||
select1Want := "[\"PONG\"]"
|
||||
failInvocationWant := `unknown command 'SELEC 1;', with args beginning with: \""}]}}`
|
||||
invokeParamWant := "[{\"id\":\"1\",\"name\":\"Alice\"},{\"id\":\"3\",\"name\":\"Sid\"}]"
|
||||
invokeParamWantNull := `[{"id":"4","name":""}]`
|
||||
mcpInvokeParamWant := `{"jsonrpc":"2.0","id":"my-param-tool","result":{"content":[{"type":"text","text":"{\"id\":\"1\",\"name\":\"Alice\"}"},{"type":"text","text":"{\"id\":\"3\",\"name\":\"Sid\"}"}]}}`
|
||||
return select1Want, failInvocationWant, invokeParamWant, invokeParamWantNull, mcpInvokeParamWant
|
||||
invokeIdNullWant := `[{"id":"4","name":""}]`
|
||||
nullWant := `["null"]`
|
||||
mcpInvokeParamWant := `{"jsonrpc":"2.0","id":"my-tool","result":{"content":[{"type":"text","text":"{\"id\":\"1\",\"name\":\"Alice\"}"},{"type":"text","text":"{\"id\":\"3\",\"name\":\"Sid\"}"}]}}`
|
||||
return select1Want, failInvocationWant, invokeParamWant, invokeIdNullWant, nullWant, mcpInvokeParamWant
|
||||
}
|
||||
|
||||
func GetRedisValkeyToolsConfig(sourceConfig map[string]any, toolKind string) map[string]any {
|
||||
@@ -528,7 +547,7 @@ func GetRedisValkeyToolsConfig(sourceConfig map[string]any, toolKind string) map
|
||||
"description": "Simple tool to test end to end functionality.",
|
||||
"commands": [][]string{{"PING"}},
|
||||
},
|
||||
"my-param-tool": map[string]any{
|
||||
"my-tool": map[string]any{
|
||||
"kind": toolKind,
|
||||
"source": "my-instance",
|
||||
"description": "Tool to test invocation with params.",
|
||||
@@ -546,7 +565,7 @@ func GetRedisValkeyToolsConfig(sourceConfig map[string]any, toolKind string) map
|
||||
},
|
||||
},
|
||||
},
|
||||
"my-param-tool2": map[string]any{
|
||||
"my-tool-by-id": map[string]any{
|
||||
"kind": toolKind,
|
||||
"source": "my-instance",
|
||||
"description": "Tool to test invocation with params.",
|
||||
@@ -559,6 +578,20 @@ func GetRedisValkeyToolsConfig(sourceConfig map[string]any, toolKind string) map
|
||||
},
|
||||
},
|
||||
},
|
||||
"my-tool-by-name": map[string]any{
|
||||
"kind": toolKind,
|
||||
"source": "my-instance",
|
||||
"description": "Tool to test invocation with params.",
|
||||
"commands": [][]string{{"GET", "null"}},
|
||||
"parameters": []any{
|
||||
map[string]any{
|
||||
"name": "name",
|
||||
"type": "string",
|
||||
"description": "user name",
|
||||
"required": false,
|
||||
},
|
||||
},
|
||||
},
|
||||
"my-array-tool": map[string]any{
|
||||
"kind": toolKind,
|
||||
"source": "my-instance",
|
||||
|
||||
@@ -103,7 +103,7 @@ func TestCouchbaseToolEndpoints(t *testing.T) {
|
||||
collectionNameTemplateParam := "template_param_" + strings.ReplaceAll(uuid.New().String(), "-", "")
|
||||
|
||||
// Set up data for param tool
|
||||
paramToolStatement, paramToolStmt2, arrayToolStatement, paramTestParams := getCouchbaseParamToolInfo(collectionNameParam)
|
||||
paramToolStatement, idParamToolStmt, nameParamToolStmt, arrayToolStatement, paramTestParams := getCouchbaseParamToolInfo(collectionNameParam)
|
||||
teardownCollection1 := setupCouchbaseCollection(t, ctx, cluster, couchbaseBucket, couchbaseScope, collectionNameParam, paramTestParams)
|
||||
defer teardownCollection1(t)
|
||||
|
||||
@@ -118,7 +118,7 @@ func TestCouchbaseToolEndpoints(t *testing.T) {
|
||||
defer teardownCollection3(t)
|
||||
|
||||
// Write config into a file and pass it to command
|
||||
toolsFile := tests.GetToolsConfig(sourceConfig, couchbaseToolKind, paramToolStatement, paramToolStmt2, arrayToolStatement, authToolStatement)
|
||||
toolsFile := tests.GetToolsConfig(sourceConfig, couchbaseToolKind, paramToolStatement, idParamToolStmt, nameParamToolStmt, arrayToolStatement, authToolStatement)
|
||||
toolsFile = tests.AddTemplateParamConfig(t, toolsFile, couchbaseToolKind, tmplSelectCombined, tmplSelectFilterCombined, tmplSelectAll)
|
||||
|
||||
cmd, cleanup, err := tests.StartCmd(ctx, toolsFile, args...)
|
||||
@@ -140,8 +140,8 @@ func TestCouchbaseToolEndpoints(t *testing.T) {
|
||||
select1Want := "[{\"$1\":1}]"
|
||||
failMcpInvocationWant := "{\"jsonrpc\":\"2.0\",\"id\":\"invoke-fail-tool\",\"result\":{\"content\":[{\"type\":\"text\",\"text\":\"unable to execute query: parsing failure | {\\\"statement\\\":\\\"SELEC 1;\\\""
|
||||
|
||||
invokeParamWant, invokeParamWantNull, mcpInvokeParamWant := tests.GetNonSpannerInvokeParamWant()
|
||||
tests.RunToolInvokeTest(t, select1Want, invokeParamWant, invokeParamWantNull, true)
|
||||
invokeParamWant, invokeIdNullWant, nullWant, mcpInvokeParamWant := tests.GetNonSpannerInvokeParamWant()
|
||||
tests.RunToolInvokeTest(t, select1Want, invokeParamWant, invokeIdNullWant, nullWant, true, true)
|
||||
tests.RunMCPToolCallMethod(t, mcpInvokeParamWant, failMcpInvocationWant)
|
||||
|
||||
templateParamTestConfig := tests.NewTemplateParameterTestConfig(
|
||||
@@ -230,27 +230,27 @@ func setupCouchbaseCollection(t *testing.T, ctx context.Context, cluster *gocb.C
|
||||
}
|
||||
}
|
||||
|
||||
// getCouchbaseParamToolInfo returns statements and params for my-param-tool couchbase-sql kind
|
||||
func getCouchbaseParamToolInfo(collectionName string) (string, string, string, []map[string]any) {
|
||||
// getCouchbaseParamToolInfo returns statements and params for my-tool couchbase-sql kind
|
||||
func getCouchbaseParamToolInfo(collectionName string) (string, string, string, string, []map[string]any) {
|
||||
// N1QL uses positional or named parameters with $ prefix
|
||||
toolStatement := fmt.Sprintf("SELECT TONUMBER(meta().id) as id, "+
|
||||
"%s.* FROM %s WHERE meta().id = TOSTRING($id) OR name = $name order by meta().id",
|
||||
collectionName, collectionName)
|
||||
|
||||
toolStatement2 := fmt.Sprintf("SELECT TONUMBER(meta().id) as id, "+
|
||||
idToolStatement := fmt.Sprintf("SELECT TONUMBER(meta().id) as id, "+
|
||||
"%s.* FROM %s WHERE meta().id = TOSTRING($id) order by meta().id",
|
||||
collectionName, collectionName)
|
||||
|
||||
nameToolStatement := fmt.Sprintf("SELECT TONUMBER(meta().id) as id, "+
|
||||
"%s.* FROM %s WHERE name = $name order by meta().id",
|
||||
collectionName, collectionName)
|
||||
arrayToolStatemnt := fmt.Sprintf("SELECT TONUMBER(meta().id) as id, "+
|
||||
"%s.* FROM %s WHERE TONUMBER(meta().id) IN $idArray AND name IN $nameArray order by meta().id", collectionName, collectionName)
|
||||
|
||||
params := []map[string]any{
|
||||
{"name": "Alice"},
|
||||
{"name": "Jane"},
|
||||
{"name": "Sid"},
|
||||
{"name": nil},
|
||||
}
|
||||
return toolStatement, toolStatement2, arrayToolStatemnt, params
|
||||
return toolStatement, idToolStatement, nameToolStatement, arrayToolStatemnt, params
|
||||
}
|
||||
|
||||
// getCouchbaseAuthToolInfo returns statements and param of my-auth-tool for couchbase-sql kind
|
||||
|
||||
@@ -62,7 +62,7 @@ func getFirestoreVars(t *testing.T) map[string]any {
|
||||
// initFirestoreConnection creates a Firestore client for testing
|
||||
func initFirestoreConnection(project, database string) (*firestoreapi.Client, error) {
|
||||
ctx := context.Background()
|
||||
|
||||
|
||||
if database == "" {
|
||||
database = "(default)"
|
||||
}
|
||||
@@ -93,20 +93,20 @@ func TestFirestoreToolEndpoints(t *testing.T) {
|
||||
testDocID1 := fmt.Sprintf("doc_%s", strings.ReplaceAll(uuid.New().String(), "-", ""))
|
||||
testDocID2 := fmt.Sprintf("doc_%s", strings.ReplaceAll(uuid.New().String(), "-", ""))
|
||||
testDocID3 := fmt.Sprintf("doc_%s", strings.ReplaceAll(uuid.New().String(), "-", ""))
|
||||
|
||||
|
||||
// Document paths for testing
|
||||
docPath1 := fmt.Sprintf("%s/%s", testCollectionName, testDocID1)
|
||||
docPath2 := fmt.Sprintf("%s/%s", testCollectionName, testDocID2)
|
||||
docPath3 := fmt.Sprintf("%s/%s", testCollectionName, testDocID3)
|
||||
|
||||
// Set up test data
|
||||
teardown := setupFirestoreTestData(t, ctx, client, testCollectionName, testSubCollectionName,
|
||||
teardown := setupFirestoreTestData(t, ctx, client, testCollectionName, testSubCollectionName,
|
||||
testDocID1, testDocID2, testDocID3)
|
||||
defer teardown(t)
|
||||
|
||||
// Write config into a file and pass it to command
|
||||
toolsFile := getFirestoreToolsConfig(sourceConfig)
|
||||
|
||||
|
||||
cmd, cleanup, err := tests.StartCmd(ctx, toolsFile, args...)
|
||||
if err != nil {
|
||||
t.Fatalf("command initialization returned an error: %s", err)
|
||||
@@ -123,7 +123,7 @@ func TestFirestoreToolEndpoints(t *testing.T) {
|
||||
|
||||
// Run Firestore-specific tool get test
|
||||
runFirestoreToolGetTest(t)
|
||||
|
||||
|
||||
// Run Firestore-specific MCP test
|
||||
runFirestoreMCPToolCallMethod(t, docPath1, docPath2)
|
||||
|
||||
@@ -170,7 +170,7 @@ func runFirestoreToolGetTest(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
for _, tc := range tcs {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
resp, err := http.Get(tc.api)
|
||||
@@ -192,7 +192,7 @@ func runFirestoreToolGetTest(t *testing.T) {
|
||||
if !ok {
|
||||
t.Fatalf("unable to find tools in response body")
|
||||
}
|
||||
|
||||
|
||||
// Compare as JSON strings to handle any ordering differences
|
||||
gotJSON, _ := json.Marshal(got)
|
||||
wantJSON, _ := json.Marshal(tc.want)
|
||||
@@ -212,31 +212,31 @@ func runFirestoreValidateRulesTest(t *testing.T) {
|
||||
isErr bool
|
||||
}{
|
||||
{
|
||||
name: "validate valid rules",
|
||||
api: "http://127.0.0.1:5000/api/tool/firestore-validate-rules/invoke",
|
||||
name: "validate valid rules",
|
||||
api: "http://127.0.0.1:5000/api/tool/firestore-validate-rules/invoke",
|
||||
requestBody: bytes.NewBuffer([]byte(`{
|
||||
"source": "rules_version = '2';\nservice cloud.firestore {\n match /databases/{database}/documents {\n match /{document=**} {\n allow read, write: if true;\n }\n }\n}"
|
||||
}`)),
|
||||
wantRegex: `"valid":true.*"issueCount":0`,
|
||||
isErr: false,
|
||||
wantRegex: `"valid":true.*"issueCount":0`,
|
||||
isErr: false,
|
||||
},
|
||||
{
|
||||
name: "validate rules with syntax error",
|
||||
api: "http://127.0.0.1:5000/api/tool/firestore-validate-rules/invoke",
|
||||
name: "validate rules with syntax error",
|
||||
api: "http://127.0.0.1:5000/api/tool/firestore-validate-rules/invoke",
|
||||
requestBody: bytes.NewBuffer([]byte(`{
|
||||
"source": "rules_version = '2';\nservice cloud.firestore {\n match /databases/{database}/documents {\n match /{document=**} {\n allow read, write: if true;;\n }\n }\n}"
|
||||
}`)),
|
||||
wantRegex: `"valid":false.*"issueCount":[1-9]`,
|
||||
isErr: false,
|
||||
wantRegex: `"valid":false.*"issueCount":[1-9]`,
|
||||
isErr: false,
|
||||
},
|
||||
{
|
||||
name: "validate rules with missing version",
|
||||
api: "http://127.0.0.1:5000/api/tool/firestore-validate-rules/invoke",
|
||||
name: "validate rules with missing version",
|
||||
api: "http://127.0.0.1:5000/api/tool/firestore-validate-rules/invoke",
|
||||
requestBody: bytes.NewBuffer([]byte(`{
|
||||
"source": "service cloud.firestore {\n match /databases/{database}/documents {\n match /{document=**} {\n allow read, write: if true;\n }\n }\n}"
|
||||
}`)),
|
||||
wantRegex: `"valid":false.*"issueCount":[1-9]`,
|
||||
isErr: false,
|
||||
wantRegex: `"valid":false.*"issueCount":[1-9]`,
|
||||
isErr: false,
|
||||
},
|
||||
{
|
||||
name: "validate empty rules",
|
||||
@@ -259,7 +259,7 @@ func runFirestoreValidateRulesTest(t *testing.T) {
|
||||
t.Fatalf("unable to create request: %s", err)
|
||||
}
|
||||
req.Header.Add("Content-type", "application/json")
|
||||
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to send request: %s", err)
|
||||
@@ -322,7 +322,7 @@ func runFirestoreGetRulesTest(t *testing.T) {
|
||||
t.Fatalf("unable to create request: %s", err)
|
||||
}
|
||||
req.Header.Add("Content-type", "application/json")
|
||||
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to send request: %s", err)
|
||||
@@ -400,7 +400,7 @@ func runFirestoreMCPToolCallMethod(t *testing.T, docPath1, docPath2 string) {
|
||||
},
|
||||
},
|
||||
wantContains: `\"name\":\"Alice\"`,
|
||||
wantError: false,
|
||||
wantError: false,
|
||||
},
|
||||
{
|
||||
name: "MCP Invoke invalid tool",
|
||||
@@ -418,7 +418,7 @@ func runFirestoreMCPToolCallMethod(t *testing.T, docPath1, docPath2 string) {
|
||||
},
|
||||
},
|
||||
wantContains: `tool with name \"foo\" does not exist`,
|
||||
wantError: true,
|
||||
wantError: true,
|
||||
},
|
||||
{
|
||||
name: "MCP Invoke my-param-tool without parameters",
|
||||
@@ -436,7 +436,7 @@ func runFirestoreMCPToolCallMethod(t *testing.T, docPath1, docPath2 string) {
|
||||
},
|
||||
},
|
||||
wantContains: `parameter \"documentPaths\" is required`,
|
||||
wantError: true,
|
||||
wantError: true,
|
||||
},
|
||||
{
|
||||
name: "MCP Invoke my-auth-required-tool",
|
||||
@@ -454,7 +454,7 @@ func runFirestoreMCPToolCallMethod(t *testing.T, docPath1, docPath2 string) {
|
||||
},
|
||||
},
|
||||
wantContains: `tool with name \"my-auth-required-tool\" does not exist`,
|
||||
wantError: true,
|
||||
wantError: true,
|
||||
},
|
||||
{
|
||||
name: "MCP Invoke my-fail-tool",
|
||||
@@ -467,17 +467,17 @@ func runFirestoreMCPToolCallMethod(t *testing.T, docPath1, docPath2 string) {
|
||||
Method: "tools/call",
|
||||
},
|
||||
Params: map[string]any{
|
||||
"name": "my-fail-tool",
|
||||
"name": "my-fail-tool",
|
||||
"arguments": map[string]any{
|
||||
"documentPaths": []string{"non-existent/path"},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantContains: `\"exists\":false`,
|
||||
wantError: false,
|
||||
wantError: false,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
for _, tc := range invokeTcs {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
reqMarshal, err := json.Marshal(tc.requestBody)
|
||||
@@ -598,12 +598,12 @@ func setupFirestoreTestData(t *testing.T, ctx context.Context, client *firestore
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create test document 1: %v", err)
|
||||
}
|
||||
|
||||
|
||||
_, err = client.Collection(collectionName).Doc(docID2).Set(ctx, testData2)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create test document 2: %v", err)
|
||||
}
|
||||
|
||||
|
||||
_, err = client.Collection(collectionName).Doc(docID3).Set(ctx, testData3)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create test document 3: %v", err)
|
||||
@@ -611,7 +611,7 @@ func setupFirestoreTestData(t *testing.T, ctx context.Context, client *firestore
|
||||
|
||||
// Create a subcollection document
|
||||
subDocData := map[string]interface{}{
|
||||
"type": "subcollection_doc",
|
||||
"type": "subcollection_doc",
|
||||
"value": "test",
|
||||
}
|
||||
_, err = client.Collection(collectionName).Doc(docID1).Collection(subCollectionName).Doc("subdoc1").Set(ctx, subDocData)
|
||||
@@ -696,7 +696,7 @@ func runFirestoreGetDocumentsTest(t *testing.T, docPath1, docPath2 string) {
|
||||
t.Fatalf("unable to create request: %s", err)
|
||||
}
|
||||
req.Header.Add("Content-type", "application/json")
|
||||
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to send request: %s", err)
|
||||
@@ -773,7 +773,7 @@ func runFirestoreListCollectionsTest(t *testing.T, collectionName, subCollection
|
||||
t.Fatalf("unable to create request: %s", err)
|
||||
}
|
||||
req.Header.Add("Content-type", "application/json")
|
||||
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to send request: %s", err)
|
||||
@@ -849,7 +849,7 @@ func runFirestoreDeleteDocumentsTest(t *testing.T, docPath string) {
|
||||
t.Fatalf("unable to create request: %s", err)
|
||||
}
|
||||
req.Header.Add("Content-type", "application/json")
|
||||
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to send request: %s", err)
|
||||
@@ -891,32 +891,32 @@ func runFirestoreQueryCollectionTest(t *testing.T, collectionName string) {
|
||||
isErr bool
|
||||
}{
|
||||
{
|
||||
name: "query collection with filter",
|
||||
api: "http://127.0.0.1:5000/api/tool/firestore-query-coll/invoke",
|
||||
name: "query collection with filter",
|
||||
api: "http://127.0.0.1:5000/api/tool/firestore-query-coll/invoke",
|
||||
requestBody: bytes.NewBuffer([]byte(fmt.Sprintf(`{
|
||||
"collectionPath": "%s",
|
||||
"filters": ["{\"field\": \"age\", \"op\": \">\", \"value\": 25}"],
|
||||
"orderBy": "",
|
||||
"limit": 10
|
||||
}`, collectionName))),
|
||||
wantRegex: `"name":"Alice"`,
|
||||
isErr: false,
|
||||
wantRegex: `"name":"Alice"`,
|
||||
isErr: false,
|
||||
},
|
||||
{
|
||||
name: "query collection with orderBy",
|
||||
api: "http://127.0.0.1:5000/api/tool/firestore-query-coll/invoke",
|
||||
name: "query collection with orderBy",
|
||||
api: "http://127.0.0.1:5000/api/tool/firestore-query-coll/invoke",
|
||||
requestBody: bytes.NewBuffer([]byte(fmt.Sprintf(`{
|
||||
"collectionPath": "%s",
|
||||
"filters": [],
|
||||
"orderBy": "{\"field\": \"age\", \"direction\": \"DESCENDING\"}",
|
||||
"limit": 2
|
||||
}`, collectionName))),
|
||||
wantRegex: `"age":30.*"age":25`, // Should be ordered by age descending (Charlie=35, Alice=30, Bob=25)
|
||||
isErr: false,
|
||||
wantRegex: `"age":30.*"age":25`, // Should be ordered by age descending (Charlie=35, Alice=30, Bob=25)
|
||||
isErr: false,
|
||||
},
|
||||
{
|
||||
name: "query collection with multiple filters",
|
||||
api: "http://127.0.0.1:5000/api/tool/firestore-query-coll/invoke",
|
||||
name: "query collection with multiple filters",
|
||||
api: "http://127.0.0.1:5000/api/tool/firestore-query-coll/invoke",
|
||||
requestBody: bytes.NewBuffer([]byte(fmt.Sprintf(`{
|
||||
"collectionPath": "%s",
|
||||
"filters": [
|
||||
@@ -926,32 +926,32 @@ func runFirestoreQueryCollectionTest(t *testing.T, collectionName string) {
|
||||
"orderBy": "",
|
||||
"limit": 10
|
||||
}`, collectionName))),
|
||||
wantRegex: `"name":"Bob".*"name":"Alice"`, // Results may be ordered by document ID
|
||||
isErr: false,
|
||||
wantRegex: `"name":"Bob".*"name":"Alice"`, // Results may be ordered by document ID
|
||||
isErr: false,
|
||||
},
|
||||
{
|
||||
name: "query with limit",
|
||||
api: "http://127.0.0.1:5000/api/tool/firestore-query-coll/invoke",
|
||||
name: "query with limit",
|
||||
api: "http://127.0.0.1:5000/api/tool/firestore-query-coll/invoke",
|
||||
requestBody: bytes.NewBuffer([]byte(fmt.Sprintf(`{
|
||||
"collectionPath": "%s",
|
||||
"filters": [],
|
||||
"orderBy": "",
|
||||
"limit": 1
|
||||
}`, collectionName))),
|
||||
wantRegex: `^\[{.*}\]$`, // Should return exactly one document
|
||||
isErr: false,
|
||||
wantRegex: `^\[{.*}\]$`, // Should return exactly one document
|
||||
isErr: false,
|
||||
},
|
||||
{
|
||||
name: "query non-existent collection",
|
||||
api: "http://127.0.0.1:5000/api/tool/firestore-query-coll/invoke",
|
||||
name: "query non-existent collection",
|
||||
api: "http://127.0.0.1:5000/api/tool/firestore-query-coll/invoke",
|
||||
requestBody: bytes.NewBuffer([]byte(`{
|
||||
"collectionPath": "non-existent-collection",
|
||||
"filters": [],
|
||||
"orderBy": "",
|
||||
"limit": 10
|
||||
}`)),
|
||||
wantRegex: `^\[\]$`, // Empty array
|
||||
isErr: false,
|
||||
wantRegex: `^\[\]$`, // Empty array
|
||||
isErr: false,
|
||||
},
|
||||
{
|
||||
name: "missing collectionPath parameter",
|
||||
@@ -960,18 +960,18 @@ func runFirestoreQueryCollectionTest(t *testing.T, collectionName string) {
|
||||
isErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid filter operator",
|
||||
api: "http://127.0.0.1:5000/api/tool/firestore-query-coll/invoke",
|
||||
name: "invalid filter operator",
|
||||
api: "http://127.0.0.1:5000/api/tool/firestore-query-coll/invoke",
|
||||
requestBody: bytes.NewBuffer([]byte(fmt.Sprintf(`{
|
||||
"collectionPath": "%s",
|
||||
"filters": ["{\"field\": \"age\", \"op\": \"INVALID\", \"value\": 25}"],
|
||||
"orderBy": ""
|
||||
}`, collectionName))),
|
||||
isErr: true,
|
||||
isErr: true,
|
||||
},
|
||||
{
|
||||
name: "query with analyzeQuery",
|
||||
api: "http://127.0.0.1:5000/api/tool/firestore-query-coll/invoke",
|
||||
name: "query with analyzeQuery",
|
||||
api: "http://127.0.0.1:5000/api/tool/firestore-query-coll/invoke",
|
||||
requestBody: bytes.NewBuffer([]byte(fmt.Sprintf(`{
|
||||
"collectionPath": "%s",
|
||||
"filters": [],
|
||||
@@ -979,8 +979,8 @@ func runFirestoreQueryCollectionTest(t *testing.T, collectionName string) {
|
||||
"analyzeQuery": true,
|
||||
"limit": 1
|
||||
}`, collectionName))),
|
||||
wantRegex: `"documents":\[.*\]`,
|
||||
isErr: false,
|
||||
wantRegex: `"documents":\[.*\]`,
|
||||
isErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -991,7 +991,7 @@ func runFirestoreQueryCollectionTest(t *testing.T, collectionName string) {
|
||||
t.Fatalf("unable to create request: %s", err)
|
||||
}
|
||||
req.Header.Add("Content-type", "application/json")
|
||||
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to send request: %s", err)
|
||||
|
||||
@@ -60,8 +60,10 @@ func multiTool(w http.ResponseWriter, r *http.Request) {
|
||||
handleTool0(w, r)
|
||||
case "tool1":
|
||||
handleTool1(w, r)
|
||||
case "tool1a":
|
||||
handleTool1a(w, r)
|
||||
case "tool1id":
|
||||
handleTool1Id(w, r)
|
||||
case "tool1name":
|
||||
handleTool1Name(w, r)
|
||||
case "tool2":
|
||||
handleTool2(w, r)
|
||||
case "tool3":
|
||||
@@ -131,7 +133,7 @@ func handleTool1(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// handler function for the test server
|
||||
func handleTool1a(w http.ResponseWriter, r *http.Request) {
|
||||
func handleTool1Id(w http.ResponseWriter, r *http.Request) {
|
||||
// expect GET method
|
||||
if r.Method != http.MethodGet {
|
||||
errorMessage := fmt.Sprintf("expected GET method but got: %s", string(r.Method))
|
||||
@@ -151,6 +153,27 @@ func handleTool1a(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
}
|
||||
|
||||
// handler function for the test server
|
||||
func handleTool1Name(w http.ResponseWriter, r *http.Request) {
|
||||
// expect GET method
|
||||
if r.Method != http.MethodGet {
|
||||
errorMessage := fmt.Sprintf("expected GET method but got: %s", string(r.Method))
|
||||
http.Error(w, errorMessage, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
name := r.URL.Query().Get("name")
|
||||
if name == "" {
|
||||
response := "null"
|
||||
_, err := w.Write([]byte(response))
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to write response", http.StatusInternalServerError)
|
||||
}
|
||||
return
|
||||
}
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
}
|
||||
|
||||
// handler function for the test server
|
||||
func handleTool2(w http.ResponseWriter, r *http.Request) {
|
||||
// expect GET method
|
||||
@@ -279,9 +302,10 @@ func TestHttpToolEndpoints(t *testing.T) {
|
||||
}
|
||||
|
||||
select1Want := `"hello world"`
|
||||
invokeParamWant, invokeParamWantNull, _ := tests.GetNonSpannerInvokeParamWant()
|
||||
invokeParamWant, invokeIdNullWant, _, _ := tests.GetNonSpannerInvokeParamWant()
|
||||
nullWant := "null"
|
||||
tests.RunToolGetTest(t)
|
||||
tests.RunToolInvokeTest(t, select1Want, invokeParamWant, invokeParamWantNull, false)
|
||||
tests.RunToolInvokeTest(t, select1Want, invokeParamWant, invokeIdNullWant, nullWant, true, false)
|
||||
runAdvancedHTTPInvokeTest(t)
|
||||
}
|
||||
|
||||
@@ -385,7 +409,7 @@ func getHTTPToolsConfig(sourceConfig map[string]any, toolKind string) map[string
|
||||
"requestBody": "{}",
|
||||
"description": "Simple tool to test end to end functionality.",
|
||||
},
|
||||
"my-param-tool": map[string]any{
|
||||
"my-tool": map[string]any{
|
||||
"kind": toolKind,
|
||||
"source": "my-instance",
|
||||
"method": "GET",
|
||||
@@ -401,16 +425,26 @@ func getHTTPToolsConfig(sourceConfig map[string]any, toolKind string) map[string
|
||||
"bodyParams": []tools.Parameter{tools.NewStringParameter("name", "user name")},
|
||||
"headers": map[string]string{"Content-Type": "application/json"},
|
||||
},
|
||||
"my-param-tool2": map[string]any{
|
||||
"my-tool-by-id": map[string]any{
|
||||
"kind": toolKind,
|
||||
"source": "my-instance",
|
||||
"method": "GET",
|
||||
"path": "/tool1a",
|
||||
"path": "/tool1id",
|
||||
"description": "some description",
|
||||
"queryParams": []tools.Parameter{
|
||||
tools.NewIntParameter("id", "user ID")},
|
||||
"headers": map[string]string{"Content-Type": "application/json"},
|
||||
},
|
||||
"my-tool-by-name": map[string]any{
|
||||
"kind": toolKind,
|
||||
"source": "my-instance",
|
||||
"method": "GET",
|
||||
"path": "/tool1name",
|
||||
"description": "some description",
|
||||
"queryParams": []tools.Parameter{
|
||||
tools.NewStringParameterWithRequired("name", "user name", false)},
|
||||
"headers": map[string]string{"Content-Type": "application/json"},
|
||||
},
|
||||
"my-auth-tool": map[string]any{
|
||||
"kind": toolKind,
|
||||
"source": "my-instance",
|
||||
|
||||
@@ -102,7 +102,7 @@ func TestMSSQLToolEndpoints(t *testing.T) {
|
||||
tableNameTemplateParam := "template_param_table_" + strings.ReplaceAll(uuid.New().String(), "-", "")
|
||||
|
||||
// set up data for param tool
|
||||
createParamTableStmt, insertParamTableStmt, paramToolStmt, paramToolStmt2, arrayToolStmt, paramTestParams := tests.GetMSSQLParamToolInfo(tableNameParam)
|
||||
createParamTableStmt, insertParamTableStmt, paramToolStmt, idParamToolStmt, nameParamToolStmt, arrayToolStmt, paramTestParams := tests.GetMSSQLParamToolInfo(tableNameParam)
|
||||
teardownTable1 := tests.SetupMsSQLTable(t, ctx, pool, createParamTableStmt, insertParamTableStmt, tableNameParam, paramTestParams)
|
||||
defer teardownTable1(t)
|
||||
|
||||
@@ -112,7 +112,7 @@ func TestMSSQLToolEndpoints(t *testing.T) {
|
||||
defer teardownTable2(t)
|
||||
|
||||
// Write config into a file and pass it to command
|
||||
toolsFile := tests.GetToolsConfig(sourceConfig, MSSQLToolKind, paramToolStmt, paramToolStmt2, arrayToolStmt, authToolStmt)
|
||||
toolsFile := tests.GetToolsConfig(sourceConfig, MSSQLToolKind, paramToolStmt, idParamToolStmt, nameParamToolStmt, arrayToolStmt, authToolStmt)
|
||||
toolsFile = tests.AddMSSQLExecuteSqlConfig(t, toolsFile)
|
||||
tmplSelectCombined, tmplSelectFilterCombined := tests.GetMSSQLTmplToolStatement()
|
||||
toolsFile = tests.AddTemplateParamConfig(t, toolsFile, MSSQLToolKind, tmplSelectCombined, tmplSelectFilterCombined, "")
|
||||
@@ -134,8 +134,8 @@ func TestMSSQLToolEndpoints(t *testing.T) {
|
||||
tests.RunToolGetTest(t)
|
||||
|
||||
select1Want, failInvocationWant, createTableStatement := tests.GetMSSQLWants()
|
||||
invokeParamWant, invokeParamWantNull, mcpInvokeParamWant := tests.GetNonSpannerInvokeParamWant()
|
||||
tests.RunToolInvokeTest(t, select1Want, invokeParamWant, invokeParamWantNull, false)
|
||||
invokeParamWant, invokeIdNullWant, nullWant, mcpInvokeParamWant := tests.GetNonSpannerInvokeParamWant()
|
||||
tests.RunToolInvokeTest(t, select1Want, invokeParamWant, invokeIdNullWant, nullWant, true, false)
|
||||
tests.RunExecuteSqlToolInvokeTest(t, createTableStatement, select1Want)
|
||||
tests.RunMCPToolCallMethod(t, mcpInvokeParamWant, failInvocationWant)
|
||||
tests.RunToolInvokeWithTemplateParameters(t, tableNameTemplateParam, tests.NewTemplateParameterTestConfig())
|
||||
|
||||
@@ -93,7 +93,7 @@ func TestMySQLToolEndpoints(t *testing.T) {
|
||||
tableNameTemplateParam := "template_param_table_" + strings.ReplaceAll(uuid.New().String(), "-", "")
|
||||
|
||||
// set up data for param tool
|
||||
createParamTableStmt, insertParamTableStmt, paramToolStmt, paramToolStmt2, arrayToolStmt, paramTestParams := tests.GetMySQLParamToolInfo(tableNameParam)
|
||||
createParamTableStmt, insertParamTableStmt, paramToolStmt, idParamToolStmt, nameParamToolStmt, arrayToolStmt, paramTestParams := tests.GetMySQLParamToolInfo(tableNameParam)
|
||||
teardownTable1 := tests.SetupMySQLTable(t, ctx, pool, createParamTableStmt, insertParamTableStmt, tableNameParam, paramTestParams)
|
||||
defer teardownTable1(t)
|
||||
|
||||
@@ -103,7 +103,7 @@ func TestMySQLToolEndpoints(t *testing.T) {
|
||||
defer teardownTable2(t)
|
||||
|
||||
// Write config into a file and pass it to command
|
||||
toolsFile := tests.GetToolsConfig(sourceConfig, MySQLToolKind, paramToolStmt, paramToolStmt2, arrayToolStmt, authToolStmt)
|
||||
toolsFile := tests.GetToolsConfig(sourceConfig, MySQLToolKind, paramToolStmt, idParamToolStmt, nameParamToolStmt, arrayToolStmt, authToolStmt)
|
||||
toolsFile = tests.AddMySqlExecuteSqlConfig(t, toolsFile)
|
||||
tmplSelectCombined, tmplSelectFilterCombined := tests.GetMySQLTmplToolStatement()
|
||||
toolsFile = tests.AddTemplateParamConfig(t, toolsFile, MySQLToolKind, tmplSelectCombined, tmplSelectFilterCombined, "")
|
||||
@@ -125,8 +125,8 @@ func TestMySQLToolEndpoints(t *testing.T) {
|
||||
tests.RunToolGetTest(t)
|
||||
|
||||
select1Want, failInvocationWant, createTableStatement := tests.GetMySQLWants()
|
||||
invokeParamWant, invokeParamWantNull, mcpInvokeParamWant := tests.GetNonSpannerInvokeParamWant()
|
||||
tests.RunToolInvokeTest(t, select1Want, invokeParamWant, invokeParamWantNull, false)
|
||||
invokeParamWant, invokeIdNullWant, nullWant, mcpInvokeParamWant := tests.GetNonSpannerInvokeParamWant()
|
||||
tests.RunToolInvokeTest(t, select1Want, invokeParamWant, invokeIdNullWant, nullWant, true, false)
|
||||
tests.RunExecuteSqlToolInvokeTest(t, createTableStatement, select1Want)
|
||||
tests.RunMCPToolCallMethod(t, mcpInvokeParamWant, failInvocationWant)
|
||||
tests.RunToolInvokeWithTemplateParameters(t, tableNameTemplateParam, tests.NewTemplateParameterTestConfig())
|
||||
|
||||
@@ -101,7 +101,7 @@ func TestNeo4jToolEndpoints(t *testing.T) {
|
||||
want map[string]any
|
||||
}{
|
||||
{
|
||||
name: "get my-simple-tool",
|
||||
name: "get my-simple-cypher-tool",
|
||||
api: "http://127.0.0.1:5000/api/tool/my-simple-cypher-tool/",
|
||||
want: map[string]any{
|
||||
"my-simple-cypher-tool": map[string]any{
|
||||
|
||||
@@ -99,7 +99,7 @@ func TestPostgres(t *testing.T) {
|
||||
tableNameTemplateParam := "template_param_table_" + strings.ReplaceAll(uuid.New().String(), "-", "")
|
||||
|
||||
// set up data for param tool
|
||||
createParamTableStmt, insertParamTableStmt, paramToolStmt, paramToolStmt2, arrayToolStmt, paramTestParams := tests.GetPostgresSQLParamToolInfo(tableNameParam)
|
||||
createParamTableStmt, insertParamTableStmt, paramToolStmt, idParamToolStmt, nameParamToolStmt, arrayToolStmt, paramTestParams := tests.GetPostgresSQLParamToolInfo(tableNameParam)
|
||||
teardownTable1 := tests.SetupPostgresSQLTable(t, ctx, pool, createParamTableStmt, insertParamTableStmt, tableNameParam, paramTestParams)
|
||||
defer teardownTable1(t)
|
||||
|
||||
@@ -109,7 +109,7 @@ func TestPostgres(t *testing.T) {
|
||||
defer teardownTable2(t)
|
||||
|
||||
// Write config into a file and pass it to command
|
||||
toolsFile := tests.GetToolsConfig(sourceConfig, PostgresToolKind, paramToolStmt, paramToolStmt2, arrayToolStmt, authToolStmt)
|
||||
toolsFile := tests.GetToolsConfig(sourceConfig, PostgresToolKind, paramToolStmt, idParamToolStmt, nameParamToolStmt, arrayToolStmt, authToolStmt)
|
||||
toolsFile = tests.AddPgExecuteSqlConfig(t, toolsFile)
|
||||
tmplSelectCombined, tmplSelectFilterCombined := tests.GetPostgresSQLTmplToolStatement()
|
||||
toolsFile = tests.AddTemplateParamConfig(t, toolsFile, PostgresToolKind, tmplSelectCombined, tmplSelectFilterCombined, "")
|
||||
@@ -131,8 +131,8 @@ func TestPostgres(t *testing.T) {
|
||||
tests.RunToolGetTest(t)
|
||||
|
||||
select1Want, failInvocationWant, createTableStatement := tests.GetPostgresWants()
|
||||
invokeParamWant, invokeParamWantNull, mcpInvokeParamWant := tests.GetNonSpannerInvokeParamWant()
|
||||
tests.RunToolInvokeTest(t, select1Want, invokeParamWant, invokeParamWantNull, true)
|
||||
invokeParamWant, invokeIdNullWant, nullWant, mcpInvokeParamWant := tests.GetNonSpannerInvokeParamWant()
|
||||
tests.RunToolInvokeTest(t, select1Want, invokeParamWant, invokeIdNullWant, nullWant, true, true)
|
||||
tests.RunExecuteSqlToolInvokeTest(t, createTableStatement, select1Want)
|
||||
tests.RunMCPToolCallMethod(t, mcpInvokeParamWant, failInvocationWant)
|
||||
tests.RunToolInvokeWithTemplateParameters(t, tableNameTemplateParam, tests.NewTemplateParameterTestConfig())
|
||||
|
||||
@@ -99,18 +99,19 @@ func TestRedisToolEndpoints(t *testing.T) {
|
||||
|
||||
tests.RunToolGetTest(t)
|
||||
|
||||
select1Want, failInvocationWant, invokeParamWant, invokeParamWantNull, mcpInvokeParamWant := tests.GetRedisValkeyWants()
|
||||
tests.RunToolInvokeTest(t, select1Want, invokeParamWant, invokeParamWantNull, true)
|
||||
select1Want, failInvocationWant, invokeParamWant, invokeIdNullWant, nullWant, mcpInvokeParamWant := tests.GetRedisValkeyWants()
|
||||
tests.RunToolInvokeTest(t, select1Want, invokeParamWant, invokeIdNullWant, nullWant, true, true)
|
||||
tests.RunMCPToolCallMethod(t, mcpInvokeParamWant, failInvocationWant)
|
||||
}
|
||||
|
||||
func setupRedisDB(t *testing.T, ctx context.Context, client *redis.Client) func(*testing.T) {
|
||||
keys := []string{"row1", "row2", "row3", "row4"}
|
||||
keys := []string{"row1", "row2", "row3", "row4", "null"}
|
||||
commands := [][]any{
|
||||
{"HSET", keys[0], "id", 1, "name", "Alice"},
|
||||
{"HSET", keys[1], "id", 2, "name", "Jane"},
|
||||
{"HSET", keys[2], "id", 3, "name", "Sid"},
|
||||
{"HSET", keys[3], "id", 4, "name", nil},
|
||||
{"SET", keys[4], "null"},
|
||||
{"HSET", tests.ServiceAccountEmail, "name", "Alice"},
|
||||
}
|
||||
for _, c := range commands {
|
||||
|
||||
@@ -108,7 +108,7 @@ func TestSpannerToolEndpoints(t *testing.T) {
|
||||
tableNameTemplateParam := "template_param_table_" + strings.ReplaceAll(uuid.New().String(), "-", "")
|
||||
|
||||
// set up data for param tool
|
||||
createParamTableStmt, insertParamTableStmt, paramToolStmt, paramToolStmt2, arrayToolStmt, paramTestParams := getSpannerParamToolInfo(tableNameParam)
|
||||
createParamTableStmt, insertParamTableStmt, paramToolStmt, idParamToolStmt, nameParamToolStmt, arrayToolStmt, paramTestParams := getSpannerParamToolInfo(tableNameParam)
|
||||
dbString := fmt.Sprintf(
|
||||
"projects/%s/instances/%s/databases/%s",
|
||||
SpannerProject,
|
||||
@@ -129,7 +129,7 @@ func TestSpannerToolEndpoints(t *testing.T) {
|
||||
defer teardownTableTmpl(t)
|
||||
|
||||
// Write config into a file and pass it to command
|
||||
toolsFile := tests.GetToolsConfig(sourceConfig, SpannerToolKind, paramToolStmt, paramToolStmt2, arrayToolStmt, authToolStmt)
|
||||
toolsFile := tests.GetToolsConfig(sourceConfig, SpannerToolKind, paramToolStmt, idParamToolStmt, nameParamToolStmt, arrayToolStmt, authToolStmt)
|
||||
toolsFile = addSpannerExecuteSqlConfig(t, toolsFile)
|
||||
toolsFile = addSpannerReadOnlyConfig(t, toolsFile)
|
||||
toolsFile = addTemplateParamConfig(t, toolsFile)
|
||||
@@ -153,11 +153,12 @@ func TestSpannerToolEndpoints(t *testing.T) {
|
||||
select1Want := "[{\"\":\"1\"}]"
|
||||
accessSchemaWant := "[{\"schema_name\":\"INFORMATION_SCHEMA\"}]"
|
||||
invokeParamWant := "[{\"id\":\"1\",\"name\":\"Alice\"},{\"id\":\"3\",\"name\":\"Sid\"}]"
|
||||
invokeParamWantNull := `[{"id":"4","name":null}]`
|
||||
mcpInvokeParamWant := `{"jsonrpc":"2.0","id":"my-param-tool","result":{"content":[{"type":"text","text":"{\"id\":\"1\",\"name\":\"Alice\"}"},{"type":"text","text":"{\"id\":\"3\",\"name\":\"Sid\"}"}]}}`
|
||||
invokeIdNullWant := `[{"id":"4","name":null}]`
|
||||
mcpInvokeParamWant := `{"jsonrpc":"2.0","id":"my-tool","result":{"content":[{"type":"text","text":"{\"id\":\"1\",\"name\":\"Alice\"}"},{"type":"text","text":"{\"id\":\"3\",\"name\":\"Sid\"}"}]}}`
|
||||
nullWant := "null"
|
||||
failInvocationWant := `"jsonrpc":"2.0","id":"invoke-fail-tool","result":{"content":[{"type":"text","text":"unable to execute client: unable to parse row: spanner: code = \"InvalidArgument\", desc = \"Syntax error: Unexpected identifier \\\\\\\"SELEC\\\\\\\" [at 1:1]\\\\nSELEC 1;\\\\n^\"`
|
||||
|
||||
tests.RunToolInvokeTest(t, select1Want, invokeParamWant, invokeParamWantNull, true)
|
||||
tests.RunToolInvokeTest(t, select1Want, invokeParamWant, invokeIdNullWant, nullWant, true, true)
|
||||
tests.RunMCPToolCallMethod(t, mcpInvokeParamWant, failInvocationWant)
|
||||
runSpannerSchemaToolInvokeTest(t, accessSchemaWant)
|
||||
runSpannerExecuteSqlToolInvokeTest(t, select1Want, invokeParamWant, tableNameParam, tableNameAuth)
|
||||
@@ -170,15 +171,16 @@ func TestSpannerToolEndpoints(t *testing.T) {
|
||||
tests.RunToolInvokeWithTemplateParameters(t, tableNameTemplateParam, templateParamTestConfig)
|
||||
}
|
||||
|
||||
// getSpannerToolInfo returns statements and param for my-param-tool for spanner-sql kind
|
||||
func getSpannerParamToolInfo(tableName string) (string, string, string, string, string, map[string]any) {
|
||||
// getSpannerToolInfo returns statements and param for my-tool for spanner-sql kind
|
||||
func getSpannerParamToolInfo(tableName string) (string, string, string, string, string, string, map[string]any) {
|
||||
createStatement := fmt.Sprintf("CREATE TABLE %s (id INT64, name STRING(MAX)) PRIMARY KEY (id)", tableName)
|
||||
insertStatement := fmt.Sprintf("INSERT INTO %s (id, name) VALUES (1, @name1), (2, @name2), (3, @name3), (4, @name4)", tableName)
|
||||
toolStatement := fmt.Sprintf("SELECT * FROM %s WHERE id = @id OR name = @name", tableName)
|
||||
toolStatement2 := fmt.Sprintf("SELECT * FROM %s WHERE id = @id", tableName)
|
||||
idToolStatement := fmt.Sprintf("SELECT * FROM %s WHERE id = @id", tableName)
|
||||
nameToolStatement := fmt.Sprintf("SELECT * FROM %s WHERE name = @name", tableName)
|
||||
arrayToolStatement := fmt.Sprintf("SELECT * FROM %s WHERE id IN UNNEST(@idArray) AND name IN UNNEST(@nameArray)", tableName)
|
||||
params := map[string]any{"name1": "Alice", "name2": "Jane", "name3": "Sid", "name4": nil}
|
||||
return createStatement, insertStatement, toolStatement, toolStatement2, arrayToolStatement, params
|
||||
return createStatement, insertStatement, toolStatement, idToolStatement, nameToolStatement, arrayToolStatement, params
|
||||
}
|
||||
|
||||
// getSpannerAuthToolInfo returns statements and param of my-auth-tool for spanner-sql kind
|
||||
|
||||
@@ -81,14 +81,15 @@ func setupSQLiteTestDB(t *testing.T, ctx context.Context, db *sql.DB, createStat
|
||||
}
|
||||
}
|
||||
|
||||
func getSQLiteParamToolInfo(tableName string) (string, string, string, string, string, []any) {
|
||||
func getSQLiteParamToolInfo(tableName string) (string, string, string, string, string, string, []any) {
|
||||
createStatement := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s (id INTEGER PRIMARY KEY, name TEXT);", tableName)
|
||||
insertStatement := fmt.Sprintf("INSERT INTO %s (name) VALUES (?), (?), (?), (?);", tableName)
|
||||
toolStatement := fmt.Sprintf("SELECT * FROM %s WHERE id = ? OR name = ?;", tableName)
|
||||
toolStatement2 := fmt.Sprintf("SELECT * FROM %s WHERE id = ?;", tableName)
|
||||
idToolStatement := fmt.Sprintf("SELECT * FROM %s WHERE id = ?;", tableName)
|
||||
nameToolStatement := fmt.Sprintf("SELECT * FROM %s WHERE name = ?;", tableName)
|
||||
arrayToolStatement := fmt.Sprintf("SELECT * FROM %s WHERE id = ANY({{.idArray}}) AND name = ANY({{.nameArray}});", tableName)
|
||||
params := []any{"Alice", "Jane", "Sid", nil}
|
||||
return createStatement, insertStatement, toolStatement, toolStatement2, arrayToolStatement, params
|
||||
return createStatement, insertStatement, toolStatement, idToolStatement, nameToolStatement, arrayToolStatement, params
|
||||
}
|
||||
|
||||
func getSQLiteAuthToolInfo(tableName string) (string, string, string, []any) {
|
||||
@@ -126,7 +127,7 @@ func TestSQLiteToolEndpoint(t *testing.T) {
|
||||
tableNameTemplateParam := "template_param_table_" + strings.ReplaceAll(uuid.New().String(), "-", "")
|
||||
|
||||
// set up data for param tool
|
||||
createParamTableStmt, insertParamTableStmt, paramToolStmt, paramToolStmt2, arrayToolStmt, paramTestParams := getSQLiteParamToolInfo(tableNameParam)
|
||||
createParamTableStmt, insertParamTableStmt, paramToolStmt, idParamToolStmt, nameParamToolStmt, arrayToolStmt, paramTestParams := getSQLiteParamToolInfo(tableNameParam)
|
||||
setupSQLiteTestDB(t, ctx, db, createParamTableStmt, insertParamTableStmt, tableNameParam, paramTestParams)
|
||||
|
||||
// set up data for auth tool
|
||||
@@ -134,7 +135,7 @@ func TestSQLiteToolEndpoint(t *testing.T) {
|
||||
setupSQLiteTestDB(t, ctx, db, createAuthTableStmt, insertAuthTableStmt, tableNameAuth, authTestParams)
|
||||
|
||||
// Write config into a file and pass it to command
|
||||
toolsFile := tests.GetToolsConfig(sourceConfig, SQLiteToolKind, paramToolStmt, paramToolStmt2, arrayToolStmt, authToolStmt)
|
||||
toolsFile := tests.GetToolsConfig(sourceConfig, SQLiteToolKind, paramToolStmt, idParamToolStmt, nameParamToolStmt, arrayToolStmt, authToolStmt)
|
||||
tmplSelectCombined, tmplSelectFilterCombined := getSQLiteTmplToolStatement()
|
||||
toolsFile = tests.AddTemplateParamConfig(t, toolsFile, SQLiteToolKind, tmplSelectCombined, tmplSelectFilterCombined, "")
|
||||
|
||||
@@ -156,8 +157,8 @@ func TestSQLiteToolEndpoint(t *testing.T) {
|
||||
|
||||
select1Want := "[{\"1\":1}]"
|
||||
failInvocationWant := `{"jsonrpc":"2.0","id":"invoke-fail-tool","result":{"content":[{"type":"text","text":"unable to execute query: SQL logic error: near \"SELEC\": syntax error (1)"}],"isError":true}}`
|
||||
invokeParamWant, invokeParamWantNull, mcpInvokeParamWant := tests.GetNonSpannerInvokeParamWant()
|
||||
tests.RunToolInvokeTest(t, select1Want, invokeParamWant, invokeParamWantNull, false)
|
||||
invokeParamWant, invokeIdNullWant, nullWant, mcpInvokeParamWant := tests.GetNonSpannerInvokeParamWant()
|
||||
tests.RunToolInvokeTest(t, select1Want, invokeParamWant, invokeIdNullWant, nullWant, true, false)
|
||||
tests.RunMCPToolCallMethod(t, mcpInvokeParamWant, failInvocationWant)
|
||||
tests.RunToolInvokeWithTemplateParameters(t, tableNameTemplateParam, tests.NewTemplateParameterTestConfig())
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ func RunToolGetTest(t *testing.T) {
|
||||
}
|
||||
|
||||
// RunToolInvoke runs the tool invoke endpoint
|
||||
func RunToolInvokeTest(t *testing.T, select1Want, invokeParamWant, invokeParamWantNull string, supportsArray bool) {
|
||||
func RunToolInvokeTest(t *testing.T, select1Want, invokeParamWant, invokeIdNullWant, nullString string, supportNullParam, supportsArray bool) {
|
||||
// Get ID token
|
||||
idToken, err := GetGoogleIdToken(ClientId)
|
||||
if err != nil {
|
||||
@@ -101,31 +101,39 @@ func RunToolInvokeTest(t *testing.T, select1Want, invokeParamWant, invokeParamWa
|
||||
isErr: false,
|
||||
},
|
||||
{
|
||||
name: "invoke my-param-tool",
|
||||
api: "http://127.0.0.1:5000/api/tool/my-param-tool/invoke",
|
||||
name: "invoke my-tool",
|
||||
api: "http://127.0.0.1:5000/api/tool/my-tool/invoke",
|
||||
requestHeader: map[string]string{},
|
||||
requestBody: bytes.NewBuffer([]byte(`{"id": 3, "name": "Alice"}`)),
|
||||
want: invokeParamWant,
|
||||
isErr: false,
|
||||
},
|
||||
{
|
||||
name: "invoke my-param-tool2 with nil response",
|
||||
api: "http://127.0.0.1:5000/api/tool/my-param-tool2/invoke",
|
||||
name: "invoke my-tool-by-id with nil response",
|
||||
api: "http://127.0.0.1:5000/api/tool/my-tool-by-id/invoke",
|
||||
requestHeader: map[string]string{},
|
||||
requestBody: bytes.NewBuffer([]byte(`{"id": 4}`)),
|
||||
want: invokeParamWantNull,
|
||||
want: invokeIdNullWant,
|
||||
isErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invoke my-param-tool without parameters",
|
||||
api: "http://127.0.0.1:5000/api/tool/my-param-tool/invoke",
|
||||
name: "invoke my-tool-by-name with nil response",
|
||||
api: "http://127.0.0.1:5000/api/tool/my-tool-by-name/invoke",
|
||||
requestHeader: map[string]string{},
|
||||
requestBody: bytes.NewBuffer([]byte(`{}`)),
|
||||
want: nullString,
|
||||
isErr: !supportNullParam,
|
||||
},
|
||||
{
|
||||
name: "Invoke my-tool without parameters",
|
||||
api: "http://127.0.0.1:5000/api/tool/my-tool/invoke",
|
||||
requestHeader: map[string]string{},
|
||||
requestBody: bytes.NewBuffer([]byte(`{}`)),
|
||||
isErr: true,
|
||||
},
|
||||
{
|
||||
name: "Invoke my-param-tool with insufficient parameters",
|
||||
api: "http://127.0.0.1:5000/api/tool/my-param-tool/invoke",
|
||||
name: "Invoke my-tool with insufficient parameters",
|
||||
api: "http://127.0.0.1:5000/api/tool/my-tool/invoke",
|
||||
requestHeader: map[string]string{},
|
||||
requestBody: bytes.NewBuffer([]byte(`{"id": 1}`)),
|
||||
isErr: true,
|
||||
@@ -629,17 +637,17 @@ func RunMCPToolCallMethod(t *testing.T, invokeParamWant, failInvocationWant stri
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "MCP Invoke my-param-tool",
|
||||
name: "MCP Invoke my-tool",
|
||||
api: "http://127.0.0.1:5000/mcp",
|
||||
requestHeader: map[string]string{},
|
||||
requestBody: jsonrpc.JSONRPCRequest{
|
||||
Jsonrpc: "2.0",
|
||||
Id: "my-param-tool",
|
||||
Id: "my-tool",
|
||||
Request: jsonrpc.Request{
|
||||
Method: "tools/call",
|
||||
},
|
||||
Params: map[string]any{
|
||||
"name": "my-param-tool",
|
||||
"name": "my-tool",
|
||||
"arguments": map[string]any{
|
||||
"id": int(3),
|
||||
"name": "Alice",
|
||||
@@ -666,7 +674,7 @@ func RunMCPToolCallMethod(t *testing.T, invokeParamWant, failInvocationWant stri
|
||||
want: `{"jsonrpc":"2.0","id":"invalid-tool","error":{"code":-32602,"message":"invalid tool name: tool with name \"foo\" does not exist"}}`,
|
||||
},
|
||||
{
|
||||
name: "MCP Invoke my-param-tool without parameters",
|
||||
name: "MCP Invoke my-tool without parameters",
|
||||
api: "http://127.0.0.1:5000/mcp",
|
||||
requestHeader: map[string]string{},
|
||||
requestBody: jsonrpc.JSONRPCRequest{
|
||||
@@ -676,14 +684,14 @@ func RunMCPToolCallMethod(t *testing.T, invokeParamWant, failInvocationWant stri
|
||||
Method: "tools/call",
|
||||
},
|
||||
Params: map[string]any{
|
||||
"name": "my-param-tool",
|
||||
"name": "my-tool",
|
||||
"arguments": map[string]any{},
|
||||
},
|
||||
},
|
||||
want: `{"jsonrpc":"2.0","id":"invoke-without-parameter","error":{"code":-32602,"message":"provided parameters were invalid: parameter \"id\" is required"}}`,
|
||||
},
|
||||
{
|
||||
name: "MCP Invoke my-param-tool with insufficient parameters",
|
||||
name: "MCP Invoke my-tool with insufficient parameters",
|
||||
api: "http://127.0.0.1:5000/mcp",
|
||||
requestHeader: map[string]string{},
|
||||
requestBody: jsonrpc.JSONRPCRequest{
|
||||
@@ -693,7 +701,7 @@ func RunMCPToolCallMethod(t *testing.T, invokeParamWant, failInvocationWant stri
|
||||
Method: "tools/call",
|
||||
},
|
||||
Params: map[string]any{
|
||||
"name": "my-param-tool",
|
||||
"name": "my-tool",
|
||||
"arguments": map[string]any{"id": 1},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -102,18 +102,19 @@ func TestValkeyToolEndpoints(t *testing.T) {
|
||||
|
||||
tests.RunToolGetTest(t)
|
||||
|
||||
select1Want, failInvocationWant, invokeParamWant, invokeParamWantNull, mcpInvokeParamWant := tests.GetRedisValkeyWants()
|
||||
tests.RunToolInvokeTest(t, select1Want, invokeParamWant, invokeParamWantNull, true)
|
||||
select1Want, failInvocationWant, invokeParamWant, invokeIdNullWant, nullWant, mcpInvokeParamWant := tests.GetRedisValkeyWants()
|
||||
tests.RunToolInvokeTest(t, select1Want, invokeParamWant, invokeIdNullWant, nullWant, true, true)
|
||||
tests.RunMCPToolCallMethod(t, mcpInvokeParamWant, failInvocationWant)
|
||||
}
|
||||
|
||||
func setupValkeyDB(t *testing.T, ctx context.Context, client valkey.Client) func(*testing.T) {
|
||||
keys := []string{"row1", "row2", "row3", "row4"}
|
||||
keys := []string{"row1", "row2", "row3", "row4", "null"}
|
||||
commands := [][]string{
|
||||
{"HSET", keys[0], "name", "Alice", "id", "1"},
|
||||
{"HSET", keys[1], "name", "Jane", "id", "2"},
|
||||
{"HSET", keys[2], "name", "Sid", "id", "3"},
|
||||
{"HSET", keys[3], "name", "", "id", "4"},
|
||||
{"SET", keys[4], "null"},
|
||||
{"HSET", tests.ServiceAccountEmail, "name", "Alice"},
|
||||
}
|
||||
builtCmds := make(valkey.Commands, len(commands))
|
||||
|
||||
Reference in New Issue
Block a user