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

231 lines
7.5 KiB
Go

package workbenchapps
import (
"context"
"encoding/json"
"errors"
"strings"
"testing"
workbenchbiz "github.com/tutti-os/tutti/services/tuttid/biz/workbench"
workspacebiz "github.com/tutti-os/tutti/services/tuttid/biz/workspace"
cliservice "github.com/tutti-os/tutti/services/tuttid/service/cli"
)
func TestOpenPublishesWorkspaceAppWorkbenchLaunch(t *testing.T) {
workspaces := fakeWorkspaceCatalog{startupID: "ws-1"}
apps := &fakeAppLauncher{
app: workspacebiz.WorkspaceApp{
Package: workspacebiz.AppPackage{AppID: "docs"},
Runtime: workspacebiz.AppRuntimeState{
Status: workspacebiz.AppRuntimeStatusRunning,
},
},
}
publisher := &fakeWorkbenchLaunchPublisher{}
provider := NewProvider(workspaces, apps, publisher)
output, err := provider.newOpenCommand().Handler(t.Context(), cliservice.InvokeRequest{
Context: cliservice.InvokeContext{Source: "cli"},
Input: map[string]any{
"app-id": "docs",
"param": []any{"path=/tmp/a", "mode=preview"},
"route": "/files",
"state-json": `{"selected":true}`,
},
})
if err != nil {
t.Fatalf("run open: %v", err)
}
if output.Rows[0]["appId"] != "docs" || output.Rows[0]["launchRequested"] != true {
t.Fatalf("output = %#v", output)
}
if apps.workspaceID != "ws-1" || apps.appID != "docs" {
t.Fatalf("launch input = %q %q", apps.workspaceID, apps.appID)
}
if len(publisher.requests) != 1 {
t.Fatalf("published requests = %#v", publisher.requests)
}
request := publisher.requests[0]
if request.WorkspaceID != "ws-1" || request.TypeID != workspaceAppTypeID || request.Source != "cli" {
t.Fatalf("request = %#v", request)
}
var payload struct {
AppID string `json:"appId"`
Intent struct {
Kind string `json:"kind"`
Params map[string]string `json:"params"`
Route string `json:"route"`
State map[string]bool `json:"state"`
} `json:"intent"`
}
if err := json.Unmarshal(request.Payload, &payload); err != nil {
t.Fatalf("unmarshal payload: %v", err)
}
if payload.AppID != "docs" ||
payload.Intent.Kind != "open-route" ||
payload.Intent.Route != "/files" ||
payload.Intent.Params["path"] != "/tmp/a" ||
payload.Intent.Params["mode"] != "preview" ||
!payload.Intent.State["selected"] {
t.Fatalf("payload = %#v", payload)
}
}
func TestOpenCommandAdvertisesRepeatableParamSchema(t *testing.T) {
command := NewProvider(fakeWorkspaceCatalog{}, &fakeAppLauncher{}, nil).newOpenCommand()
if command.Capability.Visibility != cliservice.CapabilityVisibilityPublic {
t.Fatalf("visibility = %q, want public", command.Capability.Visibility)
}
properties := command.Capability.InputSchema["properties"].(map[string]any)
param := properties["param"].(map[string]any)
oneOf := param["oneOf"].([]map[string]any)
if len(oneOf) != 2 || oneOf[0]["type"] != "string" || oneOf[1]["type"] != "array" {
t.Fatalf("param schema = %#v", param)
}
items := oneOf[1]["items"].(map[string]any)
if items["type"] != "string" {
t.Fatalf("param array items schema = %#v", items)
}
route := properties["route"].(map[string]any)
if !strings.Contains(route["description"].(string), "Origin-root") {
t.Fatalf("route schema = %#v", route)
}
}
func TestOpenPublishesAppWithoutRouteIntent(t *testing.T) {
provider := NewProvider(
fakeWorkspaceCatalog{startupID: "ws-1"},
&fakeAppLauncher{
app: workspacebiz.WorkspaceApp{
Package: workspacebiz.AppPackage{AppID: "docs"},
Runtime: workspacebiz.AppRuntimeState{
Status: workspacebiz.AppRuntimeStatusRunning,
},
},
},
&fakeWorkbenchLaunchPublisher{},
)
output, err := provider.newOpenCommand().Handler(t.Context(), cliservice.InvokeRequest{
Context: cliservice.InvokeContext{WorkspaceID: "ws-1"},
Input: map[string]any{"app-id": "docs"},
})
if err != nil {
t.Fatalf("run open: %v", err)
}
if output.Rows[0]["launchRequested"] != true {
t.Fatalf("output = %#v", output)
}
}
func TestOpenPublishesSpecialAppWorkbenchLaunch(t *testing.T) {
tests := []struct {
appID string
typeID string
dockEntryID string
payload string
}{
{appID: "agent-codex", typeID: agentGUITypeID, dockEntryID: agentGUITypeID, payload: `{"provider":"codex"}`},
{appID: "agent-claude-code", typeID: agentGUITypeID, dockEntryID: "agent-gui:claude-code", payload: `{"provider":"claude-code"}`},
{appID: "agent-tutti-agent", typeID: agentGUITypeID, dockEntryID: "agent-gui:tutti-agent", payload: `{"provider":"tutti-agent"}`},
{appID: issueManagerAppID, typeID: issueManagerTypeID, dockEntryID: issueManagerTypeID},
}
for _, test := range tests {
apps := &fakeAppLauncher{}
publisher := &fakeWorkbenchLaunchPublisher{}
provider := NewProvider(fakeWorkspaceCatalog{startupID: "ws-1"}, apps, publisher)
output, err := provider.newOpenCommand().Handler(t.Context(), cliservice.InvokeRequest{
Context: cliservice.InvokeContext{Source: "cli"},
Input: map[string]any{"app-id": test.appID},
})
if err != nil {
t.Fatalf("%s run open: %v", test.appID, err)
}
if apps.appID != "" {
t.Fatalf("%s launched app center app %q", test.appID, apps.appID)
}
if output.Rows[0]["appId"] != test.appID ||
output.Rows[0]["status"] != "launch_requested" ||
output.Rows[0]["launchRequested"] != true {
t.Fatalf("%s output = %#v", test.appID, output)
}
if len(publisher.requests) != 1 {
t.Fatalf("%s published requests = %#v", test.appID, publisher.requests)
}
request := publisher.requests[0]
if request.WorkspaceID != "ws-1" ||
request.TypeID != test.typeID ||
request.DockEntryID != test.dockEntryID ||
request.Source != "cli" ||
request.LaunchSource != "cli" {
t.Fatalf("%s request = %#v", test.appID, request)
}
if strings.TrimSpace(string(request.Payload)) != test.payload {
t.Fatalf("%s payload = %q, want %q", test.appID, string(request.Payload), test.payload)
}
}
}
func TestOpenRejectsInvalidRouteInputs(t *testing.T) {
provider := NewProvider(
fakeWorkspaceCatalog{startupID: "ws-1"},
&fakeAppLauncher{},
&fakeWorkbenchLaunchPublisher{},
)
tests := []map[string]any{
{"app-id": "docs", "route": "https://example.com/path"},
{"app-id": "docs", "route": "//example.com/path"},
{"app-id": "docs", "route": "files"},
{"app-id": "docs", "route": "/files", "state-json": "[]"},
{"app-id": "docs", "param": "path=/tmp/a"},
{"app-id": issueManagerAppID, "route": "/issues"},
}
for _, input := range tests {
_, err := provider.newOpenCommand().Handler(t.Context(), cliservice.InvokeRequest{
Input: input,
})
if !errors.Is(err, cliservice.ErrInvalidInput) {
t.Fatalf("input = %#v err = %v, want ErrInvalidInput", input, err)
}
}
}
type fakeWorkspaceCatalog struct {
startupID string
}
func (f fakeWorkspaceCatalog) Startup(context.Context) (*workspacebiz.Summary, error) {
return &workspacebiz.Summary{ID: f.startupID}, nil
}
func (fakeWorkspaceCatalog) Get(_ context.Context, id string) (workspacebiz.Summary, error) {
return workspacebiz.Summary{ID: strings.TrimSpace(id)}, nil
}
type fakeAppLauncher struct {
app workspacebiz.WorkspaceApp
appID string
workspaceID string
}
func (f *fakeAppLauncher) Launch(_ context.Context, workspaceID string, appID string) (workspacebiz.WorkspaceApp, error) {
f.workspaceID = workspaceID
f.appID = appID
if f.app.Package.AppID == "" {
f.app.Package.AppID = appID
}
return f.app, nil
}
type fakeWorkbenchLaunchPublisher struct {
requests []workbenchbiz.NodeLaunchRequest
}
func (f *fakeWorkbenchLaunchPublisher) PublishWorkbenchNodeLaunchRequested(_ context.Context, request workbenchbiz.NodeLaunchRequest) error {
f.requests = append(f.requests, request)
return nil
}