Files
tutti/services/tuttid/api/daemon_tutti_mode_activation_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

385 lines
17 KiB
Go

package api
import (
"context"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
tuttigenerated "github.com/tutti-os/tutti/services/tuttid/api/generated"
agenttargetbiz "github.com/tutti-os/tutti/services/tuttid/biz/agenttarget"
tuttimodeactivationbiz "github.com/tutti-os/tutti/services/tuttid/biz/tuttimodeactivation"
agentservice "github.com/tutti-os/tutti/services/tuttid/service/agent"
tuttimodeactivationservice "github.com/tutti-os/tutti/services/tuttid/service/tuttimodeactivation"
)
func TestGetTuttiModeActivationReturnsSessionNotFound(t *testing.T) {
t.Parallel()
api := DaemonAPI{
AgentSessionService: stubAgentSessionService{getFn: func(context.Context, string, string) (agentservice.Session, error) {
return agentservice.Session{}, agentservice.ErrSessionNotFound
}},
TuttiModeActivationService: &stubTuttiModeActivationService{},
}
response, err := api.GetWorkspaceAgentSessionTuttiModeActivation(context.Background(), tuttigenerated.GetWorkspaceAgentSessionTuttiModeActivationRequestObject{
WorkspaceID: "workspace-1", AgentSessionID: "session-1",
})
if err != nil {
t.Fatal(err)
}
if _, ok := response.(tuttigenerated.GetWorkspaceAgentSessionTuttiModeActivation404JSONResponse); !ok {
t.Fatalf("response = %T, want 404", response)
}
}
func TestUpdateTuttiModeActivationReturnsRevisionConflict(t *testing.T) {
t.Parallel()
api := DaemonAPI{
AgentSessionService: stubAgentSessionService{},
TuttiModeActivationService: &stubTuttiModeActivationService{setFn: func(context.Context, tuttimodeactivationservice.SetInput) (tuttimodeactivationservice.SetResult, error) {
return tuttimodeactivationservice.SetResult{}, tuttimodeactivationservice.ErrRevisionConflict
}},
}
expected := int64(1)
response, err := api.UpdateWorkspaceAgentSessionTuttiModeActivation(context.Background(), tuttigenerated.UpdateWorkspaceAgentSessionTuttiModeActivationRequestObject{
WorkspaceID: "workspace-1", AgentSessionID: "session-1",
Body: &tuttigenerated.UpdateTuttiModeActivationRequest{
Status: tuttigenerated.TuttiModeActivationStatusInactive,
Source: tuttigenerated.TuttiModeActivationSourceBadgeRemove,
ExpectedRevision: &expected,
},
})
if err != nil {
t.Fatal(err)
}
if _, ok := response.(tuttigenerated.UpdateWorkspaceAgentSessionTuttiModeActivation409JSONResponse); !ok {
t.Fatalf("response = %T, want 409", response)
}
}
func TestUpdateTuttiModeActivationMapsPreferencesAndLegacyEffectAlias(t *testing.T) {
t.Parallel()
effect, speed, legacy := 80, 70, 20
var received tuttimodeactivationservice.SetInput
api := DaemonAPI{
AgentSessionService: stubAgentSessionService{},
TuttiModeActivationService: &stubTuttiModeActivationService{setFn: func(_ context.Context, input tuttimodeactivationservice.SetInput) (tuttimodeactivationservice.SetResult, error) {
received = input
return tuttimodeactivationservice.SetResult{}, nil
}},
}
_, err := api.UpdateWorkspaceAgentSessionTuttiModeActivation(context.Background(), tuttigenerated.UpdateWorkspaceAgentSessionTuttiModeActivationRequestObject{
WorkspaceID: "workspace-1", AgentSessionID: "session-1",
Body: &tuttigenerated.UpdateTuttiModeActivationRequest{
Status: tuttigenerated.TuttiModeActivationStatusActive,
Source: tuttigenerated.TuttiModeActivationSourceSlashCommand,
Effect: &effect, Speed: &speed, OrchestrationIntensity: &legacy,
},
})
if err != nil {
t.Fatal(err)
}
if received.Effect == nil || *received.Effect != effect || received.Speed == nil || *received.Speed != speed {
t.Fatalf("Set input = %#v, want effect=%d speed=%d", received, effect, speed)
}
received = tuttimodeactivationservice.SetInput{}
_, err = api.UpdateWorkspaceAgentSessionTuttiModeActivation(context.Background(), tuttigenerated.UpdateWorkspaceAgentSessionTuttiModeActivationRequestObject{
WorkspaceID: "workspace-1", AgentSessionID: "session-1",
Body: &tuttigenerated.UpdateTuttiModeActivationRequest{
Status: tuttigenerated.TuttiModeActivationStatusActive,
Source: tuttigenerated.TuttiModeActivationSourceSlashCommand,
OrchestrationIntensity: &legacy,
},
})
if err != nil {
t.Fatal(err)
}
if received.Effect == nil || *received.Effect != legacy || received.Speed != nil {
t.Fatalf("legacy Set input = %#v, want effect=%d and omitted speed", received, legacy)
}
}
func TestUpdateTuttiModeActivationRejectsNegativeExpectedRevisionAtTransportBoundary(t *testing.T) {
t.Parallel()
api := DaemonAPI{
AgentSessionService: stubAgentSessionService{getFn: func(context.Context, string, string) (agentservice.Session, error) {
t.Fatal("Get should not be called for a negative expectedRevision")
return agentservice.Session{}, nil
}},
TuttiModeActivationService: &stubTuttiModeActivationService{setFn: func(context.Context, tuttimodeactivationservice.SetInput) (tuttimodeactivationservice.SetResult, error) {
t.Fatal("Set should not be called for a negative expectedRevision")
return tuttimodeactivationservice.SetResult{}, nil
}},
}
mux := http.NewServeMux()
RegisterRoutes(mux, NewRoutes(api))
request := httptest.NewRequest(
http.MethodPut,
"/v1/workspaces/workspace-1/agent-sessions/session-1/tutti-mode-activation",
strings.NewReader(`{"status":"inactive","source":"badge_remove","expectedRevision":-1}`),
)
request.Header.Set("Content-Type", "application/json")
response := httptest.NewRecorder()
mux.ServeHTTP(response, request)
if response.Code != http.StatusBadRequest {
t.Fatalf("status = %d, want %d; body: %s", response.Code, http.StatusBadRequest, response.Body.String())
}
}
func TestCreateAgentSessionRejectsInvalidInitialTuttiModeActivationAtTransportBoundary(t *testing.T) {
t.Parallel()
tests := []struct {
name string
activation map[string]any
}{
{name: "invalid status", activation: map[string]any{"status": "paused", "source": "slash_command"}},
{name: "invalid source", activation: map[string]any{"status": "active", "source": "toolbar"}},
{name: "missing status", activation: map[string]any{"source": "slash_command"}},
{name: "missing source", activation: map[string]any{"status": "active"}},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
t.Parallel()
mux := http.NewServeMux()
RegisterRoutes(mux, NewRoutes(DaemonAPI{
AgentSessionService: stubAgentSessionService{
createFn: func(context.Context, string, agentservice.CreateSessionInput) (agentservice.Session, error) {
t.Fatal("Create should not be called for an invalid initialTuttiModeActivation")
return agentservice.Session{}, nil
},
},
}))
response := performGeneratedRouteRequest(t, mux, http.MethodPost, "/v1/workspaces/workspace-1/agent-sessions", map[string]any{
"agentSessionId": "11111111-1111-4111-8111-111111111111",
"agentTargetId": agenttargetbiz.IDLocalCodex,
"clientSubmitId": "submit-1",
"initialContent": []map[string]any{},
"initialTuttiModeActivation": test.activation,
})
if response.Code != http.StatusBadRequest {
t.Fatalf("status = %d, want %d; body: %s", response.Code, http.StatusBadRequest, response.Body.String())
}
})
}
}
func TestUpdateTuttiModeActivationRejectsInvalidEnumsAtTransportBoundary(t *testing.T) {
t.Parallel()
tests := []struct {
name string
body map[string]any
}{
{name: "invalid status", body: map[string]any{"status": "paused", "source": "slash_command"}},
{name: "invalid source", body: map[string]any{"status": "active", "source": "toolbar"}},
{name: "missing status", body: map[string]any{"source": "slash_command"}},
{name: "missing source", body: map[string]any{"status": "active"}},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
t.Parallel()
api := DaemonAPI{
AgentSessionService: stubAgentSessionService{getFn: func(context.Context, string, string) (agentservice.Session, error) {
t.Fatal("Get should not be called for invalid activation enums")
return agentservice.Session{}, nil
}},
TuttiModeActivationService: &stubTuttiModeActivationService{setFn: func(context.Context, tuttimodeactivationservice.SetInput) (tuttimodeactivationservice.SetResult, error) {
t.Fatal("Set should not be called for invalid activation enums")
return tuttimodeactivationservice.SetResult{}, nil
}},
}
mux := http.NewServeMux()
RegisterRoutes(mux, NewRoutes(api))
response := performGeneratedRouteRequest(
t,
mux,
http.MethodPut,
"/v1/workspaces/workspace-1/agent-sessions/session-1/tutti-mode-activation",
test.body,
)
if response.Code != http.StatusBadRequest {
t.Fatalf("status = %d, want %d; body: %s", response.Code, http.StatusBadRequest, response.Body.String())
}
})
}
}
func TestTuttiModeActivationRouteIsRegistered(t *testing.T) {
t.Parallel()
api := DaemonAPI{
AgentSessionService: stubAgentSessionService{},
TuttiModeActivationService: &stubTuttiModeActivationService{},
}
mux := http.NewServeMux()
RegisterRoutes(mux, NewRoutes(api))
request := httptest.NewRequest(http.MethodGet, "/v1/workspaces/workspace-1/agent-sessions/session-1/tutti-mode-activation", nil)
response := httptest.NewRecorder()
mux.ServeHTTP(response, request)
if response.Code != http.StatusOK || !strings.Contains(response.Body.String(), `"activation":null`) {
t.Fatalf("route status=%d body=%s", response.Code, response.Body.String())
}
}
func TestGetTuttiModeActivationReturnsProjectionErrorForInvalidDurableIdentity(t *testing.T) {
t.Parallel()
now := time.UnixMilli(1_700_000_000_000).UTC()
api := DaemonAPI{
AgentSessionService: stubAgentSessionService{},
TuttiModeActivationService: &stubTuttiModeActivationService{getFn: func(context.Context, string, string) (*tuttimodeactivationbiz.Activation, error) {
return &tuttimodeactivationbiz.Activation{
ID: "not-a-uuid", WorkspaceID: "workspace-opaque", AgentSessionID: "session-1",
CreatedAt: now, UpdatedAt: now,
CurrentRevision: tuttimodeactivationbiz.Revision{
ID: "1ca08e98-728d-4fe9-8fd4-a2362698aeac", ActivationID: "not-a-uuid",
Revision: 1, State: tuttimodeactivationbiz.StateActive, Source: tuttimodeactivationbiz.SourceSlashCommand, CreatedAt: now,
},
}, nil
}},
}
response, err := api.GetWorkspaceAgentSessionTuttiModeActivation(context.Background(), tuttigenerated.GetWorkspaceAgentSessionTuttiModeActivationRequestObject{
WorkspaceID: "workspace-opaque", AgentSessionID: "session-1",
})
if err != nil {
t.Fatal(err)
}
if _, ok := response.(tuttigenerated.GetWorkspaceAgentSessionTuttiModeActivation502JSONResponse); !ok {
t.Fatalf("response = %T, want 502 projection error", response)
}
}
func TestGeneratedTuttiModeActivationProjectsCurrentRevision(t *testing.T) {
t.Parallel()
now := time.UnixMilli(1_700_000_000_000).UTC()
activation := &tuttimodeactivationbiz.Activation{
ID: "32bb4e49-5d37-423d-a087-c2f1b5881284", WorkspaceID: "60f2bb2e-02f0-41bd-bfaf-da28b0f92b7f", AgentSessionID: "session-1",
CreatedAt: now, UpdatedAt: now.Add(time.Second),
CurrentRevision: tuttimodeactivationbiz.Revision{
ID: "1ca08e98-728d-4fe9-8fd4-a2362698aeac", ActivationID: "32bb4e49-5d37-423d-a087-c2f1b5881284",
Revision: 2, State: tuttimodeactivationbiz.StateInactive, Source: tuttimodeactivationbiz.SourceBadgeRemove,
Effect: 80, Speed: 70, CreatedAt: now.Add(time.Second),
},
}
generated, err := generatedTuttiModeActivation(activation)
if err != nil {
t.Fatalf("generatedTuttiModeActivation() error = %v", err)
}
if generated == nil || generated.WorkspaceId != activation.WorkspaceID || generated.Status != tuttigenerated.TuttiModeActivationStatusInactive || generated.CurrentRevision.Revision != 2 || generated.CurrentRevision.Status != tuttigenerated.TuttiModeActivationStatusInactive || generated.CurrentRevision.Source != tuttigenerated.TuttiModeActivationSourceBadgeRemove || generated.CurrentRevision.Effect == nil || *generated.CurrentRevision.Effect != 80 || generated.CurrentRevision.Speed == nil || *generated.CurrentRevision.Speed != 70 || generated.CurrentRevision.OrchestrationIntensity != 80 {
t.Fatalf("generated = %#v", generated)
}
}
func TestGeneratedTuttiModeActivationPreservesOpaqueWorkspaceID(t *testing.T) {
t.Parallel()
now := time.UnixMilli(1_700_000_000_000).UTC()
activation := &tuttimodeactivationbiz.Activation{
ID: "32bb4e49-5d37-423d-a087-c2f1b5881284", WorkspaceID: "workspace-opaque", AgentSessionID: "session-1",
CreatedAt: now, UpdatedAt: now,
CurrentRevision: tuttimodeactivationbiz.Revision{
ID: "1ca08e98-728d-4fe9-8fd4-a2362698aeac", ActivationID: "32bb4e49-5d37-423d-a087-c2f1b5881284",
Revision: 1, State: tuttimodeactivationbiz.StateActive, Source: tuttimodeactivationbiz.SourceSlashCommand, CreatedAt: now,
},
}
generated, err := generatedTuttiModeActivation(activation)
if err != nil {
t.Fatalf("generatedTuttiModeActivation() error = %v", err)
}
if generated == nil || generated.WorkspaceId != "workspace-opaque" {
t.Fatalf("generated workspace id = %#v", generated)
}
}
func TestGeneratedTuttiModeActivationRejectsInvalidUUIDIdentity(t *testing.T) {
t.Parallel()
now := time.UnixMilli(1_700_000_000_000).UTC()
for _, invalidID := range []string{"not-a-uuid", "00000000-0000-0000-0000-000000000000"} {
activation := &tuttimodeactivationbiz.Activation{
ID: invalidID, WorkspaceID: "workspace-opaque", AgentSessionID: "session-1",
CreatedAt: now, UpdatedAt: now,
CurrentRevision: tuttimodeactivationbiz.Revision{
ID: "1ca08e98-728d-4fe9-8fd4-a2362698aeac", ActivationID: invalidID,
Revision: 1, State: tuttimodeactivationbiz.StateActive, Source: tuttimodeactivationbiz.SourceSlashCommand, CreatedAt: now,
},
}
generated, err := generatedTuttiModeActivation(activation)
if err == nil || generated != nil {
t.Fatalf("id=%q generated=%#v error=%v, want explicit projection error", invalidID, generated, err)
}
}
}
func TestGeneratedAgentSessionPropagatesInvalidTuttiModeIdentity(t *testing.T) {
t.Parallel()
now := time.UnixMilli(1_700_000_000_000).UTC()
_, err := generatedAgentSession(agentservice.Session{
ID: "session-1", CreatedAt: now,
TuttiModeActivation: &tuttimodeactivationbiz.Activation{
ID: "not-a-uuid", WorkspaceID: "workspace-opaque", AgentSessionID: "session-1",
CreatedAt: now, UpdatedAt: now,
CurrentRevision: tuttimodeactivationbiz.Revision{
ID: "1ca08e98-728d-4fe9-8fd4-a2362698aeac", ActivationID: "not-a-uuid",
Revision: 1, State: tuttimodeactivationbiz.StateActive, Source: tuttimodeactivationbiz.SourceSlashCommand, CreatedAt: now,
},
},
})
if err == nil {
t.Fatal("generatedAgentSession() error = nil, want activation projection error")
}
}
func TestInitialTuttiModeActivationIsIndependentFromCapabilityRefs(t *testing.T) {
t.Parallel()
effect, speed, legacy := 82, 68, 20
intent, err := tuttiModeActivationIntentFromGenerated(&tuttigenerated.TuttiModeActivationIntent{
Status: tuttigenerated.TuttiModeActivationStatusActive,
Source: tuttigenerated.TuttiModeActivationSourceSlashCommand,
Effect: &effect, Speed: &speed, OrchestrationIntensity: &legacy,
})
if err != nil {
t.Fatalf("tuttiModeActivationIntentFromGenerated() error = %v", err)
}
if intent == nil || intent.State != "active" || intent.Source != "slash_command" ||
intent.Effect == nil || *intent.Effect != effect || intent.Speed == nil || *intent.Speed != speed {
t.Fatalf("intent = %#v", intent)
}
legacyIntent, err := tuttiModeActivationIntentFromGenerated(&tuttigenerated.TuttiModeActivationIntent{
Status: tuttigenerated.TuttiModeActivationStatusActive,
Source: tuttigenerated.TuttiModeActivationSourceSlashCommand,
OrchestrationIntensity: &legacy,
})
if err != nil || legacyIntent == nil || legacyIntent.Effect == nil || *legacyIntent.Effect != legacy || legacyIntent.Speed != nil {
t.Fatalf("legacy intent = %#v error=%v", legacyIntent, err)
}
missing, err := tuttiModeActivationIntentFromGenerated(nil)
if err != nil || missing != nil {
t.Fatal("missing intent was reconstructed")
}
}
type stubTuttiModeActivationService struct {
getFn func(context.Context, string, string) (*tuttimodeactivationbiz.Activation, error)
setFn func(context.Context, tuttimodeactivationservice.SetInput) (tuttimodeactivationservice.SetResult, error)
}
func (s *stubTuttiModeActivationService) Get(ctx context.Context, workspaceID, sessionID string) (*tuttimodeactivationbiz.Activation, error) {
if s.getFn == nil {
return nil, nil
}
return s.getFn(ctx, workspaceID, sessionID)
}
func (s *stubTuttiModeActivationService) Set(ctx context.Context, input tuttimodeactivationservice.SetInput) (tuttimodeactivationservice.SetResult, error) {
if s.setFn == nil {
return tuttimodeactivationservice.SetResult{}, nil
}
return s.setFn(ctx, input)
}