110 lines
3.4 KiB
Go
110 lines
3.4 KiB
Go
// Copyright 2026 Alibaba Group
|
|
// 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 app
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"reflect"
|
|
"testing"
|
|
|
|
apperrors "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/errors"
|
|
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/executor"
|
|
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/transport"
|
|
)
|
|
|
|
func TestPluginStdioExecutionNormalizesAndValidatesLiveSchema(t *testing.T) {
|
|
isolatePluginRuntime(t)
|
|
previousInit := runnerStdioEnsureInitialized
|
|
previousList := runnerStdioListTools
|
|
previousCall := runnerStdioCallTool
|
|
t.Cleanup(func() {
|
|
runnerStdioEnsureInitialized = previousInit
|
|
runnerStdioListTools = previousList
|
|
runnerStdioCallTool = previousCall
|
|
})
|
|
|
|
client := transport.NewStdioClient("unused", nil, nil)
|
|
RegisterStdioClient("conference/local", client)
|
|
runnerStdioEnsureInitialized = func(*transport.StdioClient, context.Context) error {
|
|
return nil
|
|
}
|
|
runnerStdioListTools = func(*transport.StdioClient, context.Context) (transport.ToolsListResult, error) {
|
|
return transport.ToolsListResult{
|
|
Tools: []transport.ToolDescriptor{{
|
|
Name: "create_conference",
|
|
InputSchema: map[string]any{
|
|
"type": "object",
|
|
"required": []any{"title"},
|
|
"properties": map[string]any{
|
|
"title": map[string]any{"type": "string"},
|
|
"capture_speaker": map[string]any{"type": "bool"},
|
|
},
|
|
"additionalProperties": false,
|
|
},
|
|
}},
|
|
}, nil
|
|
}
|
|
var calledParams map[string]any
|
|
runnerStdioCallTool = func(
|
|
_ *transport.StdioClient,
|
|
_ context.Context,
|
|
_ string,
|
|
params map[string]any,
|
|
) (transport.ToolCallResult, error) {
|
|
calledParams = params
|
|
return transport.ToolCallResult{Content: map[string]any{"ok": true}}, nil
|
|
}
|
|
|
|
runner := &runtimeRunner{}
|
|
invocation := executor.Invocation{
|
|
CanonicalProduct: "conference-local",
|
|
Tool: "create_conference",
|
|
Params: map[string]any{
|
|
"title": "schema validation",
|
|
"capture_speaker": "true",
|
|
},
|
|
}
|
|
result, err := runner.executeInvocation(
|
|
context.Background(),
|
|
"stdio://conference/local",
|
|
invocation,
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("stdio plugin execution: %v", err)
|
|
}
|
|
wantParams := map[string]any{
|
|
"title": "schema validation",
|
|
"capture_speaker": true,
|
|
}
|
|
if !reflect.DeepEqual(calledParams, wantParams) ||
|
|
!reflect.DeepEqual(result.Invocation.Params, wantParams) {
|
|
t.Fatalf("normalized wire params = %#v, result = %#v", calledParams, result.Invocation.Params)
|
|
}
|
|
|
|
calledParams = nil
|
|
invocation.Params = map[string]any{"capture_speaker": "true"}
|
|
_, err = runner.executeInvocation(
|
|
context.Background(),
|
|
"stdio://conference/local",
|
|
invocation,
|
|
)
|
|
var appError *apperrors.Error
|
|
if !errors.As(err, &appError) ||
|
|
appError.Category != apperrors.CategoryValidation ||
|
|
calledParams != nil {
|
|
t.Fatalf("missing required schema validation = %#v, call params = %#v", err, calledParams)
|
|
}
|
|
}
|