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
171 lines
8.9 KiB
Go
171 lines
8.9 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net/http"
|
|
"testing"
|
|
|
|
tuttigenerated "github.com/tutti-os/tutti/services/tuttid/api/generated"
|
|
workspacedata "github.com/tutti-os/tutti/services/tuttid/data/workspace"
|
|
agentextensionservice "github.com/tutti-os/tutti/services/tuttid/service/agentextension"
|
|
)
|
|
|
|
type stubAgentTargetSetupService struct {
|
|
getFn func(context.Context, agentextensionservice.InstallPlanInput) (agentextensionservice.SetupSnapshot, error)
|
|
installFn func(context.Context, agentextensionservice.InstallInput) (agentextensionservice.SetupSnapshot, error)
|
|
authenticateFn func(context.Context, agentextensionservice.AuthenticateInput) (agentextensionservice.SetupSnapshot, error)
|
|
}
|
|
|
|
func (s stubAgentTargetSetupService) Authenticate(ctx context.Context, input agentextensionservice.AuthenticateInput) (agentextensionservice.SetupSnapshot, error) {
|
|
return s.authenticateFn(ctx, input)
|
|
}
|
|
|
|
func (s stubAgentTargetSetupService) GetSetup(ctx context.Context, input agentextensionservice.InstallPlanInput) (agentextensionservice.SetupSnapshot, error) {
|
|
return s.getFn(ctx, input)
|
|
}
|
|
|
|
func (s stubAgentTargetSetupService) Install(ctx context.Context, input agentextensionservice.InstallInput) (agentextensionservice.SetupSnapshot, error) {
|
|
return s.installFn(ctx, input)
|
|
}
|
|
|
|
func TestDaemonAPIGeneratedRoutesAgentTargetSetupInstallAndAuthenticate(t *testing.T) {
|
|
plan := agentextensionservice.InstallPlan{
|
|
AgentTargetID: "extension:codebuddy",
|
|
ExtensionInstallationID: "codebuddy@1.0.0", AgentKey: "codebuddy", ExtensionVersion: "1.0.0",
|
|
RuntimeKind: "standard-acp", Platform: "darwin-arm64", Runner: "npm",
|
|
PackageName: "@tencent-ai/codebuddy-code", PackageVersion: "2.121.2",
|
|
InstallRoot: "/state/agent/runtimes/codebuddy/1.0.0",
|
|
InstallCommand: []string{"npm", "install"}, Executable: "/state/agent/runtimes/codebuddy/1.0.0/codebuddy", LaunchArgs: []string{"--acp"},
|
|
PlanDigest: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
|
}
|
|
var capturedInstall agentextensionservice.InstallInput
|
|
var capturedAuthenticate agentextensionservice.AuthenticateInput
|
|
service := stubAgentTargetSetupService{
|
|
getFn: func(_ context.Context, input agentextensionservice.InstallPlanInput) (agentextensionservice.SetupSnapshot, error) {
|
|
return agentextensionservice.SetupSnapshot{
|
|
WorkspaceID: input.WorkspaceID, AgentTargetID: input.AgentTargetID,
|
|
Status: agentextensionservice.SetupNotInstalled, Plan: &plan,
|
|
AuthMethods: []agentextensionservice.RuntimeAuthMethod{
|
|
{ID: "external", Name: "Login with Google/GitHub"},
|
|
{
|
|
ID: "login", Name: "Login with Fixture account", Type: "terminal",
|
|
TerminalCommand: "/opt/fixture/bin/agent",
|
|
TerminalStartupAction: &agentextensionservice.RuntimeTerminalStartupAction{
|
|
Type: "slash_command", CommandName: "login", ReadyText: "Fixture Agent ready",
|
|
},
|
|
},
|
|
},
|
|
Account: &agentextensionservice.RuntimeAuthenticatedAccount{
|
|
ID: "user-1", DisplayName: "Rhinoc", AuthMethodID: "external", Organization: "Tutti",
|
|
},
|
|
}, nil
|
|
},
|
|
installFn: func(_ context.Context, input agentextensionservice.InstallInput) (agentextensionservice.SetupSnapshot, error) {
|
|
capturedInstall = input
|
|
return agentextensionservice.SetupSnapshot{
|
|
WorkspaceID: input.WorkspaceID, AgentTargetID: input.AgentTargetID,
|
|
Status: agentextensionservice.SetupInstalling,
|
|
Action: &agentextensionservice.SetupAction{ActionID: "action-1", ClientActionID: input.ClientActionID, Kind: agentextensionservice.SetupActionInstall, Status: agentextensionservice.SetupActionQueued, Phase: agentextensionservice.SetupPhasePreparing},
|
|
}, nil
|
|
},
|
|
authenticateFn: func(_ context.Context, input agentextensionservice.AuthenticateInput) (agentextensionservice.SetupSnapshot, error) {
|
|
capturedAuthenticate = input
|
|
return agentextensionservice.SetupSnapshot{
|
|
WorkspaceID: input.WorkspaceID, AgentTargetID: input.AgentTargetID,
|
|
Status: agentextensionservice.SetupAuthenticating,
|
|
Action: &agentextensionservice.SetupAction{ActionID: "auth-1", ClientActionID: input.ClientActionID, Kind: agentextensionservice.SetupActionAuthenticate, Status: agentextensionservice.SetupActionRunning, Phase: agentextensionservice.SetupPhaseAuthenticating},
|
|
}, nil
|
|
},
|
|
}
|
|
mux := http.NewServeMux()
|
|
RegisterRoutes(mux, NewRoutes(DaemonAPI{AgentTargetSetupService: service}))
|
|
|
|
getRecorder := performGeneratedRouteRequest(t, mux, http.MethodGet,
|
|
"/v1/workspaces/workspace-1/agent-targets/extension:codebuddy/setup", nil)
|
|
if getRecorder.Code != http.StatusOK {
|
|
t.Fatalf("GET status = %d; body: %s", getRecorder.Code, getRecorder.Body.String())
|
|
}
|
|
var getResponse tuttigenerated.AgentTargetSetupSnapshot
|
|
decodeGeneratedRouteResponse(t, getRecorder, &getResponse)
|
|
if getResponse.Plan == nil || getResponse.Plan.PackageName != "@tencent-ai/codebuddy-code" || getResponse.Plan.PackageVersion != "2.121.2" {
|
|
t.Fatalf("GET response = %#v", getResponse)
|
|
}
|
|
if len(getResponse.AuthMethods) != 2 || getResponse.AuthMethods[0].Id != "external" {
|
|
t.Fatalf("GET auth methods = %#v", getResponse.AuthMethods)
|
|
}
|
|
terminalMethod := getResponse.AuthMethods[1]
|
|
if terminalMethod.Type == nil || *terminalMethod.Type != "terminal" ||
|
|
terminalMethod.TerminalCommand == nil || *terminalMethod.TerminalCommand != "/opt/fixture/bin/agent" ||
|
|
terminalMethod.TerminalStartupAction == nil ||
|
|
terminalMethod.TerminalStartupAction.Type != tuttigenerated.AgentTargetTerminalStartupActionTypeSlashCommand ||
|
|
terminalMethod.TerminalStartupAction.CommandName != "login" ||
|
|
terminalMethod.TerminalStartupAction.ReadyText != "Fixture Agent ready" {
|
|
t.Fatalf("GET terminal auth method = %#v", terminalMethod)
|
|
}
|
|
if getResponse.AuthMethods[0].Type != nil || getResponse.AuthMethods[0].TerminalCommand != nil ||
|
|
getResponse.AuthMethods[0].TerminalStartupAction != nil {
|
|
t.Fatalf("GET non-terminal auth method = %#v", getResponse.AuthMethods[0])
|
|
}
|
|
if getResponse.Account == nil || getResponse.Account.Id != "user-1" || getResponse.Account.DisplayName != "Rhinoc" ||
|
|
getResponse.Account.AuthMethodId != "external" || getResponse.Account.Organization == nil || *getResponse.Account.Organization != "Tutti" {
|
|
t.Fatalf("GET account = %#v", getResponse.Account)
|
|
}
|
|
|
|
postRecorder := performGeneratedRouteRequest(t, mux, http.MethodPost,
|
|
"/v1/workspaces/workspace-1/agent-targets/extension:codebuddy/setup/install",
|
|
map[string]any{"planDigest": plan.PlanDigest, "clientActionId": "ui-action-1"})
|
|
if postRecorder.Code != http.StatusOK {
|
|
t.Fatalf("POST status = %d; body: %s", postRecorder.Code, postRecorder.Body.String())
|
|
}
|
|
if capturedInstall.AgentTargetID != "extension:codebuddy" || capturedInstall.ClientActionID != "ui-action-1" || capturedInstall.PlanDigest != plan.PlanDigest {
|
|
t.Fatalf("captured install = %#v", capturedInstall)
|
|
}
|
|
|
|
authRecorder := performGeneratedRouteRequest(t, mux, http.MethodPost,
|
|
"/v1/workspaces/workspace-1/agent-targets/extension:codebuddy/setup/authenticate",
|
|
map[string]any{"methodId": "external", "clientActionId": "ui-auth-1"})
|
|
if authRecorder.Code != http.StatusOK {
|
|
t.Fatalf("authenticate status = %d; body: %s", authRecorder.Code, authRecorder.Body.String())
|
|
}
|
|
if capturedAuthenticate.MethodID != "external" || capturedAuthenticate.ClientActionID != "ui-auth-1" {
|
|
t.Fatalf("captured authenticate = %#v", capturedAuthenticate)
|
|
}
|
|
}
|
|
|
|
func TestDaemonAPIGeneratedRouteMapsAgentTargetSetupErrors(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
err error
|
|
want int
|
|
}{
|
|
{name: "workspace missing", err: workspacedata.ErrWorkspaceNotFound, want: http.StatusNotFound},
|
|
{name: "target missing", err: workspacedata.ErrAgentTargetNotFound, want: http.StatusBadRequest},
|
|
{name: "invalid request", err: agentextensionservice.ErrInvalidInstallPlanRequest, want: http.StatusBadRequest},
|
|
{name: "storage failure", err: errors.New("storage failure"), want: http.StatusBadGateway},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
mux := http.NewServeMux()
|
|
RegisterRoutes(mux, NewRoutes(DaemonAPI{AgentTargetSetupService: stubAgentTargetSetupService{
|
|
getFn: func(context.Context, agentextensionservice.InstallPlanInput) (agentextensionservice.SetupSnapshot, error) {
|
|
return agentextensionservice.SetupSnapshot{}, test.err
|
|
},
|
|
}}))
|
|
recorder := performGeneratedRouteRequest(t, mux, http.MethodGet, "/v1/workspaces/workspace-1/agent-targets/extension:gemini/setup", nil)
|
|
if recorder.Code != test.want {
|
|
t.Fatalf("status = %d, want %d; body: %s", recorder.Code, test.want, recorder.Body.String())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDaemonAPIGeneratedRouteRequiresAgentTargetSetupService(t *testing.T) {
|
|
mux := http.NewServeMux()
|
|
RegisterRoutes(mux, NewRoutes(DaemonAPI{}))
|
|
recorder := performGeneratedRouteRequest(t, mux, http.MethodGet, "/v1/workspaces/workspace-1/agent-targets/extension:gemini/setup", nil)
|
|
if recorder.Code != http.StatusServiceUnavailable {
|
|
t.Fatalf("status = %d, want %d; body: %s", recorder.Code, http.StatusServiceUnavailable, recorder.Body.String())
|
|
}
|
|
}
|