package auth import ( "context" "net/http" "net/http/httptest" "testing" "time" ) const validJWTForTests = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LXVzZXItMTIzIn0.signature" func TestDeckAuthenticatorRejectsInvalidToken(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusUnauthorized) })) defer server.Close() a := NewDeckAuthenticator(DeckAPIs{"test": server.URL}, server.Client()) if _, err := a.Authenticate(context.Background(), "bad-token", "test"); err == nil { t.Fatal("expected error for invalid token") } } func TestDeckAuthenticatorAcceptsValidJWT(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Header.Get("Authorization") == "" { t.Fatal("expected Authorization header") } w.WriteHeader(http.StatusOK) })) defer server.Close() a := NewDeckAuthenticator(DeckAPIs{"test": server.URL}, server.Client()) identity, err := a.Authenticate(context.Background(), validJWTForTests, "test") if err != nil { t.Fatalf("expected success: %v", err) } if identity.Sub != "test-user-123" { t.Fatalf("unexpected sub: %q", identity.Sub) } if NamespaceForSub(identity.Sub) != NamespaceForSub("test-user-123") { t.Fatalf("unexpected namespace: %q", NamespaceForSub(identity.Sub)) } } func TestDeckAuthenticatorCachesValidToken(t *testing.T) { requests := 0 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { requests++ w.WriteHeader(http.StatusOK) })) defer server.Close() a := NewDeckAuthenticator(DeckAPIs{"test": server.URL}, server.Client()) if _, err := a.Authenticate(context.Background(), validJWTForTests, "test"); err != nil { t.Fatalf("first auth: %v", err) } if _, err := a.Authenticate(context.Background(), validJWTForTests, "test"); err != nil { t.Fatalf("second auth: %v", err) } if requests != 1 { t.Fatalf("expected 1 upstream request (cache), got %d", requests) } // проверяем что кэш выставлен if v, ok := a.tokenCache.Load("test:" + validJWTForTests); !ok || time.Now().After(v.(time.Time)) { t.Fatal("expected token to be cached with future expiry") } } func TestTestAuthenticatorAcceptsJWT(t *testing.T) { a := &TestAuthenticator{} identity, err := a.Authenticate(context.Background(), validJWTForTests, "test") if err != nil { t.Fatalf("expected success: %v", err) } if identity.Sub != "test-user-123" { t.Fatalf("unexpected sub: %q", identity.Sub) } } func TestTestAuthenticatorAcceptsEmail(t *testing.T) { a := &TestAuthenticator{} identity, err := a.Authenticate(context.Background(), "user@example.com", "test") if err != nil { t.Fatalf("expected success: %v", err) } if identity.Sub != "user@example.com" { t.Fatalf("unexpected sub: %q", identity.Sub) } } func TestTestAuthenticatorRejectsInvalidToken(t *testing.T) { a := &TestAuthenticator{} if _, err := a.Authenticate(context.Background(), "notajwt", "test"); err == nil { t.Fatal("expected error for non-JWT non-email token") } } func TestNamespaceForSub(t *testing.T) { ns := NamespaceForSub("test-user-123") if len(ns) == 0 { t.Fatal("empty namespace") } if ns != NamespaceForSub("test-user-123") { t.Fatal("namespace not deterministic") } if NamespaceForSub("a") == NamespaceForSub("b") { t.Fatal("different subs should produce different namespaces") } }