Files
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

293 lines
9.4 KiB
Go

package computer
import (
"context"
"errors"
"reflect"
"testing"
cliservice "github.com/tutti-os/tutti/services/tuttid/service/cli"
computersvc "github.com/tutti-os/tutti/services/tuttid/service/computer"
)
type recordedToolCall struct {
workspaceID string
tool string
args map[string]any
}
type fakeComputerService struct {
calls []recordedToolCall
nativeCalls []recordedToolCall
result computersvc.ToolResult
catalog computersvc.ToolCatalog
}
func (f *fakeComputerService) CallTool(_ context.Context, workspaceID, _ string, tool string, args map[string]any) (computersvc.ToolResult, error) {
f.calls = append(f.calls, recordedToolCall{workspaceID: workspaceID, tool: tool, args: args})
if f.result.Text == "" {
f.result.Text = "ok"
}
return f.result, nil
}
func (f *fakeComputerService) CallNativeTool(_ context.Context, workspaceID, _ string, tool string, args map[string]any) (computersvc.ToolResult, error) {
f.nativeCalls = append(f.nativeCalls, recordedToolCall{workspaceID: workspaceID, tool: tool, args: args})
if f.result.Text == "" {
f.result.Text = "ok"
}
return f.result, nil
}
func (f *fakeComputerService) ListTools(context.Context, string, string) (computersvc.ToolCatalog, error) {
return f.catalog, nil
}
func TestStableComputerCommandSchemasExposeWindowTargetsWithoutSyntheticScope(t *testing.T) {
provider := NewProvider(nil, &fakeComputerService{})
screenshot := commandByID(t, provider.Commands(), "computer.screenshot")
assertNoSchemaProperty(t, screenshot, "scope")
assertSchemaProperty(t, screenshot, "pid", "integer")
assertSchemaProperty(t, screenshot, "window-id", "integer")
for _, commandID := range []string{"computer.click", "computer.double-click", "computer.right-click", "computer.scroll"} {
command := commandByID(t, provider.Commands(), commandID)
assertSchemaProperty(t, command, "x", "integer")
assertSchemaProperty(t, command, "y", "integer")
assertNoSchemaProperty(t, command, "scope")
assertSchemaProperty(t, command, "pid", "integer")
assertSchemaProperty(t, command, "window-id", "integer")
}
scroll := commandByID(t, provider.Commands(), "computer.scroll")
assertSchemaProperty(t, scroll, "amount", "integer")
}
func TestStableScreenshotRoutesOnlyWindowTargets(t *testing.T) {
tests := []struct {
name string
input map[string]any
want map[string]any
}{
{
name: "explicit window",
input: map[string]any{"pid": "42", "window-id": "99"},
want: map[string]any{"pid": 42, "window_id": 99},
},
{
name: "compatible automatic window",
input: map[string]any{},
want: map[string]any{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
computer := &fakeComputerService{}
command := commandByID(t, NewProvider(nil, computer).Commands(), "computer.screenshot")
invokeCommand(t, command, tt.input)
assertSingleToolCall(t, computer, "screenshot", tt.want)
})
}
}
func TestPointerCommandsPassNativeNumericArguments(t *testing.T) {
tests := []struct {
name string
commandID string
tool string
input map[string]any
want map[string]any
}{
{
name: "explicit window click",
commandID: "computer.click",
tool: "click",
input: map[string]any{"pid": "42", "window-id": "99", "x": "120", "y": "240"},
want: map[string]any{"pid": 42, "window_id": 99, "x": 120, "y": 240},
},
{
name: "explicit window double click",
commandID: "computer.double-click",
tool: "click",
input: map[string]any{"pid": "42", "window-id": "99", "x": "120", "y": "240"},
want: map[string]any{"pid": 42, "window_id": 99, "x": 120, "y": 240, "count": 2},
},
{
name: "explicit window right click",
commandID: "computer.right-click",
tool: "click",
input: map[string]any{"pid": "42", "window-id": "99", "x": "120", "y": "240"},
want: map[string]any{"pid": 42, "window_id": 99, "x": 120, "y": 240, "button": "right"},
},
{
name: "move cursor",
commandID: "computer.move-cursor",
tool: "move_cursor",
input: map[string]any{"x": "120", "y": "240"},
want: map[string]any{"x": 120, "y": 240},
},
{
name: "window scroll",
commandID: "computer.scroll",
tool: "scroll",
input: map[string]any{"pid": "42", "window-id": "99", "x": "120", "y": "240", "direction": "down", "amount": "4"},
want: map[string]any{"pid": 42, "window_id": 99, "x": 120, "y": 240, "direction": "down", "amount": 4},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
computer := &fakeComputerService{}
command := commandByID(t, NewProvider(nil, computer).Commands(), tt.commandID)
invokeCommand(t, command, tt.input)
assertSingleToolCall(t, computer, tt.tool, tt.want)
})
}
}
func TestKeyboardCommandsPassExplicitWindowTarget(t *testing.T) {
tests := []struct {
commandID string
input map[string]any
tool string
want map[string]any
}{
{
commandID: "computer.type",
input: map[string]any{"text": "hello", "pid": "42", "window-id": "99"},
tool: "type_text",
want: map[string]any{"text": "hello", "pid": 42, "window_id": 99},
},
{
commandID: "computer.press-key",
input: map[string]any{"key": "cmd+c", "pid": "42", "window-id": "99"},
tool: "press_key",
want: map[string]any{"key": "cmd+c", "pid": 42, "window_id": 99},
},
}
for _, tt := range tests {
t.Run(tt.commandID, func(t *testing.T) {
computer := &fakeComputerService{}
command := commandByID(t, NewProvider(nil, computer).Commands(), tt.commandID)
invokeCommand(t, command, tt.input)
assertSingleToolCall(t, computer, tt.tool, tt.want)
})
}
}
func TestComputerCommandsRejectPartialWindowTargets(t *testing.T) {
tests := []struct {
name string
commandID string
input map[string]any
}{
{
name: "partial window target",
commandID: "computer.screenshot",
input: map[string]any{"pid": "42"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
computer := &fakeComputerService{}
command := commandByID(t, NewProvider(nil, computer).Commands(), tt.commandID)
_, err := command.Handler(context.Background(), cliservice.InvokeRequest{
Input: tt.input,
OutputMode: cliservice.OutputModePlain,
Context: cliservice.InvokeContext{WorkspaceID: "workspace-1"},
})
if !errors.Is(err, cliservice.ErrInvalidInput) {
t.Fatalf("err = %v, want ErrInvalidInput", err)
}
if len(computer.calls) != 0 {
t.Fatalf("unexpected tool calls: %#v", computer.calls)
}
})
}
}
func TestComputerCommandsSupportJSONOutput(t *testing.T) {
computer := &fakeComputerService{result: computersvc.ToolResult{
Text: "Screenshot saved to /tmp/screenshot.png",
StructuredContent: map[string]any{"screenshot_file_path": "/tmp/screenshot.png"},
}}
commands := NewProvider(nil, computer).Commands()
for _, command := range commands {
if !command.Capability.Output.JSON {
t.Errorf("%s does not advertise JSON output", command.Capability.ID)
}
}
screenshot := commandByID(t, commands, "computer.screenshot")
output, err := screenshot.Handler(context.Background(), cliservice.InvokeRequest{
OutputMode: cliservice.OutputModeJSON,
Context: cliservice.InvokeContext{WorkspaceID: "workspace-1"},
})
if err != nil {
t.Fatalf("invoke screenshot JSON: %v", err)
}
want := map[string]any{
"text": "Screenshot saved to /tmp/screenshot.png",
"structuredContent": map[string]any{
"screenshot_file_path": "/tmp/screenshot.png",
},
}
if !reflect.DeepEqual(output.Value, want) {
t.Fatalf("JSON output = %#v, want %#v", output.Value, want)
}
}
func commandByID(t *testing.T, commands []cliservice.Command, id string) cliservice.Command {
t.Helper()
for _, command := range commands {
if command.Capability.ID == id {
return command
}
}
t.Fatalf("command %q not found", id)
return cliservice.Command{}
}
func invokeCommand(t *testing.T, command cliservice.Command, input map[string]any) {
t.Helper()
if _, err := command.Handler(context.Background(), cliservice.InvokeRequest{
Input: input,
OutputMode: cliservice.OutputModePlain,
Context: cliservice.InvokeContext{WorkspaceID: "workspace-1"},
}); err != nil {
t.Fatalf("invoke %s: %v", command.Capability.ID, err)
}
}
func assertSingleToolCall(t *testing.T, computer *fakeComputerService, tool string, args map[string]any) {
t.Helper()
if len(computer.calls) != 1 {
t.Fatalf("calls = %#v, want one", computer.calls)
}
call := computer.calls[0]
if call.workspaceID != "workspace-1" || call.tool != tool || !reflect.DeepEqual(call.args, args) {
t.Fatalf("call = %#v, want workspace-1 %s %#v", call, tool, args)
}
}
func assertSchemaProperty(t *testing.T, command cliservice.Command, name, propertyType string) {
t.Helper()
properties := command.Capability.InputSchema["properties"].(map[string]any)
property, ok := properties[name].(map[string]any)
if !ok || property["type"] != propertyType {
t.Fatalf("%s property %q = %#v, want type %q", command.Capability.ID, name, properties[name], propertyType)
}
}
func assertNoSchemaProperty(t *testing.T, command cliservice.Command, name string) {
t.Helper()
properties := command.Capability.InputSchema["properties"].(map[string]any)
if _, ok := properties[name]; ok {
t.Fatalf("%s unexpectedly exposes property %q: %#v", command.Capability.ID, name, properties[name])
}
}