package httpauth import ( "context" "errors" "net/http" "net/http/httptest" "testing" "firebase.google.com/go/v4/auth" ) // --- mocks --- type mockVerifier struct { token *auth.Token err error } func (m *mockVerifier) VerifyIDTokenAndCheckRevoked(_ context.Context, _ string) (*auth.Token, error) { return m.token, m.err } func chain(mw func(http.Handler) http.Handler, h http.HandlerFunc) http.Handler { return mw(h) } // --- AuthMiddleware --- func TestAuthMiddleware_ValidToken(t *testing.T) { mv := &mockVerifier{token: &auth.Token{UID: "uid123", Claims: map[string]any{"name": "Alice"}}} reached := false h := chain(AuthMiddleware(mv, nil), func(w http.ResponseWriter, r *http.Request) { reached = true w.WriteHeader(http.StatusOK) }) req := httptest.NewRequest(http.MethodGet, "/api", nil) req.Header.Set("Authorization", "Bearer valid-token") rec := httptest.NewRecorder() h.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Errorf("want 200, got %d", rec.Code) } if !reached { t.Error("inner handler was not called") } } func TestAuthMiddleware_InvalidToken(t *testing.T) { mv := &mockVerifier{err: errors.New("token invalid")} h := chain(AuthMiddleware(mv, nil), func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) }) req := httptest.NewRequest(http.MethodGet, "/api", nil) req.Header.Set("Authorization", "Bearer bad-token") rec := httptest.NewRecorder() h.ServeHTTP(rec, req) if rec.Code != http.StatusUnauthorized { t.Errorf("want 401, got %d", rec.Code) } } func TestAuthMiddleware_MissingHeader(t *testing.T) { mv := &mockVerifier{} h := chain(AuthMiddleware(mv, nil), func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) }) rec := httptest.NewRecorder() h.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/api", nil)) if rec.Code != http.StatusUnauthorized { t.Errorf("want 401, got %d", rec.Code) } } func TestAuthMiddleware_PublicPath(t *testing.T) { mv := &mockVerifier{err: errors.New("should not be called")} h := chain(AuthMiddleware(mv, []string{"/health"}), func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) }) rec := httptest.NewRecorder() h.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/health", nil)) if rec.Code != http.StatusOK { t.Errorf("want 200, got %d", rec.Code) } } func TestAuthMiddleware_PublicPathWildcard(t *testing.T) { mv := &mockVerifier{err: errors.New("should not be called")} h := chain(AuthMiddleware(mv, []string{"/public/*"}), func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) }) rec := httptest.NewRecorder() h.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/public/resource", nil)) if rec.Code != http.StatusOK { t.Errorf("want 200, got %d", rec.Code) } }