coze-studio/backend/domain/plugin/service/exec_tool_test.go

253 lines
6.4 KiB
Go

/*
* Copyright 2025 coze-dev Authors
*
* 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 service
import (
"bytes"
"encoding/json"
"errors"
"testing"
"github.com/bytedance/mockey"
"github.com/bytedance/sonic"
"github.com/getkin/kin-openapi/openapi3"
"github.com/stretchr/testify/assert"
"github.com/coze-dev/coze-studio/backend/pkg/errorx"
model "github.com/coze-dev/coze-studio/backend/api/model/crossdomain/plugin"
)
func TestToolExecutorProcessWithInvalidRespProcessStrategyOfReturnDefault(t *testing.T) {
executor := &toolExecutor{
invalidRespProcessStrategy: model.InvalidResponseProcessStrategyOfReturnDefault,
}
paramVal := `
{
"a1": 1,
"b1": {
"a2": 2.1
},
"c1": [
{
"a2": 3.1
}
],
"d1": "hello",
"f1": true
}
`
decoder := sonic.ConfigDefault.NewDecoder(bytes.NewBufferString(paramVal))
decoder.UseNumber()
paramValMap := map[string]any{}
err := decoder.Decode(&paramValMap)
assert.NoError(t, err)
paramSchema := &openapi3.Schema{
Type: openapi3.TypeObject,
Properties: map[string]*openapi3.SchemaRef{
"a1": {
Value: &openapi3.Schema{
Type: openapi3.TypeInteger,
},
},
"b1": {
Value: &openapi3.Schema{
Type: openapi3.TypeObject,
Properties: map[string]*openapi3.SchemaRef{
"a2": {
Value: &openapi3.Schema{
Type: openapi3.TypeNumber,
},
},
},
},
},
"c1": {
Value: &openapi3.Schema{
Type: openapi3.TypeArray,
Items: &openapi3.SchemaRef{
Value: &openapi3.Schema{
Type: openapi3.TypeObject,
Properties: map[string]*openapi3.SchemaRef{
"a2": {
Value: &openapi3.Schema{
Type: openapi3.TypeNumber,
},
},
},
},
},
},
},
"d1": {
Value: &openapi3.Schema{
Type: openapi3.TypeString,
},
},
"f1": {
Value: &openapi3.Schema{
Type: openapi3.TypeBoolean,
},
},
},
}
processedParamValMap, err := executor.processWithInvalidRespProcessStrategyOfReturnDefault(nil, paramValMap, paramSchema)
assert.NoError(t, err)
assert.NotNil(t, processedParamValMap)
assert.Equal(t, int64(1), processedParamValMap["a1"])
assert.Equal(t, json.Number("2.1"), processedParamValMap["b1"].(map[string]any)["a2"])
assert.Equal(t, json.Number("3.1"), processedParamValMap["c1"].([]any)[0].(map[string]any)["a2"])
assert.Equal(t, "hello", processedParamValMap["d1"])
assert.Equal(t, true, processedParamValMap["f1"])
}
func TestToolExecutorProcessWithInvalidRespProcessStrategyOfReturnErr(t *testing.T) {
executor := &toolExecutor{
invalidRespProcessStrategy: model.InvalidResponseProcessStrategyOfReturnErr,
}
mockey.PatchConvey("integer", t, func() {
paramVal := `
{
"a": 1
}
`
decoder := sonic.ConfigDefault.NewDecoder(bytes.NewBufferString(paramVal))
decoder.UseNumber()
paramValMap := map[string]any{}
err := decoder.Decode(&paramValMap)
assert.NoError(t, err)
paramSchema := &openapi3.Schema{
Type: openapi3.TypeObject,
Properties: map[string]*openapi3.SchemaRef{
"a": {
Value: &openapi3.Schema{
Type: openapi3.TypeString,
},
},
},
}
_, err = executor.processWithInvalidRespProcessStrategyOfReturnErr(nil, paramValMap, paramSchema)
var customErr errorx.StatusError
assert.True(t, errors.As(err, &customErr))
assert.Equal(t, "execute tool failed : expected 'a' to be of type 'string', but got 'json.Number'", customErr.Msg())
paramSchema = &openapi3.Schema{
Type: openapi3.TypeObject,
Properties: map[string]*openapi3.SchemaRef{
"a1": {
Value: &openapi3.Schema{
Type: openapi3.TypeInteger,
},
},
},
}
_, err = executor.processWithInvalidRespProcessStrategyOfReturnErr(nil, paramValMap, paramSchema)
assert.NoError(t, err)
})
mockey.PatchConvey("string", t, func() {
paramVal := `
{
"a": "1"
}
`
decoder := sonic.ConfigDefault.NewDecoder(bytes.NewBufferString(paramVal))
decoder.UseNumber()
paramValMap := map[string]any{}
err := decoder.Decode(&paramValMap)
assert.NoError(t, err)
paramSchema := &openapi3.Schema{
Type: openapi3.TypeObject,
Properties: map[string]*openapi3.SchemaRef{
"a": {
Value: &openapi3.Schema{
Type: openapi3.TypeInteger,
},
},
},
}
_, err = executor.processWithInvalidRespProcessStrategyOfReturnErr(nil, paramValMap, paramSchema)
var customErr errorx.StatusError
assert.True(t, errors.As(err, &customErr))
assert.Equal(t, "execute tool failed : expected 'a' to be of type 'integer', but got 'string'", customErr.Msg())
paramSchema = &openapi3.Schema{
Type: openapi3.TypeObject,
Properties: map[string]*openapi3.SchemaRef{
"a": {
Value: &openapi3.Schema{
Type: openapi3.TypeString,
},
},
},
}
_, err = executor.processWithInvalidRespProcessStrategyOfReturnErr(nil, paramValMap, paramSchema)
assert.NoError(t, err)
})
mockey.PatchConvey("boolean", t, func() {
paramVal := `
{
"a": false
}
`
decoder := sonic.ConfigDefault.NewDecoder(bytes.NewBufferString(paramVal))
decoder.UseNumber()
paramValMap := map[string]any{}
err := decoder.Decode(&paramValMap)
assert.NoError(t, err)
paramSchema := &openapi3.Schema{
Type: openapi3.TypeObject,
Properties: map[string]*openapi3.SchemaRef{
"a": {
Value: &openapi3.Schema{
Type: openapi3.TypeString,
},
},
},
}
_, err = executor.processWithInvalidRespProcessStrategyOfReturnErr(nil, paramValMap, paramSchema)
var customErr errorx.StatusError
assert.True(t, errors.As(err, &customErr))
assert.Equal(t, "execute tool failed : expected 'a' to be of type 'string', but got 'bool'", customErr.Msg())
paramSchema = &openapi3.Schema{
Type: openapi3.TypeObject,
Properties: map[string]*openapi3.SchemaRef{
"a": {
Value: &openapi3.Schema{
Type: openapi3.TypeBoolean,
},
},
},
}
_, err = executor.processWithInvalidRespProcessStrategyOfReturnErr(nil, paramValMap, paramSchema)
assert.NoError(t, err)
})
}