package client import ( "io" "net/http" "net/http/httptest" "strings" "testing" ) func TestIsLikelyJWT(t *testing.T) { cases := []struct { in string want bool why string }{ // Real JWT minted by an HS256 signer (header `{"alg":"HS256","typ":"JWT"}`). {"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIn0.signature", true, "valid JWT shape"}, // HF backend api-keys are 64-char hex from /users/{id}/apikey. {"f654c3ff0bbc09e6a22294dfbbbff371a4550366849f59de68ddf064742831a0", false, "hex api-key"}, // Fabric api-keys carry a fak_ prefix. {"fak_30791357ca11ac2ff963999bf265f6a5f240593eb01c06fc", false, "fabric api-key"}, // eyJ prefix without three segments isn't a JWT. {"eyJabc", false, "prefix only"}, {"eyJabc.def", false, "two segments"}, // Empty / nonsense. {"", false, "empty"}, {"....", false, "dots only"}, } for _, c := range cases { if got := isLikelyJWT(c.in); got != c.want { t.Errorf("isLikelyJWT(%q) = %v, want %v (%s)", c.in, got, c.want, c.why) } } } func TestNewAutoSelectsAuthHeader(t *testing.T) { // Capture which auth header reaches the server for each token shape. var lastReq *http.Request srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { lastReq = r w.WriteHeader(http.StatusOK) _, _ = io.WriteString(w, "{}") })) defer srv.Close() // api-key path: should go via X-API-Key, NOT Authorization. apiKey := "f654c3ff0bbc09e6a22294dfbbbff371a4550366849f59de68ddf064742831a0" if _, err := New(srv.URL, apiKey).Get("/anything"); err != nil { t.Fatalf("api-key call failed: %v", err) } if got := lastReq.Header.Get("X-API-Key"); got != apiKey { t.Errorf("api-key not sent as X-API-Key (got %q)", got) } if got := lastReq.Header.Get("Authorization"); got != "" { t.Errorf("api-key leaked into Authorization header (got %q)", got) } // JWT path: should go via Authorization: Bearer, NOT X-API-Key. jwt := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIn0.signature" if _, err := New(srv.URL, jwt).Get("/anything"); err != nil { t.Fatalf("jwt call failed: %v", err) } if got := lastReq.Header.Get("Authorization"); !strings.HasPrefix(got, "Bearer ") || !strings.HasSuffix(got, jwt) { t.Errorf("jwt not sent as Bearer (got %q)", got) } if got := lastReq.Header.Get("X-API-Key"); got != "" { t.Errorf("jwt leaked into X-API-Key header (got %q)", got) } // Empty token: neither header set. if _, err := New(srv.URL, "").Get("/anything"); err != nil { t.Fatalf("empty-token call failed: %v", err) } if got := lastReq.Header.Get("Authorization"); got != "" { t.Errorf("empty token set Authorization (got %q)", got) } if got := lastReq.Header.Get("X-API-Key"); got != "" { t.Errorf("empty token set X-API-Key (got %q)", got) } } func TestNewWithAPIKeyAlwaysUsesAPIKeyHeader(t *testing.T) { // Even if someone passes a JWT-shaped string via NewWithAPIKey, it must // still go via X-API-Key — the explicit constructor wins. var lastReq *http.Request srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { lastReq = r w.WriteHeader(http.StatusOK) _, _ = io.WriteString(w, "{}") })) defer srv.Close() jwtShape := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIn0.signature" if _, err := NewWithAPIKey(srv.URL, jwtShape).Get("/anything"); err != nil { t.Fatalf("call failed: %v", err) } if got := lastReq.Header.Get("X-API-Key"); got != jwtShape { t.Errorf("NewWithAPIKey didn't use X-API-Key (got %q)", got) } if got := lastReq.Header.Get("Authorization"); got != "" { t.Errorf("NewWithAPIKey set Authorization (got %q)", got) } }