Files
tutti/services/tuttid/api/daemon_workspace_test.go
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

159 lines
4.9 KiB
Go

package api
import (
"context"
"net/http"
"testing"
"time"
tuttigenerated "github.com/tutti-os/tutti/services/tuttid/api/generated"
workspacebiz "github.com/tutti-os/tutti/services/tuttid/biz/workspace"
workspacedata "github.com/tutti-os/tutti/services/tuttid/data/workspace"
workspaceservice "github.com/tutti-os/tutti/services/tuttid/service/workspace"
)
type stubCatalogService struct {
createFn func(context.Context, workspaceservice.CreateInput) (workspacebiz.Summary, error)
deleteFn func(context.Context, string) (workspaceservice.DeleteResult, error)
getFn func(context.Context, string) (workspacebiz.Summary, error)
listFn func(context.Context) ([]workspacebiz.Summary, error)
openFn func(context.Context, string) (workspacebiz.Summary, error)
startFn func(context.Context) (*workspacebiz.Summary, error)
updateFn func(context.Context, string, workspaceservice.UpdateInput) (workspacebiz.Summary, error)
}
func (s stubCatalogService) Create(ctx context.Context, input workspaceservice.CreateInput) (workspacebiz.Summary, error) {
if s.createFn == nil {
return workspacebiz.Summary{}, nil
}
return s.createFn(ctx, input)
}
func (s stubCatalogService) Delete(ctx context.Context, workspaceID string) (workspaceservice.DeleteResult, error) {
if s.deleteFn == nil {
return workspaceservice.DeleteResult{}, nil
}
return s.deleteFn(ctx, workspaceID)
}
func (s stubCatalogService) Get(ctx context.Context, workspaceID string) (workspacebiz.Summary, error) {
if s.getFn == nil {
return workspacebiz.Summary{}, nil
}
return s.getFn(ctx, workspaceID)
}
func (s stubCatalogService) List(ctx context.Context) ([]workspacebiz.Summary, error) {
if s.listFn == nil {
return nil, nil
}
return s.listFn(ctx)
}
func (s stubCatalogService) Open(ctx context.Context, workspaceID string) (workspacebiz.Summary, error) {
if s.openFn == nil {
return workspacebiz.Summary{}, nil
}
return s.openFn(ctx, workspaceID)
}
func (s stubCatalogService) Startup(ctx context.Context) (*workspacebiz.Summary, error) {
if s.startFn == nil {
return nil, nil
}
return s.startFn(ctx)
}
func (s stubCatalogService) Update(
ctx context.Context,
workspaceID string,
input workspaceservice.UpdateInput,
) (workspacebiz.Summary, error) {
if s.updateFn == nil {
return workspacebiz.Summary{}, nil
}
return s.updateFn(ctx, workspaceID, input)
}
func TestDaemonAPIGeneratedRoutesListWorkspaces(t *testing.T) {
lastOpenedAt := time.Date(2026, 5, 21, 8, 0, 0, 0, time.UTC)
handler := generatedRouteHandler(stubCatalogService{
listFn: func(context.Context) ([]workspacebiz.Summary, error) {
return []workspacebiz.Summary{
{
ID: "ws-1",
Name: "Workspace One",
LastOpenedAt: &lastOpenedAt,
},
}, nil
},
})
recorder := performGeneratedRouteRequest(t, handler, http.MethodGet, "/v1/workspaces", nil)
if recorder.Code != http.StatusOK {
t.Fatalf("status = %d, want %d", recorder.Code, http.StatusOK)
}
var response tuttigenerated.ListWorkspacesResponse
decodeGeneratedRouteResponse(t, recorder, &response)
if response.TotalCount != 1 {
t.Fatalf("totalCount = %d, want 1", response.TotalCount)
}
if len(response.Workspaces) != 1 {
t.Fatalf("workspaces len = %d, want 1", len(response.Workspaces))
}
workspace := response.Workspaces[0]
if workspace.Id != "ws-1" || workspace.Name != "Workspace One" {
t.Fatalf("workspace = %#v", workspace)
}
if workspace.LastOpenedAt == nil || !workspace.LastOpenedAt.Equal(lastOpenedAt) {
t.Fatalf("lastOpenedAt = %#v, want %s", workspace.LastOpenedAt, lastOpenedAt.Format(time.RFC3339))
}
}
func TestDaemonAPIGeneratedRoutesCreateValidatesBody(t *testing.T) {
handler := generatedRouteHandler(stubCatalogService{})
recorder := performGeneratedRouteRequest(t, handler, http.MethodPost, "/v1/workspaces", map[string]string{
"name": " ",
})
if recorder.Code != http.StatusBadRequest {
t.Fatalf("status = %d, want %d", recorder.Code, http.StatusBadRequest)
}
assertGeneratedRouteError(
t,
recorder,
tuttigenerated.InvalidRequest,
"missing_workspace_name",
"workspace name is required",
)
}
func TestDaemonAPIGeneratedRoutesMapWorkspaceNotFound(t *testing.T) {
handler := generatedRouteHandler(stubCatalogService{
getFn: func(context.Context, string) (workspacebiz.Summary, error) {
return workspacebiz.Summary{}, workspacedata.ErrWorkspaceNotFound
},
})
recorder := performGeneratedRouteRequest(t, handler, http.MethodGet, "/v1/workspaces/ws-missing", nil)
if recorder.Code != http.StatusNotFound {
t.Fatalf("status = %d, want %d", recorder.Code, http.StatusNotFound)
}
assertGeneratedRouteError(
t,
recorder,
tuttigenerated.WorkspaceNotFound,
"workspace_not_found",
workspacedata.ErrWorkspaceNotFound.Error(),
)
}
func generatedRouteHandler(service stubCatalogService) http.Handler {
mux := http.NewServeMux()
RegisterRoutes(mux, NewRoutes(DaemonAPI{WorkspaceService: service}))
return mux
}