- Added internal/vault/client.go for creating a Vault client with configuration settings. - Introduced internal/vault/errors.go to classify Vault API errors for better UI handling. - Created internal/vault/kv.go to manage KV secrets, including listing, reading, writing, and deleting operations. - Implemented internal/vault/mounts.go to list and describe secret engine mounts. - Developed internal/vault/service.go to provide a unified entry point for Vault operations. - Added internal/vault/kv_test.go for comprehensive testing of KV operations. - Introduced internal/ui/toast.go for transient notifications in the UI. - Added renovate.json for dependency management and updates.
148 lines
4.4 KiB
Go
148 lines
4.4 KiB
Go
package config
|
|
|
|
import (
|
|
"testing"
|
|
)
|
|
|
|
func envFrom(m map[string]string) Getenv {
|
|
return func(k string) (string, bool) {
|
|
v, ok := m[k]
|
|
return v, ok
|
|
}
|
|
}
|
|
|
|
func TestResolvePrecedence_Address(t *testing.T) {
|
|
f := &File{
|
|
Version: SchemaVersion,
|
|
CurrentProfile: "p1",
|
|
Defaults: Profile{Address: "https://defaults.example.com"},
|
|
Profiles: map[string]*Profile{
|
|
"p1": {Address: "https://profile.example.com"},
|
|
},
|
|
}
|
|
|
|
cases := []struct {
|
|
name string
|
|
ov Overrides
|
|
env map[string]string
|
|
want string
|
|
}{
|
|
{"builtin only", Overrides{}, nil, "https://profile.example.com"}, // profile beats defaults
|
|
{"env beats profile", Overrides{}, map[string]string{EnvAddress: "https://env.example.com"}, "https://env.example.com"},
|
|
{"flag beats env", Overrides{Address: "https://flag.example.com"}, map[string]string{EnvAddress: "https://env.example.com"}, "https://flag.example.com"},
|
|
}
|
|
|
|
for _, c := range cases {
|
|
t.Run(c.name, func(t *testing.T) {
|
|
s, err := Resolve(f, "", c.ov, envFrom(c.env))
|
|
if err != nil {
|
|
t.Fatalf("Resolve: %v", err)
|
|
}
|
|
if s.Address != c.want {
|
|
t.Errorf("Address = %q, want %q", s.Address, c.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestResolve_DefaultsOnlyWhenNoProfile(t *testing.T) {
|
|
f := &File{Version: SchemaVersion, Defaults: Profile{Address: "https://defaults.example.com"}}
|
|
s, err := Resolve(f, "", Overrides{}, envFrom(nil))
|
|
if err != nil {
|
|
t.Fatalf("Resolve: %v", err)
|
|
}
|
|
if s.Address != "https://defaults.example.com" {
|
|
t.Errorf("Address = %q, want defaults value", s.Address)
|
|
}
|
|
}
|
|
|
|
func TestResolve_BuiltinFallback(t *testing.T) {
|
|
f := &File{Version: SchemaVersion}
|
|
s, err := Resolve(f, "", Overrides{}, envFrom(nil))
|
|
if err != nil {
|
|
t.Fatalf("Resolve: %v", err)
|
|
}
|
|
if s.Address != "https://127.0.0.1:8200" {
|
|
t.Errorf("Address = %q, want builtin default", s.Address)
|
|
}
|
|
if s.OriginOf("address").Layer != LayerBuiltin {
|
|
t.Errorf("origin layer = %q, want %q", s.OriginOf("address").Layer, LayerBuiltin)
|
|
}
|
|
}
|
|
|
|
func TestResolve_IgnoreEnvPerProfile(t *testing.T) {
|
|
ignore := true
|
|
f := &File{
|
|
Version: SchemaVersion,
|
|
Profiles: map[string]*Profile{
|
|
"staging": {Address: "https://staging.example.com", IgnoreEnv: &ignore},
|
|
},
|
|
}
|
|
s, err := Resolve(f, "staging", Overrides{}, envFrom(map[string]string{EnvAddress: "https://env.example.com"}))
|
|
if err != nil {
|
|
t.Fatalf("Resolve: %v", err)
|
|
}
|
|
if s.Address != "https://staging.example.com" {
|
|
t.Errorf("Address = %q, want profile value (env should be ignored)", s.Address)
|
|
}
|
|
}
|
|
|
|
func TestResolve_NoEnvOverrideFlag(t *testing.T) {
|
|
f := &File{
|
|
Version: SchemaVersion,
|
|
Profiles: map[string]*Profile{
|
|
"p": {Address: "https://profile.example.com"},
|
|
},
|
|
}
|
|
s, err := Resolve(f, "p", Overrides{NoEnv: true}, envFrom(map[string]string{EnvAddress: "https://env.example.com"}))
|
|
if err != nil {
|
|
t.Fatalf("Resolve: %v", err)
|
|
}
|
|
if s.Address != "https://profile.example.com" {
|
|
t.Errorf("Address = %q, want profile value (--no-env should suppress VAULT_ADDR)", s.Address)
|
|
}
|
|
}
|
|
|
|
func TestSelectProfile(t *testing.T) {
|
|
multi := &File{Profiles: map[string]*Profile{"a": {}, "b": {}}}
|
|
if _, err := SelectProfile(multi, "", envFrom(nil)); err == nil {
|
|
t.Error("expected error selecting among multiple profiles with no selection")
|
|
}
|
|
if name, err := SelectProfile(multi, "a", envFrom(nil)); err != nil || name != "a" {
|
|
t.Errorf("flag selection: got (%q, %v)", name, err)
|
|
}
|
|
if name, err := SelectProfile(multi, "", envFrom(map[string]string{EnvTUIProfile: "b"})); err != nil || name != "b" {
|
|
t.Errorf("env selection: got (%q, %v)", name, err)
|
|
}
|
|
|
|
single := &File{Profiles: map[string]*Profile{"only": {}}}
|
|
if name, err := SelectProfile(single, "", envFrom(nil)); err != nil || name != "only" {
|
|
t.Errorf("sole profile selection: got (%q, %v)", name, err)
|
|
}
|
|
|
|
empty := &File{}
|
|
if name, err := SelectProfile(empty, "", envFrom(nil)); err != nil || name != "default" {
|
|
t.Errorf("empty profiles: got (%q, %v)", name, err)
|
|
}
|
|
}
|
|
|
|
func TestResolve_ReadOnlyDefaultsSafe(t *testing.T) {
|
|
f := &File{Version: SchemaVersion}
|
|
s, err := Resolve(f, "", Overrides{}, envFrom(nil))
|
|
if err != nil {
|
|
t.Fatalf("Resolve: %v", err)
|
|
}
|
|
if !s.ReadOnly {
|
|
t.Error("ReadOnly should default to true when nothing overrides it")
|
|
}
|
|
|
|
no := false
|
|
s, err = Resolve(f, "", Overrides{ReadOnly: &no}, envFrom(nil))
|
|
if err != nil {
|
|
t.Fatalf("Resolve: %v", err)
|
|
}
|
|
if s.ReadOnly {
|
|
t.Error("--write (ReadOnly=false override) should have taken effect")
|
|
}
|
|
}
|