package config import ( "context" "encoding/json" "io" "net/http" "net/http/httptest" "strings" "sync/atomic" "testing" ) // fakeKeyProxy mimics ukrrs/mopac-keyproxy POST /v1/resolve: bearer auth, // {"ref":"mpk-"} in, {"value":...} out. type fakeKeyProxy struct { t *testing.T token string resolve func(ref string) (string, int) hits atomic.Int64 srv *httptest.Server } func newFakeKeyProxy(t *testing.T, token string) *fakeKeyProxy { t.Helper() f := &fakeKeyProxy{t: t, token: token} f.resolve = func(ref string) (string, int) { return "value-for-" + ref, 200 } f.srv = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/v1/resolve" { http.NotFound(w, r) return } if got := r.Header.Get("Authorization"); got != "Bearer "+f.token { http.Error(w, `{"error":"unauthorized"}`, http.StatusUnauthorized) return } raw, _ := io.ReadAll(io.LimitReader(r.Body, 1<<20)) var body struct { Ref string `json:"ref"` } if err := json.Unmarshal(raw, &body); err != nil || body.Ref == "" { http.Error(w, `{"error":"bad_request"}`, http.StatusBadRequest) return } f.hits.Add(1) value, code := f.resolve(body.Ref) w.Header().Set("Content-Type", "application/json") w.WriteHeader(code) w.Write([]byte(`{"value":"` + value + `"}`)) })) t.Cleanup(f.srv.Close) return f } func TestKeyResolverMPK(t *testing.T) { kp := newFakeKeyProxy(t, "kp-token") cfg := Default() cfg.KeyProxy.URL = kp.srv.URL cfg.KeyProxy.TokenRef = "literal:kp-token" r := NewKeyResolver(cfg) // Auto-prefix: mpk:redmine -> ref "mpk-redmine" on the wire. v, err := r.Resolve(context.Background(), "mpk:redmine") if err != nil { t.Fatalf("Resolve: %v", err) } if v != "value-for-mpk-redmine" { t.Errorf("value = %q", v) } // Explicit full form resolves identically (cache hit, no new call). v, err = r.Resolve(context.Background(), "mpk:mpk-redmine") if err != nil || v != "value-for-mpk-redmine" { t.Errorf("full form = %q err=%v", v, err) } if n := kp.hits.Load(); n != 1 { t.Errorf("keyproxy hits = %d, want 1 (cache must absorb the repeat)", n) } // A different ref is a fresh call. if _, err := r.Resolve(context.Background(), "mpk:litellm"); err != nil { t.Fatalf("Resolve litellm: %v", err) } if n := kp.hits.Load(); n != 2 { t.Errorf("keyproxy hits = %d, want 2", n) } } func TestKeyResolverLocalRefsStillWork(t *testing.T) { kp := newFakeKeyProxy(t, "kp-token") cfg := Default() cfg.KeyProxy.URL = kp.srv.URL cfg.KeyProxy.TokenRef = "literal:kp-token" r := NewKeyResolver(cfg) t.Setenv("HARNESS_KP_TEST", "envval") for ref, want := range map[string]string{ "env:HARNESS_KP_TEST": "envval", "literal:abc": "abc", } { if v, err := r.Resolve(context.Background(), ref); err != nil || v != want { t.Errorf("Resolve(%q) = %q err=%v, want %q", ref, v, err, want) } } if n := kp.hits.Load(); n != 0 { t.Errorf("local refs must not touch keyproxy, hits = %d", n) } } func TestKeyResolverErrors(t *testing.T) { kp := newFakeKeyProxy(t, "kp-token") // No [keyproxy] url: clear error pointing at the config. cfg := Default() r := NewKeyResolver(cfg) _, err := r.Resolve(context.Background(), "mpk:redmine") if err == nil || !strings.Contains(err.Error(), "no [keyproxy] url") { t.Errorf("no-url error = %v", err) } // Bad token: 401 surfaces, the presented token never does. cfg = Default() cfg.KeyProxy.URL = kp.srv.URL cfg.KeyProxy.TokenRef = "literal:wrong-token" r = NewKeyResolver(cfg) _, err = r.Resolve(context.Background(), "mpk:redmine") if err == nil || !strings.Contains(err.Error(), "401") { t.Errorf("401 error = %v", err) } if strings.Contains(err.Error(), "wrong-token") { t.Errorf("error leaks token: %v", err) } // Unknown ref: 404 with fixed body. cfg = Default() cfg.KeyProxy.URL = kp.srv.URL cfg.KeyProxy.TokenRef = "literal:kp-token" kp.resolve = func(ref string) (string, int) { return "", 404 } r = NewKeyResolver(cfg) _, err = r.Resolve(context.Background(), "mpk:nope") if err == nil || !strings.Contains(err.Error(), "404") { t.Errorf("404 error = %v", err) } // Hop down: transport error, not a hang or panic. cfg = Default() cfg.KeyProxy.URL = "http://127.0.0.1:1" cfg.KeyProxy.TokenRef = "literal:kp-token" r = NewKeyResolver(cfg) _, err = r.Resolve(context.Background(), "mpk:redmine") if err == nil { t.Errorf("dead hop should error") } }