package config import ( "os" "path/filepath" "strings" "testing" ) const validConfig = ` listen = "127.0.0.1:9999" [auth] token_ref = "mpk-self" [refs."mpk-self"] backend = "file" source = "/tmp/keyproxy.env" key = "KEYPROXY_TOKEN" [refs."mpk-example"] backend = "file" source = "/tmp/example.env" key = "EXAMPLE_API_KEY" [refs."mpk-example-env"] backend = "env" source = "EXAMPLE_API_KEY" [refs."mpk-stub-bw"] backend = "bitwarden" source = "sm://p/example" key = "EXAMPLE_API_KEY" [refs."mpk-stub-vault"] backend = "vault" ` func writeConfig(t *testing.T, content string) string { t.Helper() path := filepath.Join(t.TempDir(), "keyproxy.toml") if err := os.WriteFile(path, []byte(content), 0o600); err != nil { t.Fatal(err) } return path } func TestLoadValid(t *testing.T) { cfg, err := Load(writeConfig(t, validConfig)) if err != nil { t.Fatalf("unexpected error: %v", err) } if cfg.Listen != "127.0.0.1:9999" { t.Fatalf("listen = %q", cfg.Listen) } if cfg.AuthTokenRef != "mpk-self" { t.Fatalf("auth token ref = %q", cfg.AuthTokenRef) } if got := strings.Join(cfg.RefNames, ","); got != "mpk-example,mpk-example-env,mpk-self,mpk-stub-bw,mpk-stub-vault" { t.Fatalf("ref names = %q", got) } file := cfg.Refs["mpk-example"] if file.Backend != "file" || file.Source != "/tmp/example.env" || file.Key != "EXAMPLE_API_KEY" || file.Mode != "0600" { t.Fatalf("file ref parsed wrong: %+v", file) } if cfg.Refs["mpk-stub-bw"].Backend != "bitwarden" { t.Fatalf("stub ref parsed wrong: %+v", cfg.Refs["mpk-stub-bw"]) } } func TestLoadDefaults(t *testing.T) { cfg, err := Load(writeConfig(t, strings.Replace(validConfig, `listen = "127.0.0.1:9999"`, "", 1))) if err != nil { t.Fatalf("unexpected error: %v", err) } if cfg.Listen != DefaultListen { t.Fatalf("default listen = %q, want %q", cfg.Listen, DefaultListen) } } func TestLoadErrors(t *testing.T) { tests := []struct { name string mutate func(string) string wantErr string }{ { name: "missing file", mutate: func(string) string { return "" }, wantErr: "read config", }, { name: "bad ref name", mutate: func(s string) string { return strings.Replace(s, `mpk-example"`, `mpk_ExAMPLE"`, 1) }, wantErr: "must match mpk-", }, { name: "unknown backend", mutate: func(s string) string { return strings.Replace(s, `backend = "env"`, `backend = "s3"`, 1) }, wantErr: "backend must be one of", }, { name: "file ref without source", mutate: func(s string) string { return strings.Replace(s, `source = "/tmp/example.env"`, "", 1) }, wantErr: "file backend requires source", }, { name: "file ref without key", mutate: func(s string) string { return strings.Replace(s, `key = "EXAMPLE_API_KEY"`, "", 1) }, wantErr: "file backend requires key", }, { name: "file ref with junk mode", mutate: func(s string) string { return strings.Replace(s, `key = "EXAMPLE_API_KEY"`, `key = "EXAMPLE_API_KEY"`+"\n"+`mode = "readable"`, 1) }, wantErr: "mode must be an octal", }, { name: "env ref with key", mutate: func(s string) string { return strings.Replace(s, `source = "EXAMPLE_API_KEY"`, `source = "EXAMPLE_API_KEY"`+"\n"+`key = "X"`, 1) }, wantErr: "env backend must not set key", }, { name: "env ref with mode", mutate: func(s string) string { return strings.Replace(s, `source = "EXAMPLE_API_KEY"`, `source = "EXAMPLE_API_KEY"`+"\n"+`mode = "0644"`, 1) }, wantErr: "mode applies to the file backend only", }, { name: "unknown key inside ref table", mutate: func(s string) string { return strings.Replace(s, `key = "EXAMPLE_API_KEY"`, `key = "EXAMPLE_API_KEY"`+"\n"+`ttl = "5m"`, 1) }, wantErr: "unknown key", }, { name: "no auth section", mutate: func(s string) string { return strings.Replace(s, "token_ref = \"mpk-self\"", "", 1) }, wantErr: "token_ref is required", }, { name: "auth ref not configured", mutate: func(s string) string { return strings.Replace(s, `token_ref = "mpk-self"`, `token_ref = "mpk-ghost"`, 1) }, wantErr: "not a configured ref", }, { name: "auth ref not file backend", mutate: func(s string) string { return strings.Replace(s, `token_ref = "mpk-self"`, `token_ref = "mpk-example-env"`, 1) }, wantErr: "must use the file backend", }, { name: "no refs at all", mutate: func(s string) string { return "listen = \"127.0.0.1:9999\"\n[auth]\ntoken_ref = \"mpk-self\"\n" }, wantErr: "no refs configured", }, { name: "unknown top-level key", mutate: func(s string) string { return "ttl = \"5m\"\n" + s }, wantErr: "unknown top-level key", }, { name: "unknown auth key", mutate: func(s string) string { return strings.Replace(s, "token_ref = \"mpk-self\"", "token_ref = \"mpk-self\"\nrotation = \"7d\"", 1) }, wantErr: "unknown key", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { content := tt.mutate(validConfig) path := writeConfig(t, content) if content == "" { path = filepath.Join(t.TempDir(), "absent.toml") } cfg, err := Load(path) if err == nil { t.Fatalf("want error %q, got config %+v", tt.wantErr, cfg) } if !strings.Contains(err.Error(), tt.wantErr) { t.Fatalf("error %q does not contain %q", err, tt.wantErr) } }) } } func TestValidRef(t *testing.T) { valid := []string{"mpk-a", "mpk-redmine", "mpk-a-1", "mpk-9"} invalid := []string{"", "redmine", "MPK-a", "mpk-", "mpk--a", "mpk_a", "mpk-a_b", "mpk-a.b", "mpk-a b", "sk-live-pasted-secret"} for _, r := range valid { if !ValidRef(r) { t.Errorf("ValidRef(%q) = false, want true", r) } } for _, r := range invalid { if ValidRef(r) { t.Errorf("ValidRef(%q) = true, want false", r) } } }