package config import ( "os" "path/filepath" "testing" ) func writeConfig(t *testing.T, content string) string { t.Helper() dir := t.TempDir() path := filepath.Join(dir, "netssh.yaml") if err := os.WriteFile(path, []byte(content), 0o600); err != nil { t.Fatal(err) } return dir } func loadFromDir(t *testing.T, dir string) *Config { t.Helper() // Override UserConfigDir by pointing viper at our temp dir via env isn't // straightforward, so we exercise Load() by temporarily changing XDG_CONFIG_HOME. orig := os.Getenv("XDG_CONFIG_HOME") os.Setenv("XDG_CONFIG_HOME", dir) t.Cleanup(func() { os.Setenv("XDG_CONFIG_HOME", orig) }) cfg, err := Load() if err != nil { t.Fatalf("Load: %v", err) } return cfg } func TestLoad_V2TokenVersion_Preserved(t *testing.T) { dir := writeConfig(t, ` netbox: url: "https://netbox.example.com" token: "nbt_abc123" token_version: 2 `) cfg := loadFromDir(t, dir) if cfg.NetBox.TokenVersion != 2 { t.Errorf("TokenVersion: got %d, want 2", cfg.NetBox.TokenVersion) } } func TestLoad_V1TokenVersion_Preserved(t *testing.T) { dir := writeConfig(t, ` netbox: url: "https://netbox.example.com" token: "legacyhex123" token_version: 1 `) cfg := loadFromDir(t, dir) if cfg.NetBox.TokenVersion != 1 { t.Errorf("TokenVersion: got %d, want 1", cfg.NetBox.TokenVersion) } } func TestLoad_AutoDetectsV2_WhenFieldMissing(t *testing.T) { dir := writeConfig(t, ` netbox: url: "https://netbox.example.com" token: "nbt_mytoken" `) cfg := loadFromDir(t, dir) if cfg.NetBox.TokenVersion != 2 { t.Errorf("TokenVersion: got %d, want 2 (auto-detected from nbt_ prefix)", cfg.NetBox.TokenVersion) } } func TestLoad_AutoDetectsV1_WhenFieldMissing(t *testing.T) { dir := writeConfig(t, ` netbox: url: "https://netbox.example.com" token: "abc123def456" `) cfg := loadFromDir(t, dir) if cfg.NetBox.TokenVersion != 1 { t.Errorf("TokenVersion: got %d, want 1 (auto-detected from plain token)", cfg.NetBox.TokenVersion) } } func TestLoad_TokenVersionZero_WhenNoToken(t *testing.T) { dir := writeConfig(t, ` netbox: url: "https://netbox.example.com" `) cfg := loadFromDir(t, dir) if cfg.NetBox.TokenVersion != 0 { t.Errorf("TokenVersion: got %d, want 0 (no token present)", cfg.NetBox.TokenVersion) } } func TestLoad_Defaults(t *testing.T) { dir := writeConfig(t, ` netbox: url: "https://netbox.example.com" token: "nbt_x" `) cfg := loadFromDir(t, dir) if cfg.Cache.TTL != 3600 { t.Errorf("default cache.ttl: got %d, want 3600", cfg.Cache.TTL) } if cfg.Cache.Path == "" { t.Error("cache.path should be auto-set when empty") } } func TestLoad_MissingFile_ReturnsEmptyConfig(t *testing.T) { orig := os.Getenv("XDG_CONFIG_HOME") os.Setenv("XDG_CONFIG_HOME", t.TempDir()) // dir exists but no netssh.yaml defer os.Setenv("XDG_CONFIG_HOME", orig) cfg, err := Load() if err != nil { t.Fatalf("Load on missing file should not error: %v", err) } if cfg.NetBox.URL != "" { t.Errorf("expected empty URL, got %q", cfg.NetBox.URL) } } func TestLoad_InvalidYAML_ReturnsError(t *testing.T) { dir := writeConfig(t, "not: valid: yaml: [[[") orig := os.Getenv("XDG_CONFIG_HOME") os.Setenv("XDG_CONFIG_HOME", dir) defer os.Setenv("XDG_CONFIG_HOME", orig) _, err := Load() if err == nil { t.Error("invalid YAML should return an error") } } func TestPath_ReturnsNonEmpty(t *testing.T) { p := Path() if p == "" { t.Error("Path() should return a non-empty string") } if filepath.Base(p) != "netssh.yaml" { t.Errorf("Path() base: got %q, want netssh.yaml", filepath.Base(p)) } } func TestEnvironmentOverridesWithoutConfigKeys(t *testing.T) { t.Setenv("XDG_CONFIG_HOME", t.TempDir()) t.Setenv("NETSSH_NETBOX_URL", "https://env.example") t.Setenv("NETSSH_NETBOX_TOKEN", "nbt_env") t.Setenv("NETSSH_SSH_DEFAULT_PORT", "2222") cfg, err := Load() if err != nil { t.Fatal(err) } if cfg.NetBox.URL != "https://env.example" || cfg.NetBox.Token != "nbt_env" || cfg.NetBox.TokenVersion != 2 || cfg.SSH.DefaultPort != 2222 { t.Fatalf("env ignored: %+v", cfg) } } func TestCommandAliasEnvironmentAndValidation(t *testing.T) { t.Setenv("XDG_CONFIG_HOME", t.TempDir()) t.Setenv("NETSSH_SHELL_ALIAS", "j") cfg, err := Load() if err != nil || cfg.Shell.Alias != "j" { t.Fatalf("alias env: %+v, %v", cfg, err) } for _, name := range []string{"netssh", "if", "alias", "j;touch /tmp/bad", "a b", "1bad"} { t.Setenv("NETSSH_SHELL_ALIAS", name) if _, err := Load(); err == nil { t.Errorf("unsafe alias accepted: %q", name) } } for _, name := range []string{"", "j", "jump_server", "goSSH"} { if err := ValidateCommandAlias(name); err != nil { t.Errorf("valid alias rejected: %s: %v", name, err) } } }