Files
vault-tools/shared/config_test.go
T
unkin-agent b59a62f038
ci/woodpecker/pr/build Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
Rename vctl to vcm and move config/cache to vcm dirs
2026-10-23 21:55:13 +11:00

178 lines
4.4 KiB
Go

package shared
import (
"os"
"path/filepath"
"testing"
)
// withConfigDir points XDG_CONFIG_HOME at a temp dir and writes the given
// config file into <tmp>/vcm/<name>, returning the temp base.
func withConfigDir(t *testing.T, name, content string) string {
t.Helper()
base := t.TempDir()
t.Setenv("XDG_CONFIG_HOME", base)
dir := filepath.Join(base, appDir)
if err := os.MkdirAll(dir, 0o700); err != nil {
t.Fatal(err)
}
if content != "" {
if err := os.WriteFile(filepath.Join(dir, name), []byte(content), 0o600); err != nil {
t.Fatal(err)
}
}
return base
}
const sampleConfig = `
defaults:
method: ldap
user: ben
contexts:
sydney:
address: https://vault.syd1.au.unkin.net
staging/sydney:
address: https://vault-staging.syd1.au.unkin.net
namespace: staging
user: svc-ben
legacy:
address: https://vault-legacy.example.net
method: userpass
path: userpass2
`
func TestLoadAndContextNames(t *testing.T) {
withConfigDir(t, "config.yaml", sampleConfig)
cfg, err := Load()
if err != nil {
t.Fatalf("Load: %v", err)
}
got := cfg.ContextNames()
want := []string{"legacy", "staging/sydney", "sydney"}
if len(got) != len(want) {
t.Fatalf("ContextNames = %v, want %v", got, want)
}
for i := range want {
if got[i] != want[i] {
t.Fatalf("ContextNames[%d] = %q, want %q (%v)", i, got[i], want[i], got)
}
}
if cfg.Path() == "" {
t.Error("Path() is empty after loading a config")
}
}
func TestConfigPath(t *testing.T) {
base := withConfigDir(t, "config.yaml", sampleConfig)
if got, want := ConfigPath(), filepath.Join(base, appDir, "config.yaml"); got != want {
t.Errorf("ConfigPath = %q, want %q", got, want)
}
}
func TestLoadMissingConfigIsNotError(t *testing.T) {
t.Setenv("XDG_CONFIG_HOME", t.TempDir())
cfg, err := Load()
if err != nil {
t.Fatalf("Load with no file: %v", err)
}
if len(cfg.ContextNames()) != 0 {
t.Errorf("expected no contexts, got %v", cfg.ContextNames())
}
}
func TestResolveAppliesDefaults(t *testing.T) {
withConfigDir(t, "config.yaml", sampleConfig)
cfg, err := Load()
if err != nil {
t.Fatal(err)
}
// sydney: inherits method+user from defaults, no namespace, path == method.
rc, err := cfg.Resolve("sydney")
if err != nil {
t.Fatal(err)
}
if rc.Method != "ldap" || rc.User != "ben" || rc.Namespace != "" || rc.Path != "ldap" {
t.Errorf("sydney resolved = %+v", rc)
}
// staging/sydney: overrides user + namespace, inherits method.
rc, err = cfg.Resolve("staging/sydney")
if err != nil {
t.Fatal(err)
}
if rc.Method != "ldap" || rc.User != "svc-ben" || rc.Namespace != "staging" {
t.Errorf("staging/sydney resolved = %+v", rc)
}
// legacy: explicit method + custom auth path.
rc, err = cfg.Resolve("legacy")
if err != nil {
t.Fatal(err)
}
if rc.Method != "userpass" || rc.Path != "userpass2" {
t.Errorf("legacy resolved = %+v", rc)
}
}
func TestResolveUnknownContext(t *testing.T) {
withConfigDir(t, "config.yaml", sampleConfig)
cfg, _ := Load()
if _, err := cfg.Resolve("nope"); err == nil {
t.Error("expected error for unknown context")
}
}
func TestResolveMethodDefaultWhenUnset(t *testing.T) {
withConfigDir(t, "config.yaml", `
contexts:
bare:
address: https://vault.example.net
`)
t.Setenv("USER", "alice")
cfg, _ := Load()
rc, err := cfg.Resolve("bare")
if err != nil {
t.Fatal(err)
}
if rc.Method != DefaultMethod {
t.Errorf("method = %q, want %q", rc.Method, DefaultMethod)
}
if rc.User != "alice" {
t.Errorf("user = %q, want alice ($USER fallback)", rc.User)
}
}
func TestResolveWithOverrides(t *testing.T) {
withConfigDir(t, "config.yaml", sampleConfig)
cfg, _ := Load()
// Overriding the method also moves the auth path (context did not pin one).
rc, err := cfg.ResolveWithOverrides("sydney", "okta", "otheruser")
if err != nil {
t.Fatal(err)
}
if rc.Method != "okta" || rc.Path != "okta" || rc.User != "otheruser" {
t.Errorf("override resolved = %+v", rc)
}
// legacy pins path=userpass2, so a method override must NOT change the path.
rc, err = cfg.ResolveWithOverrides("legacy", "okta", "")
if err != nil {
t.Fatal(err)
}
if rc.Method != "okta" || rc.Path != "userpass2" {
t.Errorf("pinned-path override resolved = %+v", rc)
}
// Empty overrides leave resolved values untouched.
rc, err = cfg.ResolveWithOverrides("sydney", "", "")
if err != nil {
t.Fatal(err)
}
if rc.Method != "ldap" || rc.User != "ben" {
t.Errorf("no-op override resolved = %+v", rc)
}
}