e04ed9c211
CF: Deploy Dev Docs / deploy (push) Has been cancelled
Sync Labels / build (push) Has been cancelled
tests / unit tests (macos-latest) (push) Has been cancelled
tests / unit tests (windows-latest) (push) Has been cancelled
tests / unit tests (ubuntu-latest) (push) Has been cancelled
2373 lines
69 KiB
Go
2373 lines
69 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 parameters_test
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"math"
|
|
"reflect"
|
|
"slices"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/goccy/go-yaml"
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/googleapis/mcp-toolbox/internal/testutils"
|
|
"github.com/googleapis/mcp-toolbox/internal/util/parameters"
|
|
)
|
|
|
|
func TestParametersMarshal(t *testing.T) {
|
|
ctx, err := testutils.ContextWithNewLogger()
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %s", err)
|
|
}
|
|
tcs := []struct {
|
|
name string
|
|
in []map[string]any
|
|
want parameters.Parameters
|
|
}{
|
|
{
|
|
name: "string",
|
|
in: []map[string]any{
|
|
{
|
|
"name": "my_string",
|
|
"type": "string",
|
|
"description": "this param is a string",
|
|
},
|
|
},
|
|
want: parameters.Parameters{
|
|
parameters.NewStringParameter("my_string", "this param is a string"),
|
|
},
|
|
},
|
|
{
|
|
name: "string not required",
|
|
in: []map[string]any{
|
|
{
|
|
"name": "my_string",
|
|
"type": "string",
|
|
"description": "this param is a string",
|
|
"required": false,
|
|
},
|
|
},
|
|
want: parameters.Parameters{
|
|
parameters.NewStringParameter("my_string", "this param is a string", parameters.WithStringRequired(false)),
|
|
},
|
|
},
|
|
{
|
|
name: "int",
|
|
in: []map[string]any{
|
|
{
|
|
"name": "my_integer",
|
|
"type": "integer",
|
|
"description": "this param is an int",
|
|
},
|
|
},
|
|
want: parameters.Parameters{
|
|
parameters.NewIntParameter("my_integer", "this param is an int"),
|
|
},
|
|
},
|
|
{
|
|
name: "int not required",
|
|
in: []map[string]any{
|
|
{
|
|
"name": "my_integer",
|
|
"type": "integer",
|
|
"description": "this param is an int",
|
|
"required": false,
|
|
},
|
|
},
|
|
want: parameters.Parameters{
|
|
parameters.NewIntParameter("my_integer", "this param is an int", parameters.WithIntRequired(false)),
|
|
},
|
|
},
|
|
{
|
|
name: "float",
|
|
in: []map[string]any{
|
|
{
|
|
"name": "my_float",
|
|
"type": "float",
|
|
"description": "my param is a float",
|
|
},
|
|
},
|
|
want: parameters.Parameters{
|
|
parameters.NewFloatParameter("my_float", "my param is a float"),
|
|
},
|
|
},
|
|
{
|
|
name: "float not required",
|
|
in: []map[string]any{
|
|
{
|
|
"name": "my_float",
|
|
"type": "float",
|
|
"description": "my param is a float",
|
|
"required": false,
|
|
},
|
|
},
|
|
want: parameters.Parameters{
|
|
parameters.NewFloatParameter("my_float", "my param is a float", parameters.WithFloatRequired(false)),
|
|
},
|
|
},
|
|
{
|
|
name: "bool",
|
|
in: []map[string]any{
|
|
{
|
|
"name": "my_bool",
|
|
"type": "boolean",
|
|
"description": "this param is a boolean",
|
|
},
|
|
},
|
|
want: parameters.Parameters{
|
|
parameters.NewBooleanParameter("my_bool", "this param is a boolean"),
|
|
},
|
|
},
|
|
{
|
|
name: "bool not required",
|
|
in: []map[string]any{
|
|
{
|
|
"name": "my_bool",
|
|
"type": "boolean",
|
|
"description": "this param is a boolean",
|
|
"required": false,
|
|
},
|
|
},
|
|
want: parameters.Parameters{
|
|
parameters.NewBooleanParameter("my_bool", "this param is a boolean", parameters.WithBooleanRequired(false)),
|
|
},
|
|
},
|
|
{
|
|
name: "string array",
|
|
in: []map[string]any{
|
|
{
|
|
"name": "my_array",
|
|
"type": "array",
|
|
"description": "this param is an array of strings",
|
|
"items": map[string]string{
|
|
"name": "my_string",
|
|
"type": "string",
|
|
"description": "string item",
|
|
},
|
|
},
|
|
},
|
|
want: parameters.Parameters{
|
|
parameters.NewArrayParameter("my_array", "this param is an array of strings", parameters.NewStringParameter("my_string", "string item")),
|
|
},
|
|
},
|
|
{
|
|
name: "string array not required",
|
|
in: []map[string]any{
|
|
{
|
|
"name": "my_array",
|
|
"type": "array",
|
|
"description": "this param is an array of strings",
|
|
"required": false,
|
|
"items": map[string]string{
|
|
"name": "my_string",
|
|
"type": "string",
|
|
"description": "string item",
|
|
},
|
|
},
|
|
},
|
|
want: parameters.Parameters{
|
|
parameters.NewArrayParameter("my_array", "this param is an array of strings", parameters.NewStringParameter("my_string", "string item"), parameters.WithArrayRequired(false)),
|
|
},
|
|
},
|
|
{
|
|
name: "float array",
|
|
in: []map[string]any{
|
|
{
|
|
"name": "my_array",
|
|
"type": "array",
|
|
"description": "this param is an array of floats",
|
|
"items": map[string]string{
|
|
"name": "my_float",
|
|
"type": "float",
|
|
"description": "float item",
|
|
},
|
|
},
|
|
},
|
|
want: parameters.Parameters{
|
|
parameters.NewArrayParameter("my_array", "this param is an array of floats", parameters.NewFloatParameter("my_float", "float item")),
|
|
},
|
|
},
|
|
{
|
|
name: "string default",
|
|
in: []map[string]any{
|
|
{
|
|
"name": "my_string",
|
|
"type": "string",
|
|
"default": "foo",
|
|
"description": "this param is a string",
|
|
},
|
|
},
|
|
want: parameters.Parameters{
|
|
parameters.NewStringParameter("my_string", "this param is a string", parameters.WithStringDefault("foo")),
|
|
},
|
|
},
|
|
{
|
|
name: "int default",
|
|
in: []map[string]any{
|
|
{
|
|
"name": "my_integer",
|
|
"type": "integer",
|
|
"default": 5,
|
|
"description": "this param is an int",
|
|
},
|
|
},
|
|
want: parameters.Parameters{
|
|
parameters.NewIntParameter("my_integer", "this param is an int", parameters.WithIntDefault(5)),
|
|
},
|
|
},
|
|
{
|
|
name: "float default",
|
|
in: []map[string]any{
|
|
{
|
|
"name": "my_float",
|
|
"type": "float",
|
|
"default": 1.1,
|
|
"description": "my param is a float",
|
|
},
|
|
},
|
|
want: parameters.Parameters{
|
|
parameters.NewFloatParameter("my_float", "my param is a float", parameters.WithFloatDefault(1.1)),
|
|
},
|
|
},
|
|
{
|
|
name: "bool default",
|
|
in: []map[string]any{
|
|
{
|
|
"name": "my_bool",
|
|
"type": "boolean",
|
|
"default": true,
|
|
"description": "this param is a boolean",
|
|
},
|
|
},
|
|
want: parameters.Parameters{
|
|
parameters.NewBooleanParameter("my_bool", "this param is a boolean", parameters.WithBooleanDefault(true)),
|
|
},
|
|
},
|
|
{
|
|
name: "string array default",
|
|
in: []map[string]any{
|
|
{
|
|
"name": "my_array",
|
|
"type": "array",
|
|
"default": []any{"foo", "bar"},
|
|
"description": "this param is an array of strings",
|
|
"items": map[string]string{
|
|
"name": "my_string",
|
|
"type": "string",
|
|
"description": "string item",
|
|
},
|
|
},
|
|
},
|
|
want: parameters.Parameters{
|
|
parameters.NewArrayParameter("my_array", "this param is an array of strings", parameters.NewStringParameter("my_string", "string item"), parameters.WithArrayDefault([]any{"foo", "bar"})),
|
|
},
|
|
},
|
|
{
|
|
name: "float array default",
|
|
in: []map[string]any{
|
|
{
|
|
"name": "my_array",
|
|
"type": "array",
|
|
"default": []any{1.0, 1.1},
|
|
"description": "this param is an array of floats",
|
|
"items": map[string]string{
|
|
"name": "my_float",
|
|
"type": "float",
|
|
"description": "float item",
|
|
},
|
|
},
|
|
},
|
|
want: parameters.Parameters{
|
|
parameters.NewArrayParameter("my_array", "this param is an array of floats", parameters.NewFloatParameter("my_float", "float item"), parameters.WithArrayDefault([]any{1.0, 1.1})),
|
|
},
|
|
},
|
|
{
|
|
name: "map with string values",
|
|
in: []map[string]any{
|
|
{
|
|
"name": "my_map",
|
|
"type": "map",
|
|
"description": "this param is a map of strings",
|
|
"valueType": "string",
|
|
},
|
|
},
|
|
want: parameters.Parameters{
|
|
parameters.NewMapParameter("my_map", "this param is a map of strings", "string"),
|
|
},
|
|
},
|
|
{
|
|
name: "map not required",
|
|
in: []map[string]any{
|
|
{
|
|
"name": "my_map",
|
|
"type": "map",
|
|
"description": "this param is a map of strings",
|
|
"required": false,
|
|
"valueType": "string",
|
|
},
|
|
},
|
|
want: parameters.Parameters{
|
|
parameters.NewMapParameter("my_map", "this param is a map of strings", "string", parameters.WithMapRequired(false)),
|
|
},
|
|
},
|
|
{
|
|
name: "map with default",
|
|
in: []map[string]any{
|
|
{
|
|
"name": "my_map",
|
|
"type": "map",
|
|
"description": "this param is a map of strings",
|
|
"default": map[string]any{"key1": "val1"},
|
|
"valueType": "string",
|
|
},
|
|
},
|
|
want: parameters.Parameters{
|
|
parameters.NewMapParameter("my_map", "this param is a map of strings", "string", parameters.WithMapDefault(map[string]any{"key1": "val1"})),
|
|
},
|
|
},
|
|
{
|
|
name: "generic map (no valueType)",
|
|
in: []map[string]any{
|
|
{
|
|
"name": "my_generic_map",
|
|
"type": "map",
|
|
"description": "this param is a generic map",
|
|
},
|
|
},
|
|
want: parameters.Parameters{
|
|
parameters.NewMapParameter("my_generic_map", "this param is a generic map", ""),
|
|
},
|
|
},
|
|
}
|
|
for _, tc := range tcs {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
var got parameters.Parameters
|
|
// parse map to bytes
|
|
data, err := yaml.Marshal(tc.in)
|
|
if err != nil {
|
|
t.Fatalf("unable to marshal input to yaml: %s", err)
|
|
}
|
|
// parse bytes to object
|
|
err = yaml.UnmarshalContext(ctx, data, &got)
|
|
if err != nil {
|
|
t.Fatalf("unable to unmarshal: %s", err)
|
|
}
|
|
if diff := cmp.Diff(tc.want, got); diff != "" {
|
|
t.Fatalf("incorrect parse: diff %v", diff)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAuthParametersMarshal(t *testing.T) {
|
|
ctx, err := testutils.ContextWithNewLogger()
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %s", err)
|
|
}
|
|
authServices := []parameters.ParamAuthService{{Name: "my-google-auth-service", Field: "user_id"}, {Name: "other-auth-service", Field: "user_id"}}
|
|
tcs := []struct {
|
|
name string
|
|
in []map[string]any
|
|
want parameters.Parameters
|
|
}{
|
|
{
|
|
name: "string",
|
|
in: []map[string]any{
|
|
{
|
|
"name": "my_string",
|
|
"type": "string",
|
|
"description": "this param is a string",
|
|
"authServices": []map[string]string{
|
|
{
|
|
"name": "my-google-auth-service",
|
|
"field": "user_id",
|
|
},
|
|
{
|
|
"name": "other-auth-service",
|
|
"field": "user_id",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
want: parameters.Parameters{
|
|
parameters.NewStringParameter("my_string", "this param is a string", parameters.WithStringAuth(authServices)),
|
|
},
|
|
},
|
|
{
|
|
name: "string with authServices",
|
|
in: []map[string]any{
|
|
{
|
|
"name": "my_string",
|
|
"type": "string",
|
|
"description": "this param is a string",
|
|
"authServices": []map[string]string{
|
|
{
|
|
"name": "my-google-auth-service",
|
|
"field": "user_id",
|
|
},
|
|
{
|
|
"name": "other-auth-service",
|
|
"field": "user_id",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
want: parameters.Parameters{
|
|
parameters.NewStringParameter("my_string", "this param is a string", parameters.WithStringAuth(authServices)),
|
|
},
|
|
},
|
|
{
|
|
name: "int",
|
|
in: []map[string]any{
|
|
{
|
|
"name": "my_integer",
|
|
"type": "integer",
|
|
"description": "this param is an int",
|
|
"authServices": []map[string]string{
|
|
{
|
|
"name": "my-google-auth-service",
|
|
"field": "user_id",
|
|
},
|
|
{
|
|
"name": "other-auth-service",
|
|
"field": "user_id",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
want: parameters.Parameters{
|
|
parameters.NewIntParameter("my_integer", "this param is an int", parameters.WithIntAuth(authServices)),
|
|
},
|
|
},
|
|
{
|
|
name: "int with authServices",
|
|
in: []map[string]any{
|
|
{
|
|
"name": "my_integer",
|
|
"type": "integer",
|
|
"description": "this param is an int",
|
|
"authServices": []map[string]string{
|
|
{
|
|
"name": "my-google-auth-service",
|
|
"field": "user_id",
|
|
},
|
|
{
|
|
"name": "other-auth-service",
|
|
"field": "user_id",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
want: parameters.Parameters{
|
|
parameters.NewIntParameter("my_integer", "this param is an int", parameters.WithIntAuth(authServices)),
|
|
},
|
|
},
|
|
{
|
|
name: "float",
|
|
in: []map[string]any{
|
|
{
|
|
"name": "my_float",
|
|
"type": "float",
|
|
"description": "my param is a float",
|
|
"authServices": []map[string]string{
|
|
{
|
|
"name": "my-google-auth-service",
|
|
"field": "user_id",
|
|
},
|
|
{
|
|
"name": "other-auth-service",
|
|
"field": "user_id",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
want: parameters.Parameters{
|
|
parameters.NewFloatParameter("my_float", "my param is a float", parameters.WithFloatAuth(authServices)),
|
|
},
|
|
},
|
|
{
|
|
name: "float with authServices",
|
|
in: []map[string]any{
|
|
{
|
|
"name": "my_float",
|
|
"type": "float",
|
|
"description": "my param is a float",
|
|
"authServices": []map[string]string{
|
|
{
|
|
"name": "my-google-auth-service",
|
|
"field": "user_id",
|
|
},
|
|
{
|
|
"name": "other-auth-service",
|
|
"field": "user_id",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
want: parameters.Parameters{
|
|
parameters.NewFloatParameter("my_float", "my param is a float", parameters.WithFloatAuth(authServices)),
|
|
},
|
|
},
|
|
{
|
|
name: "bool",
|
|
in: []map[string]any{
|
|
{
|
|
"name": "my_bool",
|
|
"type": "boolean",
|
|
"description": "this param is a boolean",
|
|
"authServices": []map[string]string{
|
|
{
|
|
"name": "my-google-auth-service",
|
|
"field": "user_id",
|
|
},
|
|
{
|
|
"name": "other-auth-service",
|
|
"field": "user_id",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
want: parameters.Parameters{
|
|
parameters.NewBooleanParameter("my_bool", "this param is a boolean", parameters.WithBooleanAuth(authServices)),
|
|
},
|
|
},
|
|
{
|
|
name: "bool with authServices",
|
|
in: []map[string]any{
|
|
{
|
|
"name": "my_bool",
|
|
"type": "boolean",
|
|
"description": "this param is a boolean",
|
|
"authServices": []map[string]string{
|
|
{
|
|
"name": "my-google-auth-service",
|
|
"field": "user_id",
|
|
},
|
|
{
|
|
"name": "other-auth-service",
|
|
"field": "user_id",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
want: parameters.Parameters{
|
|
parameters.NewBooleanParameter("my_bool", "this param is a boolean", parameters.WithBooleanAuth(authServices)),
|
|
},
|
|
},
|
|
{
|
|
name: "string array",
|
|
in: []map[string]any{
|
|
{
|
|
"name": "my_array",
|
|
"type": "array",
|
|
"description": "this param is an array of strings",
|
|
"items": map[string]string{
|
|
"name": "my_string",
|
|
"type": "string",
|
|
"description": "string item",
|
|
},
|
|
"authServices": []map[string]string{
|
|
{
|
|
"name": "my-google-auth-service",
|
|
"field": "user_id",
|
|
},
|
|
{
|
|
"name": "other-auth-service",
|
|
"field": "user_id",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
want: parameters.Parameters{
|
|
parameters.NewArrayParameter("my_array", "this param is an array of strings", parameters.NewStringParameter("my_string", "string item"), parameters.WithArrayAuth(authServices)),
|
|
},
|
|
},
|
|
{
|
|
name: "string array with authServices",
|
|
in: []map[string]any{
|
|
{
|
|
"name": "my_array",
|
|
"type": "array",
|
|
"description": "this param is an array of strings",
|
|
"items": map[string]string{
|
|
"name": "my_string",
|
|
"type": "string",
|
|
"description": "string item",
|
|
},
|
|
"authServices": []map[string]string{
|
|
{
|
|
"name": "my-google-auth-service",
|
|
"field": "user_id",
|
|
},
|
|
{
|
|
"name": "other-auth-service",
|
|
"field": "user_id",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
want: parameters.Parameters{
|
|
parameters.NewArrayParameter("my_array", "this param is an array of strings", parameters.NewStringParameter("my_string", "string item"), parameters.WithArrayAuth(authServices)),
|
|
},
|
|
},
|
|
{
|
|
name: "float array",
|
|
in: []map[string]any{
|
|
{
|
|
"name": "my_array",
|
|
"type": "array",
|
|
"description": "this param is an array of floats",
|
|
"items": map[string]string{
|
|
"name": "my_float",
|
|
"type": "float",
|
|
"description": "float item",
|
|
},
|
|
"authServices": []map[string]string{
|
|
{
|
|
"name": "my-google-auth-service",
|
|
"field": "user_id",
|
|
},
|
|
{
|
|
"name": "other-auth-service",
|
|
"field": "user_id",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
want: parameters.Parameters{
|
|
parameters.NewArrayParameter("my_array", "this param is an array of floats", parameters.NewFloatParameter("my_float", "float item"), parameters.WithArrayAuth(authServices)),
|
|
},
|
|
},
|
|
{
|
|
name: "map",
|
|
in: []map[string]any{
|
|
{
|
|
"name": "my_map",
|
|
"type": "map",
|
|
"description": "this param is a map of strings",
|
|
"valueType": "string",
|
|
"authServices": []map[string]string{
|
|
{"name": "my-google-auth-service", "field": "user_id"},
|
|
{"name": "other-auth-service", "field": "user_id"},
|
|
},
|
|
},
|
|
},
|
|
want: parameters.Parameters{
|
|
parameters.NewMapParameter("my_map", "this param is a map of strings", "string", parameters.WithMapAuth(authServices)),
|
|
},
|
|
},
|
|
}
|
|
for _, tc := range tcs {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
var got parameters.Parameters
|
|
// parse map to bytes
|
|
data, err := yaml.Marshal(tc.in)
|
|
if err != nil {
|
|
t.Fatalf("unable to marshal input to yaml: %s", err)
|
|
}
|
|
// parse bytes to object
|
|
err = yaml.UnmarshalContext(ctx, data, &got)
|
|
if err != nil {
|
|
t.Fatalf("unable to unmarshal: %s", err)
|
|
}
|
|
if diff := cmp.Diff(tc.want, got); diff != "" {
|
|
t.Fatalf("incorrect parse: diff %v", diff)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParametersParse(t *testing.T) {
|
|
intValue := 2
|
|
floatValue := 1.5
|
|
tcs := []struct {
|
|
name string
|
|
params parameters.Parameters
|
|
in map[string]any
|
|
want parameters.ParamValues
|
|
}{
|
|
// ... (primitive type tests are unchanged)
|
|
{
|
|
name: "string",
|
|
params: parameters.Parameters{
|
|
parameters.NewStringParameter("my_string", "this param is a string"),
|
|
},
|
|
in: map[string]any{
|
|
"my_string": "hello world",
|
|
},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_string", Value: "hello world"}},
|
|
},
|
|
{
|
|
name: "not string",
|
|
params: parameters.Parameters{
|
|
parameters.NewStringParameter("my_string", "this param is a string"),
|
|
},
|
|
in: map[string]any{
|
|
"my_string": 4,
|
|
},
|
|
},
|
|
{
|
|
name: "string allowed",
|
|
params: parameters.Parameters{
|
|
parameters.NewStringParameter("my_string", "this param is a string", parameters.WithStringAllowedValues([]any{"foo"})),
|
|
},
|
|
in: map[string]any{
|
|
"my_string": "foo",
|
|
},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_string", Value: "foo"}},
|
|
},
|
|
{
|
|
name: "string allowed regex",
|
|
params: parameters.Parameters{
|
|
parameters.NewStringParameter("my_string", "this param is a string", parameters.WithStringAllowedValues([]any{"^f.*"})),
|
|
},
|
|
in: map[string]any{
|
|
"my_string": "foo",
|
|
},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_string", Value: "foo"}},
|
|
},
|
|
{
|
|
name: "string not allowed",
|
|
params: parameters.Parameters{
|
|
parameters.NewStringParameter("my_string", "this param is a string", parameters.WithStringAllowedValues([]any{"foo"})),
|
|
},
|
|
in: map[string]any{
|
|
"my_string": "bar",
|
|
},
|
|
},
|
|
{
|
|
name: "string not allowed regex",
|
|
params: parameters.Parameters{
|
|
parameters.NewStringParameter("my_string", "this param is a string", parameters.WithStringAllowedValues([]any{"^f.*"})),
|
|
},
|
|
in: map[string]any{
|
|
"my_string": "bar",
|
|
},
|
|
},
|
|
{
|
|
name: "string excluded",
|
|
params: parameters.Parameters{
|
|
parameters.NewStringParameter("my_string", "this param is a string", parameters.WithStringExcludedValues([]any{"foo"})),
|
|
},
|
|
in: map[string]any{
|
|
"my_string": "foo",
|
|
},
|
|
},
|
|
{
|
|
name: "string excluded regex",
|
|
params: parameters.Parameters{
|
|
parameters.NewStringParameter("my_string", "this param is a string", parameters.WithStringExcludedValues([]any{"^f.*"})),
|
|
},
|
|
in: map[string]any{
|
|
"my_string": "foo",
|
|
},
|
|
},
|
|
{
|
|
name: "string not excluded",
|
|
params: parameters.Parameters{
|
|
parameters.NewStringParameter("my_string", "this param is a string", parameters.WithStringExcludedValues([]any{"foo"})),
|
|
},
|
|
in: map[string]any{
|
|
"my_string": "bar",
|
|
},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_string", Value: "bar"}},
|
|
},
|
|
{
|
|
name: "string with escape backticks",
|
|
params: parameters.Parameters{
|
|
parameters.NewStringParameter("my_string", "this param is a string", parameters.WithStringEscape("backticks")),
|
|
},
|
|
in: map[string]any{
|
|
"my_string": "foo",
|
|
},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_string", Value: "`foo`"}},
|
|
},
|
|
{
|
|
name: "string with escape double quotes",
|
|
params: parameters.Parameters{
|
|
parameters.NewStringParameter("my_string", "this param is a string", parameters.WithStringEscape("double-quotes")),
|
|
},
|
|
in: map[string]any{
|
|
"my_string": "foo",
|
|
},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_string", Value: `"foo"`}},
|
|
},
|
|
{
|
|
name: "string with escape single quotes",
|
|
params: parameters.Parameters{
|
|
parameters.NewStringParameter("my_string", "this param is a string", parameters.WithStringEscape("single-quotes")),
|
|
},
|
|
in: map[string]any{
|
|
"my_string": "foo",
|
|
},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_string", Value: `'foo'`}},
|
|
},
|
|
{
|
|
name: "string with escape square brackets",
|
|
params: parameters.Parameters{
|
|
parameters.NewStringParameter("my_string", "this param is a string", parameters.WithStringEscape("square-brackets")),
|
|
},
|
|
in: map[string]any{
|
|
"my_string": "foo",
|
|
},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_string", Value: "[foo]"}},
|
|
},
|
|
{
|
|
name: "string with escape backticks containing backtick (injection prevention)",
|
|
params: parameters.Parameters{
|
|
parameters.NewStringParameter("my_string", "this param is a string", parameters.WithStringEscape("backticks")),
|
|
},
|
|
in: map[string]any{
|
|
"my_string": "users` OR 1=1--",
|
|
},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_string", Value: "`users`` OR 1=1--`"}},
|
|
},
|
|
{
|
|
name: "string with escape double quotes containing double quote (injection prevention)",
|
|
params: parameters.Parameters{
|
|
parameters.NewStringParameter("my_string", "this param is a string", parameters.WithStringEscape("double-quotes")),
|
|
},
|
|
in: map[string]any{
|
|
"my_string": `col" OR 1=1--`,
|
|
},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_string", Value: `"col"" OR 1=1--"`}},
|
|
},
|
|
{
|
|
name: "string with escape single quotes containing single quote (injection prevention)",
|
|
params: parameters.Parameters{
|
|
parameters.NewStringParameter("my_string", "this param is a string", parameters.WithStringEscape("single-quotes")),
|
|
},
|
|
in: map[string]any{
|
|
"my_string": "val' OR 1=1--",
|
|
},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_string", Value: "'val'' OR 1=1--'"}},
|
|
},
|
|
{
|
|
name: "string with escape square brackets containing closing bracket (injection prevention)",
|
|
params: parameters.Parameters{
|
|
parameters.NewStringParameter("my_string", "this param is a string", parameters.WithStringEscape("square-brackets")),
|
|
},
|
|
in: map[string]any{
|
|
"my_string": "col] OR 1=1--",
|
|
},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_string", Value: "[col]] OR 1=1--]"}},
|
|
},
|
|
{
|
|
name: "int",
|
|
params: parameters.Parameters{
|
|
parameters.NewIntParameter("my_int", "this param is an int"),
|
|
},
|
|
in: map[string]any{
|
|
"my_int": 100,
|
|
},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_int", Value: 100}},
|
|
},
|
|
{
|
|
name: "not int",
|
|
params: parameters.Parameters{
|
|
parameters.NewIntParameter("my_int", "this param is an int"),
|
|
},
|
|
in: map[string]any{
|
|
"my_int": 14.5,
|
|
},
|
|
},
|
|
{
|
|
name: "not int (big)",
|
|
params: parameters.Parameters{
|
|
parameters.NewIntParameter("my_int", "this param is an int"),
|
|
},
|
|
in: map[string]any{
|
|
"my_int": math.MaxInt64,
|
|
},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_int", Value: math.MaxInt64}},
|
|
},
|
|
{
|
|
name: "int allowed",
|
|
params: parameters.Parameters{
|
|
parameters.NewIntParameter("my_int", "this param is an int", parameters.WithIntAllowedValues([]any{1})),
|
|
},
|
|
in: map[string]any{
|
|
"my_int": 1,
|
|
},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_int", Value: 1}},
|
|
},
|
|
{
|
|
name: "int allowed regex",
|
|
params: parameters.Parameters{
|
|
parameters.NewIntParameter("my_int", "this param is an int", parameters.WithIntAllowedValues([]any{"^\\d{2}$"})),
|
|
},
|
|
in: map[string]any{
|
|
"my_int": 10,
|
|
},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_int", Value: 10}},
|
|
},
|
|
{
|
|
name: "int not allowed",
|
|
params: parameters.Parameters{
|
|
parameters.NewIntParameter("my_int", "this param is an int", parameters.WithIntAllowedValues([]any{1})),
|
|
},
|
|
in: map[string]any{
|
|
"my_int": 2,
|
|
},
|
|
},
|
|
{
|
|
name: "int not allowed regex",
|
|
params: parameters.Parameters{
|
|
parameters.NewIntParameter("my_int", "this param is an int", parameters.WithIntAllowedValues([]any{"^\\d{2}$"})),
|
|
},
|
|
in: map[string]any{
|
|
"my_int": 100,
|
|
},
|
|
},
|
|
{
|
|
name: "int excluded",
|
|
params: parameters.Parameters{
|
|
parameters.NewIntParameter("my_int", "this param is an int", parameters.WithIntExcludedValues([]any{1})),
|
|
},
|
|
in: map[string]any{
|
|
"my_int": 1,
|
|
},
|
|
},
|
|
{
|
|
name: "int excluded regex",
|
|
params: parameters.Parameters{
|
|
parameters.NewIntParameter("my_int", "this param is an int", parameters.WithIntExcludedValues([]any{"^\\d{2}$"})),
|
|
},
|
|
in: map[string]any{
|
|
"my_int": 10,
|
|
},
|
|
},
|
|
{
|
|
name: "int not excluded",
|
|
params: parameters.Parameters{
|
|
parameters.NewIntParameter("my_int", "this param is an int", parameters.WithIntExcludedValues([]any{1})),
|
|
},
|
|
in: map[string]any{
|
|
"my_int": 2,
|
|
},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_int", Value: 2}},
|
|
},
|
|
{
|
|
name: "int not excluded regex",
|
|
params: parameters.Parameters{
|
|
parameters.NewIntParameter("my_int", "this param is an int", parameters.WithIntExcludedValues([]any{"^\\d{2}$"})),
|
|
},
|
|
in: map[string]any{
|
|
"my_int": 2,
|
|
},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_int", Value: 2}},
|
|
},
|
|
{
|
|
name: "int minValue",
|
|
params: parameters.Parameters{
|
|
parameters.NewIntParameter("my_int", "this param is an int", parameters.WithIntMinValue(&intValue), parameters.WithIntMaxValue(nil)),
|
|
},
|
|
in: map[string]any{
|
|
"my_int": 3,
|
|
},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_int", Value: 3}},
|
|
},
|
|
{
|
|
name: "int minValue disallow",
|
|
params: parameters.Parameters{
|
|
parameters.NewIntParameter("my_int", "this param is an int", parameters.WithIntMinValue(&intValue), parameters.WithIntMaxValue(nil)),
|
|
},
|
|
in: map[string]any{
|
|
"my_int": 1,
|
|
},
|
|
},
|
|
{
|
|
name: "int maxValue",
|
|
params: parameters.Parameters{
|
|
parameters.NewIntParameter("my_int", "this param is an int", parameters.WithIntMinValue(nil), parameters.WithIntMaxValue(&intValue)),
|
|
},
|
|
in: map[string]any{
|
|
"my_int": 1,
|
|
},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_int", Value: 1}},
|
|
},
|
|
{
|
|
name: "int maxValue disallow",
|
|
params: parameters.Parameters{
|
|
parameters.NewIntParameter("my_int", "this param is an int", parameters.WithIntMinValue(nil), parameters.WithIntMaxValue(&intValue)),
|
|
},
|
|
in: map[string]any{
|
|
"my_int": 3,
|
|
},
|
|
},
|
|
{
|
|
name: "float",
|
|
params: parameters.Parameters{
|
|
parameters.NewFloatParameter("my_float", "this param is a float"),
|
|
},
|
|
in: map[string]any{
|
|
"my_float": 1.5,
|
|
},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_float", Value: 1.5}},
|
|
},
|
|
{
|
|
name: "not float",
|
|
params: parameters.Parameters{
|
|
parameters.NewFloatParameter("my_float", "this param is a float"),
|
|
},
|
|
in: map[string]any{
|
|
"my_float": true,
|
|
},
|
|
},
|
|
{
|
|
name: "float allowed",
|
|
params: parameters.Parameters{
|
|
parameters.NewFloatParameter("my_float", "this param is a float", parameters.WithFloatAllowedValues([]any{1.1})),
|
|
},
|
|
in: map[string]any{
|
|
"my_float": 1.1,
|
|
},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_float", Value: 1.1}},
|
|
},
|
|
{
|
|
name: "float allowed regex",
|
|
params: parameters.Parameters{
|
|
parameters.NewFloatParameter("my_float", "this param is a float", parameters.WithFloatAllowedValues([]any{"^0\\.\\d+$"})),
|
|
},
|
|
in: map[string]any{
|
|
"my_float": 0.99,
|
|
},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_float", Value: 0.99}},
|
|
},
|
|
{
|
|
name: "float not allowed",
|
|
params: parameters.Parameters{
|
|
parameters.NewFloatParameter("my_float", "this param is a float", parameters.WithFloatAllowedValues([]any{1.1})),
|
|
},
|
|
in: map[string]any{
|
|
"my_float": 1.2,
|
|
},
|
|
},
|
|
{
|
|
name: "float not allowed regex",
|
|
params: parameters.Parameters{
|
|
parameters.NewFloatParameter("my_float", "this param is a float", parameters.WithFloatAllowedValues([]any{"^0\\.\\d+$"})),
|
|
},
|
|
in: map[string]any{
|
|
"my_float": 1.99,
|
|
},
|
|
},
|
|
{
|
|
name: "float excluded",
|
|
params: parameters.Parameters{
|
|
parameters.NewFloatParameter("my_float", "this param is a float", parameters.WithFloatExcludedValues([]any{1.1})),
|
|
},
|
|
in: map[string]any{
|
|
"my_float": 1.1,
|
|
},
|
|
},
|
|
{
|
|
name: "float excluded regex",
|
|
params: parameters.Parameters{
|
|
parameters.NewFloatParameter("my_float", "this param is a float", parameters.WithFloatExcludedValues([]any{"^0\\.\\d+$"})),
|
|
},
|
|
in: map[string]any{
|
|
"my_float": 0.99,
|
|
},
|
|
},
|
|
{
|
|
name: "float not excluded",
|
|
params: parameters.Parameters{
|
|
parameters.NewFloatParameter("my_float", "this param is a float", parameters.WithFloatExcludedValues([]any{1.1})),
|
|
},
|
|
in: map[string]any{
|
|
"my_float": 1.2,
|
|
},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_float", Value: 1.2}},
|
|
},
|
|
{
|
|
name: "float not excluded regex",
|
|
params: parameters.Parameters{
|
|
parameters.NewFloatParameter("my_float", "this param is a float", parameters.WithFloatExcludedValues([]any{"^0\\.\\d+$"})),
|
|
},
|
|
in: map[string]any{
|
|
"my_float": 1.99,
|
|
},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_float", Value: 1.99}},
|
|
},
|
|
|
|
{
|
|
name: "float minValue",
|
|
params: parameters.Parameters{
|
|
parameters.NewFloatParameter("my_float", "this param is a float", parameters.WithFloatMinValue(&floatValue), parameters.WithFloatMaxValue(nil)),
|
|
},
|
|
in: map[string]any{
|
|
"my_float": 1.8,
|
|
},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_float", Value: 1.8}},
|
|
},
|
|
{
|
|
name: "float minValue disallow",
|
|
params: parameters.Parameters{
|
|
parameters.NewFloatParameter("my_float", "this param is a float", parameters.WithFloatMinValue(&floatValue), parameters.WithFloatMaxValue(nil)),
|
|
},
|
|
in: map[string]any{
|
|
"my_float": 1.2,
|
|
},
|
|
},
|
|
{
|
|
name: "float maxValue",
|
|
params: parameters.Parameters{
|
|
parameters.NewFloatParameter("my_float", "this param is a float", parameters.WithFloatMinValue(nil), parameters.WithFloatMaxValue(&floatValue)),
|
|
},
|
|
in: map[string]any{
|
|
"my_float": 1.2,
|
|
},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_float", Value: 1.2}},
|
|
},
|
|
{
|
|
name: "float maxValue disallow",
|
|
params: parameters.Parameters{
|
|
parameters.NewFloatParameter("my_float", "this param is a float", parameters.WithFloatMinValue(nil), parameters.WithFloatMaxValue(&floatValue)),
|
|
},
|
|
in: map[string]any{
|
|
"my_float": 1.8,
|
|
},
|
|
},
|
|
{
|
|
name: "bool",
|
|
params: parameters.Parameters{
|
|
parameters.NewBooleanParameter("my_bool", "this param is a bool"),
|
|
},
|
|
in: map[string]any{
|
|
"my_bool": true,
|
|
},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_bool", Value: true}},
|
|
},
|
|
{
|
|
name: "not bool",
|
|
params: parameters.Parameters{
|
|
parameters.NewBooleanParameter("my_bool", "this param is a bool"),
|
|
},
|
|
in: map[string]any{
|
|
"my_bool": 1.5,
|
|
},
|
|
},
|
|
{
|
|
name: "bool allowed",
|
|
params: parameters.Parameters{
|
|
parameters.NewBooleanParameter("my_bool", "this param is a bool", parameters.WithBooleanAllowedValues([]any{false})),
|
|
},
|
|
in: map[string]any{
|
|
"my_bool": false,
|
|
},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_bool", Value: false}},
|
|
},
|
|
{
|
|
name: "bool not allowed",
|
|
params: parameters.Parameters{
|
|
parameters.NewBooleanParameter("my_bool", "this param is a bool", parameters.WithBooleanAllowedValues([]any{false})),
|
|
},
|
|
in: map[string]any{
|
|
"my_bool": true,
|
|
},
|
|
},
|
|
{
|
|
name: "bool excluded",
|
|
params: parameters.Parameters{
|
|
parameters.NewBooleanParameter("my_bool", "this param is a bool", parameters.WithBooleanExcludedValues([]any{true})),
|
|
},
|
|
in: map[string]any{
|
|
"my_bool": true,
|
|
},
|
|
},
|
|
{
|
|
name: "bool not excluded",
|
|
params: parameters.Parameters{
|
|
parameters.NewBooleanParameter("my_bool", "this param is a bool", parameters.WithBooleanExcludedValues([]any{false})),
|
|
},
|
|
in: map[string]any{
|
|
"my_bool": true,
|
|
},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_bool", Value: true}},
|
|
},
|
|
{
|
|
name: "string default",
|
|
params: parameters.Parameters{
|
|
parameters.NewStringParameter("my_string", "this param is a string", parameters.WithStringDefault("foo")),
|
|
},
|
|
in: map[string]any{},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_string", Value: "foo"}},
|
|
},
|
|
{
|
|
name: "int default",
|
|
params: parameters.Parameters{
|
|
parameters.NewIntParameter("my_int", "this param is an int", parameters.WithIntDefault(100)),
|
|
},
|
|
in: map[string]any{},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_int", Value: 100}},
|
|
},
|
|
{
|
|
name: "int (big)",
|
|
params: parameters.Parameters{
|
|
parameters.NewIntParameter("my_big_int", "this param is an int", parameters.WithIntDefault(math.MaxInt64)),
|
|
},
|
|
in: map[string]any{},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_big_int", Value: math.MaxInt64}},
|
|
},
|
|
{
|
|
name: "float default",
|
|
params: parameters.Parameters{
|
|
parameters.NewFloatParameter("my_float", "this param is a float", parameters.WithFloatDefault(1.1)),
|
|
},
|
|
in: map[string]any{},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_float", Value: 1.1}},
|
|
},
|
|
{
|
|
name: "bool default",
|
|
params: parameters.Parameters{
|
|
parameters.NewBooleanParameter("my_bool", "this param is a bool", parameters.WithBooleanDefault(true)),
|
|
},
|
|
in: map[string]any{},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_bool", Value: true}},
|
|
},
|
|
{
|
|
name: "string not required",
|
|
params: parameters.Parameters{
|
|
parameters.NewStringParameter("my_string", "this param is a string", parameters.WithStringRequired(false)),
|
|
},
|
|
in: map[string]any{},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_string", Value: nil}},
|
|
},
|
|
{
|
|
name: "int not required",
|
|
params: parameters.Parameters{
|
|
parameters.NewIntParameter("my_int", "this param is an int", parameters.WithIntRequired(false)),
|
|
},
|
|
in: map[string]any{},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_int", Value: nil}},
|
|
},
|
|
{
|
|
name: "float not required",
|
|
params: parameters.Parameters{
|
|
parameters.NewFloatParameter("my_float", "this param is a float", parameters.WithFloatRequired(false)),
|
|
},
|
|
in: map[string]any{},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_float", Value: nil}},
|
|
},
|
|
{
|
|
name: "bool not required",
|
|
params: parameters.Parameters{
|
|
parameters.NewBooleanParameter("my_bool", "this param is a bool", parameters.WithBooleanRequired(false)),
|
|
},
|
|
in: map[string]any{},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_bool", Value: nil}},
|
|
},
|
|
{
|
|
name: "array with string escape",
|
|
params: parameters.Parameters{
|
|
parameters.NewArrayParameter("my_array", "an array", parameters.NewStringParameter("my_string", "string item", parameters.WithStringEscape("backticks"))),
|
|
},
|
|
in: map[string]any{
|
|
"my_array": []string{"val1", "val2"},
|
|
},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_array", Value: []any{string("`val1`"), string("`val2`")}}},
|
|
},
|
|
{
|
|
name: "map",
|
|
params: parameters.Parameters{
|
|
parameters.NewMapParameter("my_map", "a map", "string"),
|
|
},
|
|
in: map[string]any{
|
|
"my_map": map[string]any{"key1": "val1", "key2": "val2"},
|
|
},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_map", Value: map[string]any{"key1": "val1", "key2": "val2"}}},
|
|
},
|
|
{
|
|
name: "generic map",
|
|
params: parameters.Parameters{
|
|
parameters.NewMapParameter("my_map_generic_type", "a generic map", ""),
|
|
},
|
|
in: map[string]any{
|
|
"my_map_generic_type": map[string]any{"key1": "val1", "key2": 123, "key3": true},
|
|
},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_map_generic_type", Value: map[string]any{"key1": "val1", "key2": int64(123), "key3": true}}},
|
|
},
|
|
{
|
|
name: "not map (value type mismatch)",
|
|
params: parameters.Parameters{
|
|
parameters.NewMapParameter("my_map", "a map", "string"),
|
|
},
|
|
in: map[string]any{
|
|
"my_map": map[string]any{"key1": 123},
|
|
},
|
|
},
|
|
{
|
|
name: "map default",
|
|
params: parameters.Parameters{
|
|
parameters.NewMapParameter("my_map_default", "a map", "string", parameters.WithMapDefault(map[string]any{"default_key": "default_val"})),
|
|
},
|
|
in: map[string]any{},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_map_default", Value: map[string]any{"default_key": "default_val"}}},
|
|
},
|
|
{
|
|
name: "map not required",
|
|
params: parameters.Parameters{
|
|
parameters.NewMapParameter("my_map_not_required", "a map", "string", parameters.WithMapRequired(false)),
|
|
},
|
|
in: map[string]any{},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_map_not_required", Value: nil}},
|
|
},
|
|
{
|
|
name: "map allowed",
|
|
params: parameters.Parameters{
|
|
parameters.NewMapParameter("my_map", "a map", "string", parameters.WithMapAllowedValues([]any{map[string]any{"key1": "val1"}})),
|
|
},
|
|
in: map[string]any{
|
|
"my_map": map[string]any{"key1": "val1"},
|
|
},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_map", Value: map[string]any{"key1": "val1"}}},
|
|
},
|
|
{
|
|
name: "map not allowed",
|
|
params: parameters.Parameters{
|
|
parameters.NewMapParameter("my_map", "a map", "string", parameters.WithMapAllowedValues([]any{map[string]any{"key1": "val1"}})),
|
|
},
|
|
in: map[string]any{
|
|
"my_map": map[string]any{"key1": "val2"},
|
|
},
|
|
},
|
|
{
|
|
name: "map excluded",
|
|
params: parameters.Parameters{
|
|
parameters.NewMapParameter("my_map", "a map", "string", parameters.WithMapExcludedValues([]any{map[string]any{"key1": "val1"}})),
|
|
},
|
|
in: map[string]any{
|
|
"my_map": map[string]any{"key1": "val1"},
|
|
},
|
|
},
|
|
{
|
|
name: "map not excluded",
|
|
params: parameters.Parameters{
|
|
parameters.NewMapParameter("my_map", "a map", "string", parameters.WithMapExcludedValues([]any{map[string]any{"key1": "val1"}})),
|
|
},
|
|
in: map[string]any{
|
|
"my_map": map[string]any{"key1": "val2"},
|
|
},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_map", Value: map[string]any{"key1": "val2"}}},
|
|
},
|
|
}
|
|
for _, tc := range tcs {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
// parse map to bytes
|
|
data, err := json.Marshal(tc.in)
|
|
if err != nil {
|
|
t.Fatalf("unable to marshal input to yaml: %s", err)
|
|
}
|
|
// parse bytes to object
|
|
var m map[string]any
|
|
|
|
d := json.NewDecoder(bytes.NewReader(data))
|
|
d.UseNumber()
|
|
err = d.Decode(&m)
|
|
if err != nil {
|
|
t.Fatalf("unable to unmarshal: %s", err)
|
|
}
|
|
|
|
wantErr := len(tc.want) == 0 // error is expected if no items in want
|
|
gotAll, err := parameters.ParseParams(tc.params, m, make(map[string]map[string]any))
|
|
if err != nil {
|
|
if wantErr {
|
|
return
|
|
}
|
|
t.Fatalf("unexpected error from ParseParams: %s", err)
|
|
}
|
|
if wantErr {
|
|
t.Fatalf("expected error but Param parsed successfully: %s", gotAll)
|
|
}
|
|
|
|
// Use cmp.Diff for robust comparison
|
|
if diff := cmp.Diff(tc.want, gotAll); diff != "" {
|
|
t.Fatalf("ParseParams() mismatch (-want +got):\n%s", diff)
|
|
}
|
|
})
|
|
}
|
|
t.Run("CheckNullForRequiredParam", func(t *testing.T) {
|
|
// Define a required string parameter
|
|
params := parameters.Parameters{
|
|
parameters.NewStringParameter("required_param", "this is required"),
|
|
}
|
|
|
|
// Input map with explicit nil
|
|
input := map[string]any{
|
|
"required_param": nil,
|
|
}
|
|
|
|
// Call ParseParams
|
|
_, err := parameters.ParseParams(params, input, nil)
|
|
|
|
// Expect an error because the parameter is required
|
|
if err == nil {
|
|
t.Errorf("ParseParams allowed explicit nil for required parameter, expected error")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestParseTypeErrorReportsValue(t *testing.T) {
|
|
// When a value fails the type assertion, the error must report the actual
|
|
// offending value, not the zero value of the target type. The primitive
|
|
// parameters already do this; array and map parameters used to pass their
|
|
// (nil) asserted variable instead of the input, producing useless errors
|
|
// like `%!q(<nil>) not type "array"` that hid the bad value from the user.
|
|
tcs := []struct {
|
|
name string
|
|
param parameters.Parameter
|
|
in string
|
|
}{
|
|
{
|
|
name: "array given a non-array",
|
|
param: parameters.NewArrayParameter("my_array", "an array", parameters.NewStringParameter("my_string", "string item")),
|
|
in: "not-an-array",
|
|
},
|
|
{
|
|
name: "map given a non-map",
|
|
param: parameters.NewMapParameter("my_map", "a map", "string"),
|
|
in: "not-a-map",
|
|
},
|
|
}
|
|
for _, tc := range tcs {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
_, err := tc.param.Parse(tc.in)
|
|
if err == nil {
|
|
t.Fatalf("Parse(%q) expected a type error, got nil", tc.in)
|
|
}
|
|
if got := err.Error(); !strings.Contains(got, tc.in) {
|
|
t.Errorf("error %q does not mention the offending value %q", got, tc.in)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAuthParametersParse(t *testing.T) {
|
|
authServices := []parameters.ParamAuthService{
|
|
{
|
|
Name: "my-google-auth-service",
|
|
Field: "auth_field",
|
|
},
|
|
{
|
|
Name: "other-auth-service",
|
|
Field: "other_auth_field",
|
|
}}
|
|
tcs := []struct {
|
|
name string
|
|
params parameters.Parameters
|
|
in map[string]any
|
|
claimsMap map[string]map[string]any
|
|
want parameters.ParamValues
|
|
}{
|
|
{
|
|
name: "string",
|
|
params: parameters.Parameters{
|
|
parameters.NewStringParameter("my_string", "this param is a string", parameters.WithStringAuth(authServices)),
|
|
},
|
|
in: map[string]any{
|
|
"my_string": "hello world",
|
|
},
|
|
claimsMap: map[string]map[string]any{"my-google-auth-service": {"auth_field": "hello"}},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_string", Value: "hello"}},
|
|
},
|
|
{
|
|
name: "not string",
|
|
params: parameters.Parameters{
|
|
parameters.NewStringParameter("my_string", "this param is a string", parameters.WithStringAuth(authServices)),
|
|
},
|
|
in: map[string]any{
|
|
"my_string": 4,
|
|
},
|
|
claimsMap: map[string]map[string]any{},
|
|
},
|
|
{
|
|
name: "int",
|
|
params: parameters.Parameters{
|
|
parameters.NewIntParameter("my_int", "this param is an int", parameters.WithIntAuth(authServices)),
|
|
},
|
|
in: map[string]any{
|
|
"my_int": 100,
|
|
},
|
|
claimsMap: map[string]map[string]any{"other-auth-service": {"other_auth_field": 120}},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_int", Value: 120}},
|
|
},
|
|
{
|
|
name: "not int",
|
|
params: parameters.Parameters{
|
|
parameters.NewIntParameter("my_int", "this param is an int", parameters.WithIntAuth(authServices)),
|
|
},
|
|
in: map[string]any{
|
|
"my_int": 14.5,
|
|
},
|
|
claimsMap: map[string]map[string]any{},
|
|
},
|
|
{
|
|
name: "float",
|
|
params: parameters.Parameters{
|
|
parameters.NewFloatParameter("my_float", "this param is a float", parameters.WithFloatAuth(authServices)),
|
|
},
|
|
in: map[string]any{
|
|
"my_float": 1.5,
|
|
},
|
|
claimsMap: map[string]map[string]any{"my-google-auth-service": {"auth_field": 2.1}},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_float", Value: 2.1}},
|
|
},
|
|
{
|
|
name: "not float",
|
|
params: parameters.Parameters{
|
|
parameters.NewFloatParameter("my_float", "this param is a float", parameters.WithFloatAuth(authServices)),
|
|
},
|
|
in: map[string]any{
|
|
"my_float": true,
|
|
},
|
|
claimsMap: map[string]map[string]any{},
|
|
},
|
|
{
|
|
name: "bool",
|
|
params: parameters.Parameters{
|
|
parameters.NewBooleanParameter("my_bool", "this param is a bool", parameters.WithBooleanAuth(authServices)),
|
|
},
|
|
in: map[string]any{
|
|
"my_bool": true,
|
|
},
|
|
claimsMap: map[string]map[string]any{"my-google-auth-service": {"auth_field": false}},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_bool", Value: false}},
|
|
},
|
|
{
|
|
name: "not bool",
|
|
params: parameters.Parameters{
|
|
parameters.NewBooleanParameter("my_bool", "this param is a bool", parameters.WithBooleanAuth(authServices)),
|
|
},
|
|
in: map[string]any{
|
|
"my_bool": 1.5,
|
|
},
|
|
claimsMap: map[string]map[string]any{},
|
|
},
|
|
{
|
|
name: "username",
|
|
params: parameters.Parameters{
|
|
parameters.NewStringParameter("username", "username string", parameters.WithStringAuth(authServices)),
|
|
},
|
|
in: map[string]any{
|
|
"username": "Violet",
|
|
},
|
|
claimsMap: map[string]map[string]any{"my-google-auth-service": {"auth_field": "Alice"}},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "username", Value: "Alice"}},
|
|
},
|
|
{
|
|
name: "expect claim error",
|
|
params: parameters.Parameters{
|
|
parameters.NewStringParameter("username", "username string", parameters.WithStringAuth(authServices)),
|
|
},
|
|
in: map[string]any{
|
|
"username": "Violet",
|
|
},
|
|
claimsMap: map[string]map[string]any{"my-google-auth-service": {"not_an_auth_field": "Alice"}},
|
|
},
|
|
{
|
|
name: "map",
|
|
params: parameters.Parameters{
|
|
parameters.NewMapParameter("my_map", "a map", "string", parameters.WithMapAuth(authServices)),
|
|
},
|
|
in: map[string]any{"my_map": map[string]any{"key1": "val1"}},
|
|
claimsMap: map[string]map[string]any{"my-google-auth-service": {"auth_field": map[string]any{"authed_key": "authed_val"}}},
|
|
want: parameters.ParamValues{parameters.ParamValue{Name: "my_map", Value: map[string]any{"authed_key": "authed_val"}}},
|
|
},
|
|
}
|
|
for _, tc := range tcs {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
// parse map to bytes
|
|
data, err := json.Marshal(tc.in)
|
|
if err != nil {
|
|
t.Fatalf("unable to marshal input to yaml: %s", err)
|
|
}
|
|
// parse bytes to object
|
|
var m map[string]any
|
|
d := json.NewDecoder(bytes.NewReader(data))
|
|
d.UseNumber()
|
|
err = d.Decode(&m)
|
|
if err != nil {
|
|
t.Fatalf("unable to unmarshal: %s", err)
|
|
}
|
|
|
|
gotAll, err := parameters.ParseParams(tc.params, m, tc.claimsMap)
|
|
if err != nil {
|
|
if len(tc.want) == 0 {
|
|
// error is expected if no items in want
|
|
return
|
|
}
|
|
t.Fatalf("unexpected error from ParseParams: %s", err)
|
|
}
|
|
|
|
if diff := cmp.Diff(tc.want, gotAll); diff != "" {
|
|
t.Fatalf("ParseParams() mismatch (-want +got):\n%s", diff)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParamValues(t *testing.T) {
|
|
tcs := []struct {
|
|
name string
|
|
in parameters.ParamValues
|
|
wantSlice []any
|
|
wantMap map[string]interface{}
|
|
wantMapOrdered map[string]interface{}
|
|
wantMapWithDollar map[string]interface{}
|
|
}{
|
|
{
|
|
name: "string",
|
|
in: parameters.ParamValues{parameters.ParamValue{Name: "my_bool", Value: true}, parameters.ParamValue{Name: "my_string", Value: "hello world"}},
|
|
wantSlice: []any{true, "hello world"},
|
|
wantMap: map[string]interface{}{"my_bool": true, "my_string": "hello world"},
|
|
wantMapOrdered: map[string]interface{}{"p1": true, "p2": "hello world"},
|
|
wantMapWithDollar: map[string]interface{}{
|
|
"$my_bool": true,
|
|
"$my_string": "hello world",
|
|
},
|
|
},
|
|
}
|
|
for _, tc := range tcs {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
gotSlice := tc.in.AsSlice()
|
|
gotMap := tc.in.AsMap()
|
|
gotMapOrdered := tc.in.AsMapByOrderedKeys()
|
|
gotMapWithDollar := tc.in.AsMapWithDollarPrefix()
|
|
|
|
for i, got := range gotSlice {
|
|
want := tc.wantSlice[i]
|
|
if got != want {
|
|
t.Fatalf("unexpected value: got %q, want %q", got, want)
|
|
}
|
|
}
|
|
for i, got := range gotMap {
|
|
want := tc.wantMap[i]
|
|
if got != want {
|
|
t.Fatalf("unexpected value: got %q, want %q", got, want)
|
|
}
|
|
}
|
|
for i, got := range gotMapOrdered {
|
|
want := tc.wantMapOrdered[i]
|
|
if got != want {
|
|
t.Fatalf("unexpected value: got %q, want %q", got, want)
|
|
}
|
|
}
|
|
for key, got := range gotMapWithDollar {
|
|
want := tc.wantMapWithDollar[key]
|
|
if got != want {
|
|
t.Fatalf("unexpected value in AsMapWithDollarPrefix: got %q, want %q", got, want)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParamManifest(t *testing.T) {
|
|
tcs := []struct {
|
|
name string
|
|
in parameters.Parameter
|
|
want parameters.ParameterManifest
|
|
}{
|
|
{
|
|
name: "string",
|
|
in: parameters.NewStringParameter("foo-string", "bar"),
|
|
want: parameters.ParameterManifest{Name: "foo-string", Type: "string", Required: true, Description: "bar", AuthServices: []string{}},
|
|
},
|
|
{
|
|
name: "int",
|
|
in: parameters.NewIntParameter("foo-int", "bar"),
|
|
want: parameters.ParameterManifest{Name: "foo-int", Type: "integer", Required: true, Description: "bar", AuthServices: []string{}},
|
|
},
|
|
{
|
|
name: "float",
|
|
in: parameters.NewFloatParameter("foo-float", "bar"),
|
|
want: parameters.ParameterManifest{Name: "foo-float", Type: "float", Required: true, Description: "bar", AuthServices: []string{}},
|
|
},
|
|
{
|
|
name: "boolean",
|
|
in: parameters.NewBooleanParameter("foo-bool", "bar"),
|
|
want: parameters.ParameterManifest{Name: "foo-bool", Type: "boolean", Required: true, Description: "bar", AuthServices: []string{}},
|
|
},
|
|
{
|
|
name: "array",
|
|
in: parameters.NewArrayParameter("foo-array", "bar", parameters.NewStringParameter("foo-string", "bar")),
|
|
want: parameters.ParameterManifest{
|
|
Name: "foo-array",
|
|
Type: "array",
|
|
Required: true,
|
|
Description: "bar",
|
|
AuthServices: []string{},
|
|
Items: ¶meters.ParameterManifest{Name: "foo-string", Type: "string", Required: true, Description: "bar", AuthServices: []string{}},
|
|
},
|
|
},
|
|
{
|
|
name: "string default",
|
|
in: parameters.NewStringParameter("foo-string", "bar", parameters.WithStringDefault("foo")),
|
|
want: parameters.ParameterManifest{Name: "foo-string", Type: "string", Required: false, Description: "bar", Default: "foo", AuthServices: []string{}},
|
|
},
|
|
{
|
|
name: "int default",
|
|
in: parameters.NewIntParameter("foo-int", "bar", parameters.WithIntDefault(1)),
|
|
want: parameters.ParameterManifest{Name: "foo-int", Type: "integer", Required: false, Description: "bar", Default: 1, AuthServices: []string{}},
|
|
},
|
|
{
|
|
name: "float default",
|
|
in: parameters.NewFloatParameter("foo-float", "bar", parameters.WithFloatDefault(1.1)),
|
|
want: parameters.ParameterManifest{Name: "foo-float", Type: "float", Required: false, Description: "bar", Default: 1.1, AuthServices: []string{}},
|
|
},
|
|
{
|
|
name: "boolean default",
|
|
in: parameters.NewBooleanParameter("foo-bool", "bar", parameters.WithBooleanDefault(true)),
|
|
want: parameters.ParameterManifest{Name: "foo-bool", Type: "boolean", Required: false, Description: "bar", Default: true, AuthServices: []string{}},
|
|
},
|
|
{
|
|
name: "array default",
|
|
in: parameters.NewArrayParameter("foo-array", "bar", parameters.NewStringParameter("foo-string", "bar"), parameters.WithArrayDefault([]any{"foo", "bar"})),
|
|
want: parameters.ParameterManifest{
|
|
Name: "foo-array",
|
|
Type: "array",
|
|
Required: false,
|
|
Description: "bar",
|
|
Default: []any{"foo", "bar"},
|
|
AuthServices: []string{},
|
|
Items: ¶meters.ParameterManifest{Name: "foo-string", Type: "string", Required: false, Description: "bar", AuthServices: []string{}},
|
|
},
|
|
},
|
|
{
|
|
name: "string not required",
|
|
in: parameters.NewStringParameter("foo-string", "bar", parameters.WithStringRequired(false)),
|
|
want: parameters.ParameterManifest{Name: "foo-string", Type: "string", Required: false, Description: "bar", AuthServices: []string{}},
|
|
},
|
|
{
|
|
name: "int not required",
|
|
in: parameters.NewIntParameter("foo-int", "bar", parameters.WithIntRequired(false)),
|
|
want: parameters.ParameterManifest{Name: "foo-int", Type: "integer", Required: false, Description: "bar", AuthServices: []string{}},
|
|
},
|
|
{
|
|
name: "float not required",
|
|
in: parameters.NewFloatParameter("foo-float", "bar", parameters.WithFloatRequired(false)),
|
|
want: parameters.ParameterManifest{Name: "foo-float", Type: "float", Required: false, Description: "bar", AuthServices: []string{}},
|
|
},
|
|
{
|
|
name: "boolean not required",
|
|
in: parameters.NewBooleanParameter("foo-bool", "bar", parameters.WithBooleanRequired(false)),
|
|
want: parameters.ParameterManifest{Name: "foo-bool", Type: "boolean", Required: false, Description: "bar", AuthServices: []string{}},
|
|
},
|
|
{
|
|
name: "array not required",
|
|
in: parameters.NewArrayParameter("foo-array", "bar", parameters.NewStringParameter("foo-string", "bar"), parameters.WithArrayRequired(false)),
|
|
want: parameters.ParameterManifest{
|
|
Name: "foo-array",
|
|
Type: "array",
|
|
Required: false,
|
|
Description: "bar",
|
|
AuthServices: []string{},
|
|
Items: ¶meters.ParameterManifest{Name: "foo-string", Type: "string", Required: false, Description: "bar", AuthServices: []string{}},
|
|
},
|
|
},
|
|
{
|
|
name: "map with string values",
|
|
in: parameters.NewMapParameter("foo-map", "bar", "string"),
|
|
want: parameters.ParameterManifest{
|
|
Name: "foo-map",
|
|
Type: "object",
|
|
Required: true,
|
|
Description: "bar",
|
|
AuthServices: []string{},
|
|
AdditionalProperties: map[string]any{"type": "string"},
|
|
},
|
|
},
|
|
{
|
|
name: "map not required",
|
|
in: parameters.NewMapParameter("foo-map", "bar", "string", parameters.WithMapRequired(false)),
|
|
want: parameters.ParameterManifest{
|
|
Name: "foo-map",
|
|
Type: "object",
|
|
Required: false,
|
|
Description: "bar",
|
|
AuthServices: []string{},
|
|
AdditionalProperties: map[string]any{"type": "string"},
|
|
},
|
|
},
|
|
{
|
|
name: "generic map (additionalProperties true)",
|
|
in: parameters.NewMapParameter("foo-map", "bar", ""),
|
|
want: parameters.ParameterManifest{
|
|
Name: "foo-map",
|
|
Type: "object",
|
|
Required: true,
|
|
Description: "bar",
|
|
AuthServices: []string{},
|
|
AdditionalProperties: true,
|
|
},
|
|
},
|
|
}
|
|
for _, tc := range tcs {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
got := tc.in.Manifest()
|
|
if diff := cmp.Diff(tc.want, got); diff != "" {
|
|
t.Fatalf("unexpected manifest (-want +got):\n%s", diff)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParamMcpManifest(t *testing.T) {
|
|
tcs := []struct {
|
|
name string
|
|
in parameters.Parameter
|
|
want parameters.ParameterMcpManifest
|
|
wantAuthParam []string
|
|
}{
|
|
{
|
|
name: "string",
|
|
in: parameters.NewStringParameter("foo-string", "bar"),
|
|
want: parameters.ParameterMcpManifest{Type: "string", Description: "bar"},
|
|
wantAuthParam: []string{},
|
|
},
|
|
{
|
|
name: "int",
|
|
in: parameters.NewIntParameter("foo-int", "bar"),
|
|
want: parameters.ParameterMcpManifest{Type: "integer", Description: "bar"},
|
|
wantAuthParam: []string{},
|
|
},
|
|
{
|
|
name: "float",
|
|
in: parameters.NewFloatParameter("foo-float", "bar"),
|
|
want: parameters.ParameterMcpManifest{Type: "number", Description: "bar"},
|
|
wantAuthParam: []string{},
|
|
},
|
|
{
|
|
name: "boolean",
|
|
in: parameters.NewBooleanParameter("foo-bool", "bar"),
|
|
want: parameters.ParameterMcpManifest{Type: "boolean", Description: "bar"},
|
|
wantAuthParam: []string{},
|
|
},
|
|
{
|
|
name: "array",
|
|
in: parameters.NewArrayParameter("foo-array", "bar", parameters.NewStringParameter("foo-string", "bar")),
|
|
want: parameters.ParameterMcpManifest{
|
|
Type: "array",
|
|
Description: "bar",
|
|
Items: ¶meters.ParameterMcpManifest{Type: "string", Description: "bar"},
|
|
},
|
|
wantAuthParam: []string{},
|
|
},
|
|
|
|
{
|
|
name: "map with string values",
|
|
in: parameters.NewMapParameter("foo-map", "bar", "string"),
|
|
want: parameters.ParameterMcpManifest{
|
|
Type: "object",
|
|
Description: "bar",
|
|
AdditionalProperties: map[string]any{"type": "string"},
|
|
},
|
|
wantAuthParam: []string{},
|
|
},
|
|
{
|
|
name: "generic map (additionalProperties true)",
|
|
in: parameters.NewMapParameter("foo-map", "bar", ""),
|
|
want: parameters.ParameterMcpManifest{
|
|
Type: "object",
|
|
Description: "bar",
|
|
AdditionalProperties: true,
|
|
},
|
|
wantAuthParam: []string{},
|
|
},
|
|
}
|
|
for _, tc := range tcs {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
got, gotAuthParam := tc.in.McpManifest()
|
|
if diff := cmp.Diff(tc.want, got); diff != "" {
|
|
t.Fatalf("unexpected manifest (-want +got):\n%s", diff)
|
|
}
|
|
slices.Sort(gotAuthParam)
|
|
if !reflect.DeepEqual(gotAuthParam, tc.wantAuthParam) {
|
|
t.Fatalf("unexpected auth param list: got %s, want %s", gotAuthParam, tc.wantAuthParam)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFailParametersUnmarshal(t *testing.T) {
|
|
ctx, err := testutils.ContextWithNewLogger()
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %s", err)
|
|
}
|
|
tcs := []struct {
|
|
name string
|
|
in []map[string]any
|
|
err string
|
|
}{
|
|
{
|
|
name: "common parameter missing name",
|
|
in: []map[string]any{
|
|
{
|
|
"type": "string",
|
|
"description": "this is a param for string",
|
|
},
|
|
},
|
|
err: "unable to parse as \"string\": Key: 'CommonParameter.Name' Error:Field validation for 'Name' failed on the 'required' tag",
|
|
},
|
|
{
|
|
name: "common parameter missing type",
|
|
in: []map[string]any{
|
|
{
|
|
"name": "string",
|
|
"description": "this is a param for string",
|
|
},
|
|
},
|
|
err: "parameter is missing 'type' field",
|
|
},
|
|
{
|
|
name: "common parameter with non-string type",
|
|
in: []map[string]any{
|
|
{
|
|
"name": "my_string",
|
|
"type": 123,
|
|
"description": "this is a param with a numeric type",
|
|
},
|
|
},
|
|
err: "parameter 'type' field must be a string",
|
|
},
|
|
{
|
|
name: "common parameter missing description",
|
|
in: []map[string]any{
|
|
{
|
|
"name": "my_string",
|
|
"type": "string",
|
|
},
|
|
},
|
|
err: "unable to parse as \"string\": Key: 'CommonParameter.Desc' Error:Field validation for 'Desc' failed on the 'required' tag",
|
|
},
|
|
{
|
|
name: "array parameter missing items",
|
|
in: []map[string]any{
|
|
{
|
|
"name": "my_array",
|
|
"type": "array",
|
|
"description": "this param is an array of strings",
|
|
},
|
|
},
|
|
err: "unable to parse as \"array\": unable to parse 'items' field: error parsing parameters: nothing to unmarshal",
|
|
},
|
|
{
|
|
name: "array parameter missing items' name",
|
|
in: []map[string]any{
|
|
{
|
|
"name": "my_array",
|
|
"type": "array",
|
|
"description": "this param is an array of strings",
|
|
"items": map[string]string{
|
|
"type": "string",
|
|
"description": "string item",
|
|
},
|
|
},
|
|
},
|
|
err: "unable to parse as \"array\": unable to parse 'items' field: unable to parse as \"string\": Key: 'CommonParameter.Name' Error:Field validation for 'Name' failed on the 'required' tag",
|
|
},
|
|
// --- MODIFIED MAP PARAMETER TEST ---
|
|
{
|
|
name: "map with invalid valueType",
|
|
in: []map[string]any{
|
|
{
|
|
"name": "my_map",
|
|
"type": "map",
|
|
"description": "this param is a map",
|
|
"valueType": "not-a-real-type",
|
|
},
|
|
},
|
|
err: "unsupported valueType \"not-a-real-type\" for map parameter",
|
|
},
|
|
}
|
|
for _, tc := range tcs {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
var got parameters.Parameters
|
|
// parse map to bytes
|
|
data, err := yaml.Marshal(tc.in)
|
|
if err != nil {
|
|
t.Fatalf("unable to marshal input to yaml: %s", err)
|
|
}
|
|
// parse bytes to object
|
|
err = yaml.UnmarshalContext(ctx, data, &got)
|
|
if err == nil {
|
|
t.Fatalf("expect parsing to fail")
|
|
}
|
|
errStr := err.Error()
|
|
|
|
if !strings.Contains(errStr, tc.err) {
|
|
t.Fatalf("unexpected error: got %q, want to contain %q", errStr, tc.err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// ... (Remaining test functions do not involve parameter definitions and need no changes)
|
|
|
|
func TestConvertArrayParamToString(t *testing.T) {
|
|
|
|
tcs := []struct {
|
|
name string
|
|
in []any
|
|
want string
|
|
}{
|
|
{
|
|
in: []any{
|
|
"id",
|
|
"name",
|
|
"location",
|
|
},
|
|
want: "id, name, location",
|
|
},
|
|
{
|
|
in: []any{
|
|
"id",
|
|
},
|
|
want: "id",
|
|
},
|
|
{
|
|
in: []any{
|
|
"id",
|
|
"5",
|
|
"false",
|
|
},
|
|
want: "id, 5, false",
|
|
},
|
|
{
|
|
in: []any{},
|
|
want: "",
|
|
},
|
|
{
|
|
in: []any{},
|
|
want: "",
|
|
},
|
|
}
|
|
for _, tc := range tcs {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
got, _ := parameters.ConvertArrayParamToString(tc.in)
|
|
if diff := cmp.Diff(tc.want, got); diff != "" {
|
|
t.Fatalf("incorrect array param conversion: diff %v", diff)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFailConvertArrayParamToString(t *testing.T) {
|
|
tcs := []struct {
|
|
name string
|
|
in []any
|
|
err string
|
|
}{
|
|
{
|
|
in: []any{5, 10, 15},
|
|
err: "templateParameter only supports string arrays",
|
|
},
|
|
{
|
|
in: []any{"id", "name", 15},
|
|
err: "templateParameter only supports string arrays",
|
|
},
|
|
{
|
|
in: []any{false},
|
|
err: "templateParameter only supports string arrays",
|
|
},
|
|
{
|
|
in: []any{10, true},
|
|
err: "templateParameter only supports string arrays",
|
|
},
|
|
}
|
|
for _, tc := range tcs {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
_, err := parameters.ConvertArrayParamToString(tc.in)
|
|
errStr := err.Error()
|
|
if errStr != tc.err {
|
|
t.Fatalf("unexpected error: got %q, want %q", errStr, tc.err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetParams(t *testing.T) {
|
|
tcs := []struct {
|
|
name string
|
|
in map[string]any
|
|
params parameters.Parameters
|
|
want parameters.ParamValues
|
|
}{
|
|
{
|
|
name: "parameters to include and exclude",
|
|
params: parameters.Parameters{
|
|
parameters.NewStringParameter("my_string_inc", "this should be included"),
|
|
parameters.NewStringParameter("my_string_inc2", "this should be included"),
|
|
},
|
|
in: map[string]any{
|
|
"my_string_inc": "hello world A",
|
|
"my_string_inc2": "hello world B",
|
|
"my_string_exc": "hello world C",
|
|
},
|
|
want: parameters.ParamValues{
|
|
parameters.ParamValue{Name: "my_string_inc", Value: "hello world A"},
|
|
parameters.ParamValue{Name: "my_string_inc2", Value: "hello world B"},
|
|
},
|
|
},
|
|
{
|
|
name: "include all",
|
|
params: parameters.Parameters{
|
|
parameters.NewStringParameter("my_string_inc", "this should be included"),
|
|
},
|
|
in: map[string]any{
|
|
"my_string_inc": "hello world A",
|
|
},
|
|
want: parameters.ParamValues{
|
|
parameters.ParamValue{Name: "my_string_inc", Value: "hello world A"},
|
|
},
|
|
},
|
|
{
|
|
name: "exclude all",
|
|
params: parameters.Parameters{},
|
|
in: map[string]any{
|
|
"my_string_exc": "hello world A",
|
|
"my_string_exc2": "hello world B",
|
|
},
|
|
want: parameters.ParamValues{},
|
|
},
|
|
{
|
|
name: "empty",
|
|
params: parameters.Parameters{},
|
|
in: map[string]any{},
|
|
want: parameters.ParamValues{},
|
|
},
|
|
}
|
|
for _, tc := range tcs {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
got, _ := parameters.GetParams(tc.params, tc.in)
|
|
if diff := cmp.Diff(tc.want, got); diff != "" {
|
|
t.Fatalf("incorrect get params: diff %v", diff)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFailGetParams(t *testing.T) {
|
|
|
|
tcs := []struct {
|
|
name string
|
|
params parameters.Parameters
|
|
in map[string]any
|
|
err string
|
|
}{
|
|
{
|
|
name: "missing the only parameter",
|
|
params: parameters.Parameters{parameters.NewStringParameter("my_string", "this was missing")},
|
|
in: map[string]any{},
|
|
err: "missing parameter my_string",
|
|
},
|
|
{
|
|
name: "missing one parameter of multiple",
|
|
params: parameters.Parameters{
|
|
parameters.NewStringParameter("my_string_inc", "this should be included"),
|
|
parameters.NewStringParameter("my_string_exc", "this was missing"),
|
|
},
|
|
in: map[string]any{
|
|
"my_string_inc": "hello world A",
|
|
},
|
|
err: "missing parameter my_string_exc",
|
|
},
|
|
}
|
|
for _, tc := range tcs {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
_, err := parameters.GetParams(tc.params, tc.in)
|
|
errStr := err.Error()
|
|
if errStr != tc.err {
|
|
t.Fatalf("unexpected error: got %q, want %q", errStr, tc.err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestResolveTemplateParameters(t *testing.T) {
|
|
tcs := []struct {
|
|
name string
|
|
templateParams parameters.Parameters
|
|
statement string
|
|
in map[string]any
|
|
want string
|
|
}{
|
|
{
|
|
name: "single template parameter",
|
|
templateParams: parameters.Parameters{
|
|
parameters.NewStringParameter("tableName", "this is a string template parameter"),
|
|
},
|
|
statement: "SELECT * FROM {{.tableName}}",
|
|
in: map[string]any{
|
|
"tableName": "hotels",
|
|
},
|
|
want: "SELECT * FROM hotels",
|
|
},
|
|
{
|
|
name: "multiple template parameters",
|
|
templateParams: parameters.Parameters{
|
|
parameters.NewStringParameter("tableName", "this is a string template parameter"),
|
|
parameters.NewStringParameter("columnName", "this is a string template parameter"),
|
|
},
|
|
statement: "SELECT * FROM {{.tableName}} WHERE {{.columnName}} = 'Hilton'",
|
|
in: map[string]any{
|
|
"tableName": "hotels",
|
|
"columnName": "name",
|
|
},
|
|
want: "SELECT * FROM hotels WHERE name = 'Hilton'",
|
|
},
|
|
{
|
|
name: "standard and template parameter",
|
|
templateParams: parameters.Parameters{
|
|
parameters.NewStringParameter("tableName", "this is a string template parameter"),
|
|
parameters.NewStringParameter("hotelName", "this is a string parameter"),
|
|
},
|
|
statement: "SELECT * FROM {{.tableName}} WHERE name = $1",
|
|
in: map[string]any{
|
|
"tableName": "hotels",
|
|
"hotelName": "name",
|
|
},
|
|
want: "SELECT * FROM hotels WHERE name = $1",
|
|
},
|
|
{
|
|
name: "standard parameter",
|
|
templateParams: parameters.Parameters{
|
|
parameters.NewStringParameter("hotelName", "this is a string parameter"),
|
|
},
|
|
statement: "SELECT * FROM hotels WHERE name = $1",
|
|
in: map[string]any{
|
|
"hotelName": "hotels",
|
|
},
|
|
want: "SELECT * FROM hotels WHERE name = $1",
|
|
},
|
|
}
|
|
for _, tc := range tcs {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
got, _ := parameters.ResolveTemplateParams(tc.templateParams, tc.statement, tc.in)
|
|
if diff := cmp.Diff(tc.want, got); diff != "" {
|
|
t.Fatalf("incorrect resolved template params: diff %v", diff)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFailResolveTemplateParameters(t *testing.T) {
|
|
tcs := []struct {
|
|
name string
|
|
templateParams parameters.Parameters
|
|
statement string
|
|
in map[string]any
|
|
err string
|
|
}{
|
|
{
|
|
name: "wrong param name",
|
|
templateParams: parameters.Parameters{
|
|
parameters.NewStringParameter("tableName", "this is a string template parameter"),
|
|
},
|
|
statement: "SELECT * FROM {{.missingParam}}",
|
|
in: map[string]any{},
|
|
err: "error getting template params missing parameter tableName",
|
|
},
|
|
{
|
|
name: "incomplete param template",
|
|
templateParams: parameters.Parameters{
|
|
parameters.NewStringParameter("tableName", "this is a string template parameter"),
|
|
},
|
|
statement: "SELECT * FROM {{.tableName",
|
|
in: map[string]any{
|
|
"tableName": "hotels",
|
|
},
|
|
err: "error creating go template template: statement:1: unclosed action",
|
|
},
|
|
{
|
|
name: "undefined function",
|
|
templateParams: parameters.Parameters{
|
|
parameters.NewStringParameter("tableName", "this is a string template parameter"),
|
|
},
|
|
statement: "SELECT * FROM {{json .tableName}}",
|
|
in: map[string]any{
|
|
"tableName": "hotels",
|
|
},
|
|
err: "error creating go template template: statement:1: function \"json\" not defined",
|
|
},
|
|
{
|
|
name: "undefined method",
|
|
templateParams: parameters.Parameters{
|
|
parameters.NewStringParameter("tableName", "this is a string template parameter"),
|
|
},
|
|
statement: "SELECT * FROM {{.tableName .wrong}}",
|
|
in: map[string]any{
|
|
"tableName": "hotels",
|
|
},
|
|
err: "error executing go template template: statement:1:16: executing \"statement\" at <.tableName>: tableName is not a method but has arguments",
|
|
},
|
|
}
|
|
for _, tc := range tcs {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
_, err := parameters.ResolveTemplateParams(tc.templateParams, tc.statement, tc.in)
|
|
errStr := err.Error()
|
|
if errStr != tc.err {
|
|
t.Fatalf("unexpected error: got %q, want %q", errStr, tc.err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCheckParamRequired(t *testing.T) {
|
|
tcs := []struct {
|
|
name string
|
|
required bool
|
|
defaultV any
|
|
want bool
|
|
}{
|
|
{
|
|
name: "required and no default",
|
|
required: true,
|
|
defaultV: nil,
|
|
want: true,
|
|
},
|
|
{
|
|
name: "required and default",
|
|
required: true,
|
|
defaultV: "foo",
|
|
want: false,
|
|
},
|
|
{
|
|
name: "not required and no default",
|
|
required: false,
|
|
defaultV: nil,
|
|
want: false,
|
|
},
|
|
{
|
|
name: "not required and default",
|
|
required: false,
|
|
defaultV: "foo",
|
|
want: false,
|
|
},
|
|
}
|
|
for _, tc := range tcs {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
got := parameters.CheckParamRequired(tc.required, tc.defaultV)
|
|
if got != tc.want {
|
|
t.Fatalf("got %v, want %v", got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|