package config_matrix import ( "os" "path/filepath" "strings" "testing" "github.com/chenhg5/cc-connect/config" ) func writeConfig(t *testing.T, body string) string { t.Helper() path := filepath.Join(t.TempDir(), "config.toml") if err := os.WriteFile(path, []byte(body), 0o644); err != nil { t.Fatalf("write config: %v", err) } return path } func baseProjectTOML(extra string) string { return ` data_dir = "` + filepath.ToSlash(os.TempDir()) + `/cc-connect-release-test" ` + extra + ` [[projects]] name = "release" [projects.agent] type = "claudecode" work_dir = "/tmp/cc-connect-release-work" [[projects.platforms]] type = "feishu" app_id = "cli_release" app_secret = "secret" ` } func TestReleaseConfig_ProjectDisplayOverridesGlobalFromLoadedConfig(t *testing.T) { path := writeConfig(t, ` attachment_send = "off" [display] mode = "quiet" card_mode = "rich" thinking_messages = true tool_messages = false [[projects]] name = "release" reset_on_idle_mins = 0 [projects.display] mode = "full" card_mode = "legacy" thinking_messages = false tool_messages = true thinking_max_len = 111 tool_max_len = 222 [projects.agent] type = "claudecode" work_dir = "/tmp/cc-connect-release-work" [[projects.platforms]] type = "feishu" app_id = "cli_release" app_secret = "secret" `) cfg, err := config.Load(path) if err != nil { t.Fatalf("Load() error = %v", err) } if cfg.AttachmentSend != "off" { t.Fatalf("AttachmentSend = %q, want off", cfg.AttachmentSend) } if len(cfg.Projects) != 1 { t.Fatalf("projects = %d, want 1", len(cfg.Projects)) } proj := &cfg.Projects[0] if proj.ResetOnIdleMins == nil || *proj.ResetOnIdleMins != 0 { t.Fatalf("ResetOnIdleMins = %#v, want explicit 0", proj.ResetOnIdleMins) } mode, thinking, tools, thinkingMax, toolMax, _, _ := config.EffectiveDisplay(cfg, proj) if mode != config.DisplayModeFull { t.Fatalf("mode = %q, want project full override", mode) } if thinking { t.Fatal("thinking_messages = true, want project override false") } if !tools { t.Fatal("tool_messages = false, want project override true") } if thinkingMax != 111 || toolMax != 222 { t.Fatalf("max lens = %d/%d, want 111/222", thinkingMax, toolMax) } if got := config.EffectiveCardMode(cfg, proj); got != "legacy" { t.Fatalf("card mode = %q, want project legacy override", got) } } func TestReleaseConfig_DefaultsKeepAttachmentsAndFullDisplayEnabled(t *testing.T) { path := writeConfig(t, baseProjectTOML("")) cfg, err := config.Load(path) if err != nil { t.Fatalf("Load() error = %v", err) } if cfg.AttachmentSend != "on" { t.Fatalf("AttachmentSend = %q, want default on", cfg.AttachmentSend) } mode, thinking, tools, _, _, _, _ := config.EffectiveDisplay(cfg, &cfg.Projects[0]) if mode != config.DisplayModeFull || !thinking || !tools { t.Fatalf("display = mode:%s thinking:%v tools:%v, want full/true/true", mode, thinking, tools) } if got := config.EffectiveCardMode(cfg, &cfg.Projects[0]); got != "legacy" { t.Fatalf("card mode = %q, want default legacy", got) } } func TestReleaseConfig_BehaviorControlSwitchesParseFromLoadedConfig(t *testing.T) { path := writeConfig(t, ` [stream_preview] enabled = false disabled_platforms = ["feishu", "telegram"] interval_ms = 250 min_delta_chars = 12 max_chars = 777 [[projects]] name = "release" show_context_indicator = false reply_footer = false disabled_commands = ["restart", "shell"] [projects.display] mode = "quiet" card_mode = "rich" thinking_messages = false tool_messages = false [projects.agent] type = "claudecode" work_dir = "/tmp/cc-connect-release-work" [[projects.platforms]] type = "feishu" app_id = "cli_release" app_secret = "secret" `) cfg, err := config.Load(path) if err != nil { t.Fatalf("Load() error = %v", err) } if cfg.StreamPreview.Enabled == nil || *cfg.StreamPreview.Enabled { t.Fatalf("stream_preview.enabled = %#v, want false", cfg.StreamPreview.Enabled) } if got := strings.Join(cfg.StreamPreview.DisabledPlatforms, ","); got != "feishu,telegram" { t.Fatalf("stream_preview.disabled_platforms = %#v", cfg.StreamPreview.DisabledPlatforms) } if cfg.StreamPreview.IntervalMs == nil || *cfg.StreamPreview.IntervalMs != 250 { t.Fatalf("stream_preview.interval_ms = %#v, want 250", cfg.StreamPreview.IntervalMs) } if cfg.StreamPreview.MinDeltaChars == nil || *cfg.StreamPreview.MinDeltaChars != 12 { t.Fatalf("stream_preview.min_delta_chars = %#v, want 12", cfg.StreamPreview.MinDeltaChars) } if cfg.StreamPreview.MaxChars == nil || *cfg.StreamPreview.MaxChars != 777 { t.Fatalf("stream_preview.max_chars = %#v, want 777", cfg.StreamPreview.MaxChars) } proj := &cfg.Projects[0] if proj.ShowContextIndicator == nil || *proj.ShowContextIndicator { t.Fatalf("show_context_indicator = %#v, want false", proj.ShowContextIndicator) } if proj.ReplyFooter == nil || *proj.ReplyFooter { t.Fatalf("reply_footer = %#v, want false", proj.ReplyFooter) } if strings.Join(proj.DisabledCommands, ",") != "restart,shell" { t.Fatalf("disabled_commands = %#v", proj.DisabledCommands) } mode, thinking, tools, _, _, _, _ := config.EffectiveDisplay(cfg, proj) if mode != config.DisplayModeQuiet || thinking || tools { t.Fatalf("display = mode:%s thinking:%v tools:%v, want quiet/false/false", mode, thinking, tools) } if got := config.EffectiveCardMode(cfg, proj); got != "rich" { t.Fatalf("card mode = %q, want rich", got) } } func TestReleaseConfig_InvalidCriticalOptionsFailFast(t *testing.T) { tests := []struct { name string toml string wantErr string }{ { name: "invalid attachment send", toml: baseProjectTOML(` attachment_send = "maybe" `), wantErr: `attachment_send must be "on" or "off"`, }, { name: "invalid project display mode", toml: ` [[projects]] name = "release" [projects.display] mode = "verbose" [projects.agent] type = "claudecode" work_dir = "/tmp/cc-connect-release-work" [[projects.platforms]] type = "feishu" app_id = "cli_release" app_secret = "secret" `, wantErr: `projects[0].display.mode must be "full", "compact", or "quiet"`, }, { name: "negative reset on idle", toml: ` [[projects]] name = "release" reset_on_idle_mins = -1 [projects.agent] type = "claudecode" work_dir = "/tmp/cc-connect-release-work" [[projects.platforms]] type = "feishu" app_id = "cli_release" app_secret = "secret" `, wantErr: "reset_on_idle_mins must be >= 0", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { _, err := config.Load(writeConfig(t, tt.toml)) if err == nil { t.Fatal("Load() error = nil, want validation error") } if !strings.Contains(err.Error(), tt.wantErr) { t.Fatalf("Load() error = %q, want contains %q", err.Error(), tt.wantErr) } }) } }