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

387 lines
16 KiB
Go

package api
import (
"context"
"net/http"
"testing"
tuttigenerated "github.com/tutti-os/tutti/services/tuttid/api/generated"
preferencesbiz "github.com/tutti-os/tutti/services/tuttid/biz/preferences"
preferencesservice "github.com/tutti-os/tutti/services/tuttid/service/preferences"
)
type stubPreferencesService struct {
getFn func(context.Context) (preferencesbiz.DesktopPreferences, error)
putFn func(context.Context, preferencesservice.PutInput) (preferencesbiz.DesktopPreferences, error)
}
func (s stubPreferencesService) Get(ctx context.Context) (preferencesbiz.DesktopPreferences, error) {
if s.getFn == nil {
return preferencesbiz.DefaultDesktopPreferences(), nil
}
return s.getFn(ctx)
}
func (s stubPreferencesService) Put(ctx context.Context, input preferencesservice.PutInput) (preferencesbiz.DesktopPreferences, error) {
if s.putFn == nil {
return preferencesbiz.DesktopPreferences{
AgentConversationDetailMode: input.AgentConversationDetailMode,
AgentDockLayout: input.AgentDockLayout,
DefaultAgentProvider: input.DefaultAgentProvider,
DockIconStyle: "default",
DockPlacement: input.DockPlacement,
Initialized: true,
Locale: input.Locale,
SleepPreventionMode: input.SleepPreventionMode,
ThemeSource: input.ThemeSource,
UpdateChannel: input.UpdateChannel,
UpdatePolicy: input.UpdatePolicy,
}, nil
}
return s.putFn(ctx, input)
}
func TestDaemonAPIGeneratedRoutesGetDesktopPreferences(t *testing.T) {
mux := http.NewServeMux()
RegisterRoutes(mux, NewRoutes(DaemonAPI{
PreferencesService: stubPreferencesService{
getFn: func(context.Context) (preferencesbiz.DesktopPreferences, error) {
return preferencesbiz.DesktopPreferences{
AgentConversationDetailMode: "general",
AgentDockLayout: "unified",
DefaultAgentProvider: "claude-code",
DockIconStyle: "default",
DockPlacement: "left",
Initialized: true,
Locale: "zh-CN",
MinimizeAnimation: "scale",
SleepPreventionMode: "whileAgentRunning",
ThemeSource: "dark",
UpdateChannel: "rc",
UpdatePolicy: "auto",
}, nil
},
},
}))
recorder := performGeneratedRouteRequest(t, mux, http.MethodGet, "/v1/preferences/desktop", nil)
if recorder.Code != http.StatusOK {
t.Fatalf("status = %d, want %d; body: %s", recorder.Code, http.StatusOK, recorder.Body.String())
}
var response tuttigenerated.DesktopPreferencesStateResponse
decodeGeneratedRouteResponse(t, recorder, &response)
if !response.Initialized {
t.Fatal("initialized = false, want true")
}
if response.Preferences.DockPlacement != tuttigenerated.Left {
t.Fatalf("dockPlacement = %q, want %q", response.Preferences.DockPlacement, tuttigenerated.Left)
}
if response.Preferences.Locale != tuttigenerated.ZhCN {
t.Fatalf("locale = %q, want %q", response.Preferences.Locale, tuttigenerated.ZhCN)
}
if response.Preferences.DefaultAgentProvider != tuttigenerated.DesktopDefaultAgentProviderClaudeCode {
t.Fatalf("defaultAgentProvider = %q, want %q", response.Preferences.DefaultAgentProvider, tuttigenerated.DesktopDefaultAgentProviderClaudeCode)
}
if response.Preferences.AgentConversationDetailMode != tuttigenerated.General {
t.Fatalf("agentConversationDetailMode = %q, want %q", response.Preferences.AgentConversationDetailMode, tuttigenerated.General)
}
if response.Preferences.AgentDockLayout != tuttigenerated.Unified {
t.Fatalf("agentDockLayout = %q, want %q", response.Preferences.AgentDockLayout, tuttigenerated.Unified)
}
if response.Preferences.ThemeSource != tuttigenerated.DesktopThemeSourceDark {
t.Fatalf("themeSource = %q, want %q", response.Preferences.ThemeSource, tuttigenerated.DesktopThemeSourceDark)
}
if response.Preferences.SleepPreventionMode != tuttigenerated.WhileAgentRunning {
t.Fatalf("sleepPreventionMode = %q, want %q", response.Preferences.SleepPreventionMode, tuttigenerated.WhileAgentRunning)
}
if response.Preferences.UpdateChannel != tuttigenerated.Rc {
t.Fatalf("updateChannel = %q, want %q", response.Preferences.UpdateChannel, tuttigenerated.Rc)
}
if response.Preferences.UpdatePolicy != tuttigenerated.DesktopUpdatePolicyAuto {
t.Fatalf("updatePolicy = %q, want %q", response.Preferences.UpdatePolicy, tuttigenerated.DesktopUpdatePolicyAuto)
}
}
func TestDaemonAPIGeneratedRoutesPutDesktopPreferencesPersistsAgentGUIConversationRailPreference(t *testing.T) {
mux := http.NewServeMux()
var captured preferencesservice.PutInput
RegisterRoutes(mux, NewRoutes(DaemonAPI{
PreferencesService: stubPreferencesService{
putFn: func(_ context.Context, input preferencesservice.PutInput) (preferencesbiz.DesktopPreferences, error) {
captured = input
return preferencesbiz.DesktopPreferences{
AgentCLIUpdateCheckEnabled: input.AgentCLIUpdateCheckEnabled,
AgentGUIConversationRailCollapsedByProvider: input.AgentGUIConversationRailCollapsedByProvider,
AgentConversationDetailMode: input.AgentConversationDetailMode,
AgentDockLayout: input.AgentDockLayout,
AppCatalogChannel: input.AppCatalogChannel,
DefaultAgentProvider: input.DefaultAgentProvider,
DockIconStyle: input.DockIconStyle,
DockPlacement: input.DockPlacement,
Initialized: true,
Locale: input.Locale,
SleepPreventionMode: input.SleepPreventionMode,
ThemeSource: input.ThemeSource,
UpdateChannel: input.UpdateChannel,
UpdatePolicy: input.UpdatePolicy,
}, nil
},
},
}))
recorder := performGeneratedRouteRequest(t, mux, http.MethodPut, "/v1/preferences/desktop", map[string]any{
"writeMode": "initializeIfAbsent",
"preferences": map[string]any{
"agentCliUpdateCheckEnabled": false,
"agentComposerDefaultsByProvider": map[string]any{},
"agentGuiConversationRailCollapsedByProvider": map[string]any{
"claude-code": false,
"codex": true,
},
"agentConversationDetailMode": "general",
"agentDockLayout": "legacySplit",
"defaultAgentProvider": "codex",
"appCatalogChannel": "staging",
"dockIconStyle": "default",
"dockPlacement": "bottom",
"locale": "zh-CN",
"minimizeAnimation": "scale",
"sleepPreventionMode": "never",
"themeSource": "dark",
"updateChannel": "stable",
"updatePolicy": "prompt",
},
})
if recorder.Code != http.StatusOK {
t.Fatalf("status = %d, want %d; body: %s", recorder.Code, http.StatusOK, recorder.Body.String())
}
if captured.WriteMode != preferencesservice.DesktopPreferencesWriteModeInitializeIfAbsent {
t.Fatalf("captured write mode = %q, want initializeIfAbsent", captured.WriteMode)
}
if captured.AgentCLIUpdateCheckEnabled {
t.Fatal("captured agent CLI update check = true, want false")
}
if !captured.AgentGUIConversationRailCollapsedByProvider["codex"] {
t.Fatalf("captured rail preference = %#v, want codex true", captured.AgentGUIConversationRailCollapsedByProvider)
}
if collapsed, ok := captured.AgentGUIConversationRailCollapsedByProvider["claude-code"]; !ok || collapsed {
t.Fatalf("captured rail preference = %#v, want claude-code false", captured.AgentGUIConversationRailCollapsedByProvider)
}
if captured.AgentDockLayout != "legacySplit" {
t.Fatalf("captured agentDockLayout = %q, want legacySplit", captured.AgentDockLayout)
}
if captured.AppCatalogChannel != "staging" {
t.Fatalf("captured appCatalogChannel = %q, want staging", captured.AppCatalogChannel)
}
if captured.AgentConversationDetailMode != "general" {
t.Fatalf("captured agentConversationDetailMode = %q, want general", captured.AgentConversationDetailMode)
}
var response tuttigenerated.DesktopPreferencesStateResponse
decodeGeneratedRouteResponse(t, recorder, &response)
if response.Preferences.AgentCliUpdateCheckEnabled {
t.Fatal("response agent CLI update check = true, want false")
}
if response.Preferences.AgentGuiConversationRailCollapsedByProvider.Codex == nil ||
!*response.Preferences.AgentGuiConversationRailCollapsedByProvider.Codex {
t.Fatalf("response rail codex = %#v, want true", response.Preferences.AgentGuiConversationRailCollapsedByProvider.Codex)
}
if response.Preferences.AgentGuiConversationRailCollapsedByProvider.ClaudeCode == nil ||
*response.Preferences.AgentGuiConversationRailCollapsedByProvider.ClaudeCode {
t.Fatalf("response rail claude-code = %#v, want false", response.Preferences.AgentGuiConversationRailCollapsedByProvider.ClaudeCode)
}
if response.Preferences.AgentConversationDetailMode != tuttigenerated.General {
t.Fatalf("response agentConversationDetailMode = %q, want general", response.Preferences.AgentConversationDetailMode)
}
if response.Preferences.AppCatalogChannel != tuttigenerated.Staging {
t.Fatalf("response appCatalogChannel = %q, want staging", response.Preferences.AppCatalogChannel)
}
}
func TestDaemonAPIGeneratedRoutesPutDesktopPreferencesRejectsUnsupportedWriteMode(t *testing.T) {
mux := http.NewServeMux()
RegisterRoutes(mux, NewRoutes(DaemonAPI{
PreferencesService: stubPreferencesService{
putFn: func(context.Context, preferencesservice.PutInput) (preferencesbiz.DesktopPreferences, error) {
t.Fatal("Put should not be called when write mode is invalid")
return preferencesbiz.DesktopPreferences{}, nil
},
},
}))
recorder := performGeneratedRouteRequest(t, mux, http.MethodPut, "/v1/preferences/desktop", map[string]any{
"writeMode": "merge",
"preferences": map[string]any{},
})
if recorder.Code != http.StatusBadRequest {
t.Fatalf("status = %d, want %d; body: %s", recorder.Code, http.StatusBadRequest, recorder.Body.String())
}
assertGeneratedRouteError(
t,
recorder,
tuttigenerated.InvalidRequest,
"unsupported_desktop_preferences_write_mode",
"desktop preferences write mode is unsupported",
)
}
func TestDaemonAPIGeneratedRoutesPutDesktopPreferencesRequiresAgentConversationDetailMode(t *testing.T) {
mux := http.NewServeMux()
RegisterRoutes(mux, NewRoutes(DaemonAPI{
PreferencesService: stubPreferencesService{
putFn: func(context.Context, preferencesservice.PutInput) (preferencesbiz.DesktopPreferences, error) {
t.Fatal("Put should not be called when agent conversation detail mode is missing")
return preferencesbiz.DesktopPreferences{}, nil
},
},
}))
recorder := performGeneratedRouteRequest(t, mux, http.MethodPut, "/v1/preferences/desktop", map[string]any{
"preferences": map[string]any{
"defaultAgentProvider": "codex",
"appCatalogChannel": "production",
"dockIconStyle": "default",
"dockPlacement": "bottom",
"locale": "en",
"minimizeAnimation": "scale",
"sleepPreventionMode": "never",
"themeSource": "dark",
"updateChannel": "stable",
"updatePolicy": "prompt",
},
})
if recorder.Code != http.StatusBadRequest {
t.Fatalf("status = %d, want %d; body: %s", recorder.Code, http.StatusBadRequest, recorder.Body.String())
}
assertGeneratedRouteError(
t,
recorder,
tuttigenerated.InvalidRequest,
"missing_desktop_agent_conversation_detail_mode",
"desktop agent conversation detail mode is required",
)
}
func TestDaemonAPIGeneratedRoutesPutDesktopPreferencesValidatesAgentConversationDetailMode(t *testing.T) {
mux := http.NewServeMux()
RegisterRoutes(mux, NewRoutes(DaemonAPI{
PreferencesService: stubPreferencesService{
putFn: func(context.Context, preferencesservice.PutInput) (preferencesbiz.DesktopPreferences, error) {
t.Fatal("Put should not be called when agent conversation detail mode is invalid")
return preferencesbiz.DesktopPreferences{}, nil
},
},
}))
recorder := performGeneratedRouteRequest(t, mux, http.MethodPut, "/v1/preferences/desktop", map[string]any{
"preferences": map[string]any{
"agentConversationDetailMode": "daily",
"agentDockLayout": "legacySplit",
"defaultAgentProvider": "codex",
"appCatalogChannel": "production",
"dockIconStyle": "default",
"dockPlacement": "bottom",
"locale": "en",
"minimizeAnimation": "scale",
"sleepPreventionMode": "never",
"themeSource": "dark",
"updateChannel": "stable",
"updatePolicy": "prompt",
},
})
if recorder.Code != http.StatusBadRequest {
t.Fatalf("status = %d, want %d; body: %s", recorder.Code, http.StatusBadRequest, recorder.Body.String())
}
assertGeneratedRouteError(
t,
recorder,
tuttigenerated.InvalidRequest,
"unsupported_desktop_agent_conversation_detail_mode",
"desktop agent conversation detail mode is unsupported",
)
}
func TestDaemonAPIGeneratedRoutesPutDesktopPreferencesValidatesLocale(t *testing.T) {
mux := http.NewServeMux()
RegisterRoutes(mux, NewRoutes(DaemonAPI{
PreferencesService: stubPreferencesService{
putFn: func(context.Context, preferencesservice.PutInput) (preferencesbiz.DesktopPreferences, error) {
t.Fatal("Put should not be called for invalid locale")
return preferencesbiz.DesktopPreferences{}, nil
},
},
}))
recorder := performGeneratedRouteRequest(t, mux, http.MethodPut, "/v1/preferences/desktop", map[string]any{
"preferences": map[string]any{
"agentConversationDetailMode": "general",
"agentDockLayout": "legacySplit",
"defaultAgentProvider": "codex",
"appCatalogChannel": "production",
"dockIconStyle": "default",
"dockPlacement": "bottom",
"locale": "fr",
"minimizeAnimation": "scale",
"sleepPreventionMode": "never",
"themeSource": "dark",
"updateChannel": "stable",
"updatePolicy": "prompt",
},
})
if recorder.Code != http.StatusBadRequest {
t.Fatalf("status = %d, want %d; body: %s", recorder.Code, http.StatusBadRequest, recorder.Body.String())
}
assertGeneratedRouteError(
t,
recorder,
tuttigenerated.InvalidRequest,
"unsupported_desktop_locale",
"desktop locale is unsupported",
)
}
func TestDaemonAPIGeneratedRoutesPutDesktopPreferencesRequiresAgentDockLayout(t *testing.T) {
mux := http.NewServeMux()
RegisterRoutes(mux, NewRoutes(DaemonAPI{
PreferencesService: stubPreferencesService{
putFn: func(context.Context, preferencesservice.PutInput) (preferencesbiz.DesktopPreferences, error) {
t.Fatal("Put should not be called when agent dock layout is missing")
return preferencesbiz.DesktopPreferences{}, nil
},
},
}))
recorder := performGeneratedRouteRequest(t, mux, http.MethodPut, "/v1/preferences/desktop", map[string]any{
"preferences": map[string]any{
"agentConversationDetailMode": "general",
"defaultAgentProvider": "codex",
"appCatalogChannel": "production",
"dockIconStyle": "default",
"dockPlacement": "bottom",
"locale": "en",
"minimizeAnimation": "scale",
"sleepPreventionMode": "never",
"themeSource": "dark",
"updateChannel": "stable",
"updatePolicy": "prompt",
},
})
if recorder.Code != http.StatusBadRequest {
t.Fatalf("status = %d, want %d; body: %s", recorder.Code, http.StatusBadRequest, recorder.Body.String())
}
assertGeneratedRouteError(
t,
recorder,
tuttigenerated.InvalidRequest,
"missing_desktop_agent_dock_layout",
"desktop agent dock layout is required",
)
}