package config import ( "os" "path/filepath" "strings" "testing" "gopkg.in/yaml.v3") func TestParseConfigBytes_PluginsDefaults(t *testing.T) { cfg, errParse := ParseConfigBytes([]byte(`plugins: {}`)) if errParse != nil { t.Fatalf("ParseConfigBytes() error = %v", errParse) } if cfg.Plugins.Enabled { t.Fatal("Plugins.Enabled = true, want false") } if cfg.Plugins.Dir != "plugins" { t.Fatalf("Plugins.Dir = %q, want plugins", cfg.Plugins.Dir) } if cfg.Plugins.Configs == nil { t.Fatal("Plugins.Configs = nil, want empty map") } if len(cfg.Plugins.Configs) != 0 { t.Fatalf("len(Plugins.Configs) = %d, want 0", len(cfg.Plugins.Configs)) }} func TestParseConfigBytes_PluginsDirExpandsLeadingTilde(t *testing.T) { homeDir := t.TempDir() t.Setenv("HOME", homeDir) t.Setenv("USERPROFILE", homeDir) cfg, errParse := ParseConfigBytes([]byte(`plugins: dir: "~/.cli-proxy-api/plugins"`)) if errParse != nil { t.Fatalf("ParseConfigBytes() error = %v", errParse) } want := filepath.Join(homeDir, ".cli-proxy-api", "plugins") if cfg.Plugins.Dir != want { t.Fatalf("Plugins.Dir = %q, want %q", cfg.Plugins.Dir, want) }} func TestLoadConfig_PluginsDirExpandsLeadingTilde(t *testing.T) { homeDir := t.TempDir() t.Setenv("HOME", homeDir) t.Setenv("USERPROFILE", homeDir) configPath := filepath.Join(t.TempDir(), "config.yaml") if errWrite := os.WriteFile(configPath, []byte("plugins:\n dir: \"~/.cli-proxy-api/plugins\"\n"), 0o600); errWrite != nil { t.Fatalf("os.WriteFile() error = %v", errWrite) } cfg, errLoad := LoadConfig(configPath) if errLoad != nil { t.Fatalf("LoadConfig() error = %v", errLoad) } want := filepath.Join(homeDir, ".cli-proxy-api", "plugins") if cfg.Plugins.Dir != want { t.Fatalf("Plugins.Dir = %q, want %q", cfg.Plugins.Dir, want) }} func TestParseConfigBytes_PluginStoreSources(t *testing.T) { cfg, errParse := ParseConfigBytes([]byte(`plugins: store-sources: - " https://community.example/registry.json " - ""`)) if errParse != nil { t.Fatalf("ParseConfigBytes() error = %v", errParse) } if len(cfg.Plugins.StoreSources) != 1 { t.Fatalf("Plugins.StoreSources len = %d, want 1", len(cfg.Plugins.StoreSources)) } source := cfg.Plugins.StoreSources[0] if source != "https://community.example/registry.json" { t.Fatalf("Plugins.StoreSources[0] = %#v", source) }} func TestParseConfigBytes_PluginStoreAuth(t *testing.T) { cfg, errParse := ParseConfigBytes([]byte(`plugins: store-auth: - match: " https://plugins.example.com/ " apply-to: ["registry", "artifact", "registry"] type: bearer token-env: " CLIPROXY_PLUGIN_STORE_TOKEN " - match: "" type: bearer`)) if errParse != nil { t.Fatalf("ParseConfigBytes() error = %v", errParse) } if len(cfg.Plugins.StoreAuth) != 1 { t.Fatalf("Plugins.StoreAuth len = %d, want 1", len(cfg.Plugins.StoreAuth)) } auth := cfg.Plugins.StoreAuth[0] if auth.Match != "https://plugins.example.com/" || auth.Type != "bearer" || auth.TokenEnv != "CLIPROXY_PLUGIN_STORE_TOKEN" { t.Fatalf("Plugins.StoreAuth[0] = %#v", auth) } if len(auth.ApplyTo) != 2 || auth.ApplyTo[0] != "registry" || auth.ApplyTo[1] != "artifact" { t.Fatalf("Plugins.StoreAuth[0].ApplyTo = %#v", auth.ApplyTo) }} func TestParseConfigBytes_PluginAuthRevision(t *testing.T) { cfg, errParse := ParseConfigBytes([]byte("plugins:\n auth-revision: 42\n")) if errParse != nil { t.Fatalf("ParseConfigBytes() error = %v", errParse) } if cfg.Plugins.AuthRevision != 42 { t.Fatalf("Plugins.AuthRevision = %d, want 42", cfg.Plugins.AuthRevision) }} func TestParseConfigBytes_PluginInstanceEmptyRawYAML(t *testing.T) { cfg, errParse := ParseConfigBytes([]byte(`plugins: configs: sample: {}`)) if errParse != nil { t.Fatalf("ParseConfigBytes() error = %v", errParse) } plugin, ok := cfg.Plugins.Configs["sample"] if !ok { t.Fatal("Plugins.Configs[\"sample\"] missing") } if plugin.Enabled == nil { t.Fatal("Plugin.Enabled = nil, want false pointer") } if *plugin.Enabled { t.Fatal("Plugin.Enabled = true, want false") } if plugin.Priority != 0 { t.Fatalf("Plugin.Priority = %d, want 0", plugin.Priority) } raw, errMarshal := yaml.Marshal(&plugin.Raw) if errMarshal != nil { t.Fatalf("yaml.Marshal(Raw) error = %v", errMarshal) } rawText := string(raw) if strings.Contains(rawText, "enabled:") { t.Fatalf("Raw YAML contains enabled default:\n%s", rawText) } if strings.Contains(rawText, "priority:") { t.Fatalf("Raw YAML contains priority default:\n%s", rawText) } marshaled, errMarshalPlugin := yaml.Marshal(plugin) if errMarshalPlugin != nil { t.Fatalf("yaml.Marshal(plugin) error = %v", errMarshalPlugin) } marshaledText := string(marshaled) if strings.Contains(marshaledText, "enabled:") { t.Fatalf("Plugin YAML contains enabled default:\n%s", marshaledText) } if strings.Contains(marshaledText, "priority:") { t.Fatalf("Plugin YAML contains priority default:\n%s", marshaledText) }} func TestSaveConfigPreserveComments_PrunesDefaultPluginsDir(t *testing.T) { configPath := filepath.Join(t.TempDir(), "config.yaml") if errWrite := os.WriteFile(configPath, []byte("debug: true\n"), 0o600); errWrite != nil { t.Fatalf("os.WriteFile() error = %v", errWrite) } cfg := &Config{ Debug: true, Plugins: PluginsConfig{ Dir: "plugins", Configs: map[string]PluginInstanceConfig{}, }, } if errSave := SaveConfigPreserveComments(configPath, cfg); errSave != nil { t.Fatalf("SaveConfigPreserveComments() error = %v", errSave) } data, errRead := os.ReadFile(configPath) if errRead != nil { t.Fatalf("os.ReadFile() error = %v", errRead) } text := string(data) if strings.Contains(text, "plugins:") { t.Fatalf("saved config contains plugins default section:\n%s", text) } if strings.Contains(text, "dir: plugins") { t.Fatalf("saved config contains default plugins dir:\n%s", text) }} func TestParseConfigBytes_PluginInstanceRawYAML(t *testing.T) { cfg, errParse := ParseConfigBytes([]byte(`plugins: enabled: true dir: custom-plugins configs: sample: enabled: false priority: 7 config1: value1 config2: nested: value2`)) if errParse != nil { t.Fatalf("ParseConfigBytes() error = %v", errParse) } plugin, ok := cfg.Plugins.Configs["sample"] if !ok { t.Fatal("Plugins.Configs[\"sample\"] missing") } if plugin.Enabled == nil { t.Fatal("Plugin.Enabled = nil, want false pointer") } if *plugin.Enabled { t.Fatal("Plugin.Enabled = true, want false") } if plugin.Priority != 7 { t.Fatalf("Plugin.Priority = %d, want 7", plugin.Priority) } raw, errMarshal := yaml.Marshal(&plugin.Raw) if errMarshal != nil { t.Fatalf("yaml.Marshal(Raw) error = %v", errMarshal) } rawText := string(raw) for _, want := range []string{ "enabled: false", "priority: 7", "config1: value1", "config2:", "nested: value2", } { if !strings.Contains(rawText, want) { t.Fatalf("Raw YAML missing %q in:\n%s", want, rawText) } }}
package config
import (
"os"
"path/filepath"
"strings"
"testing"
"gopkg.in/yaml.v3"
)
func TestParseConfigBytes_PluginsDefaults(t *testing.T) {
cfg, errParse := ParseConfigBytes([]byte(`
plugins: {}
`))
if errParse != nil {
t.Fatalf("ParseConfigBytes() error = %v", errParse)
}
if cfg.Plugins.Enabled {
t.Fatal("Plugins.Enabled = true, want false")
}
if cfg.Plugins.Dir != "plugins" {
t.Fatalf("Plugins.Dir = %q, want plugins", cfg.Plugins.Dir)
}
if cfg.Plugins.Configs == nil {
t.Fatal("Plugins.Configs = nil, want empty map")
}
if len(cfg.Plugins.Configs) != 0 {
t.Fatalf("len(Plugins.Configs) = %d, want 0", len(cfg.Plugins.Configs))
}
}
func TestParseConfigBytes_PluginsDirExpandsLeadingTilde(t *testing.T) {
homeDir := t.TempDir()
t.Setenv("HOME", homeDir)
t.Setenv("USERPROFILE", homeDir)
cfg, errParse := ParseConfigBytes([]byte(`
plugins:
dir: "~/.cli-proxy-api/plugins"
`))
if errParse != nil {
t.Fatalf("ParseConfigBytes() error = %v", errParse)
}
want := filepath.Join(homeDir, ".cli-proxy-api", "plugins")
if cfg.Plugins.Dir != want {
t.Fatalf("Plugins.Dir = %q, want %q", cfg.Plugins.Dir, want)
}
}
func TestLoadConfig_PluginsDirExpandsLeadingTilde(t *testing.T) {
homeDir := t.TempDir()
t.Setenv("HOME", homeDir)
t.Setenv("USERPROFILE", homeDir)
configPath := filepath.Join(t.TempDir(), "config.yaml")
if errWrite := os.WriteFile(configPath, []byte("plugins:\n dir: \"~/.cli-proxy-api/plugins\"\n"), 0o600); errWrite != nil {
t.Fatalf("os.WriteFile() error = %v", errWrite)
}
cfg, errLoad := LoadConfig(configPath)
if errLoad != nil {
t.Fatalf("LoadConfig() error = %v", errLoad)
}
want := filepath.Join(homeDir, ".cli-proxy-api", "plugins")
if cfg.Plugins.Dir != want {
t.Fatalf("Plugins.Dir = %q, want %q", cfg.Plugins.Dir, want)
}
}
func TestParseConfigBytes_PluginStoreSources(t *testing.T) {
cfg, errParse := ParseConfigBytes([]byte(`
plugins:
store-sources:
- " https://community.example/registry.json "
- ""
`))
if errParse != nil {
t.Fatalf("ParseConfigBytes() error = %v", errParse)
}
if len(cfg.Plugins.StoreSources) != 1 {
t.Fatalf("Plugins.StoreSources len = %d, want 1", len(cfg.Plugins.StoreSources))
}
source := cfg.Plugins.StoreSources[0]
if source != "https://community.example/registry.json" {
t.Fatalf("Plugins.StoreSources[0] = %#v", source)
}
}
func TestParseConfigBytes_PluginStoreAuth(t *testing.T) {
cfg, errParse := ParseConfigBytes([]byte(`
plugins:
store-auth:
- match: " https://plugins.example.com/ "
apply-to: ["registry", "artifact", "registry"]
type: bearer
token-env: " CLIPROXY_PLUGIN_STORE_TOKEN "
- match: ""
type: bearer
`))
if errParse != nil {
t.Fatalf("ParseConfigBytes() error = %v", errParse)
}
if len(cfg.Plugins.StoreAuth) != 1 {
t.Fatalf("Plugins.StoreAuth len = %d, want 1", len(cfg.Plugins.StoreAuth))
}
auth := cfg.Plugins.StoreAuth[0]
if auth.Match != "https://plugins.example.com/" || auth.Type != "bearer" || auth.TokenEnv != "CLIPROXY_PLUGIN_STORE_TOKEN" {
t.Fatalf("Plugins.StoreAuth[0] = %#v", auth)
}
if len(auth.ApplyTo) != 2 || auth.ApplyTo[0] != "registry" || auth.ApplyTo[1] != "artifact" {
t.Fatalf("Plugins.StoreAuth[0].ApplyTo = %#v", auth.ApplyTo)
}
}
func TestParseConfigBytes_PluginAuthRevision(t *testing.T) {
cfg, errParse := ParseConfigBytes([]byte("plugins:\n auth-revision: 42\n"))
if errParse != nil {
t.Fatalf("ParseConfigBytes() error = %v", errParse)
}
if cfg.Plugins.AuthRevision != 42 {
t.Fatalf("Plugins.AuthRevision = %d, want 42", cfg.Plugins.AuthRevision)
}
}
func TestParseConfigBytes_PluginInstanceEmptyRawYAML(t *testing.T) {
cfg, errParse := ParseConfigBytes([]byte(`
plugins:
configs:
sample: {}
`))
if errParse != nil {
t.Fatalf("ParseConfigBytes() error = %v", errParse)
}
plugin, ok := cfg.Plugins.Configs["sample"]
if !ok {
t.Fatal("Plugins.Configs[\"sample\"] missing")
}
if plugin.Enabled == nil {
t.Fatal("Plugin.Enabled = nil, want false pointer")
}
if *plugin.Enabled {
t.Fatal("Plugin.Enabled = true, want false")
}
if plugin.Priority != 0 {
t.Fatalf("Plugin.Priority = %d, want 0", plugin.Priority)
}
raw, errMarshal := yaml.Marshal(&plugin.Raw)
if errMarshal != nil {
t.Fatalf("yaml.Marshal(Raw) error = %v", errMarshal)
}
rawText := string(raw)
if strings.Contains(rawText, "enabled:") {
t.Fatalf("Raw YAML contains enabled default:\n%s", rawText)
}
if strings.Contains(rawText, "priority:") {
t.Fatalf("Raw YAML contains priority default:\n%s", rawText)
}
marshaled, errMarshalPlugin := yaml.Marshal(plugin)
if errMarshalPlugin != nil {
t.Fatalf("yaml.Marshal(plugin) error = %v", errMarshalPlugin)
}
marshaledText := string(marshaled)
if strings.Contains(marshaledText, "enabled:") {
t.Fatalf("Plugin YAML contains enabled default:\n%s", marshaledText)
}
if strings.Contains(marshaledText, "priority:") {
t.Fatalf("Plugin YAML contains priority default:\n%s", marshaledText)
}
}
func TestSaveConfigPreserveComments_PrunesDefaultPluginsDir(t *testing.T) {
configPath := filepath.Join(t.TempDir(), "config.yaml")
if errWrite := os.WriteFile(configPath, []byte("debug: true\n"), 0o600); errWrite != nil {
t.Fatalf("os.WriteFile() error = %v", errWrite)
}
cfg := &Config{
Debug: true,
Plugins: PluginsConfig{
Dir: "plugins",
Configs: map[string]PluginInstanceConfig{},
},
}
if errSave := SaveConfigPreserveComments(configPath, cfg); errSave != nil {
t.Fatalf("SaveConfigPreserveComments() error = %v", errSave)
}
data, errRead := os.ReadFile(configPath)
if errRead != nil {
t.Fatalf("os.ReadFile() error = %v", errRead)
}
text := string(data)
if strings.Contains(text, "plugins:") {
t.Fatalf("saved config contains plugins default section:\n%s", text)
}
if strings.Contains(text, "dir: plugins") {
t.Fatalf("saved config contains default plugins dir:\n%s", text)
}
}
func TestParseConfigBytes_PluginInstanceRawYAML(t *testing.T) {
cfg, errParse := ParseConfigBytes([]byte(`
plugins:
enabled: true
dir: custom-plugins
configs:
sample:
enabled: false
priority: 7
config1: value1
config2:
nested: value2
`))
if errParse != nil {
t.Fatalf("ParseConfigBytes() error = %v", errParse)
}
plugin, ok := cfg.Plugins.Configs["sample"]
if !ok {
t.Fatal("Plugins.Configs[\"sample\"] missing")
}
if plugin.Enabled == nil {
t.Fatal("Plugin.Enabled = nil, want false pointer")
}
if *plugin.Enabled {
t.Fatal("Plugin.Enabled = true, want false")
}
if plugin.Priority != 7 {
t.Fatalf("Plugin.Priority = %d, want 7", plugin.Priority)
}
raw, errMarshal := yaml.Marshal(&plugin.Raw)
if errMarshal != nil {
t.Fatalf("yaml.Marshal(Raw) error = %v", errMarshal)
}
rawText := string(raw)
for _, want := range []string{
"enabled: false",
"priority: 7",
"config1: value1",
"config2:",
"nested: value2",
} {
if !strings.Contains(rawText, want) {
t.Fatalf("Raw YAML missing %q in:\n%s", want, rawText)
}
}
}