- Add config_test.go (5 tests for env parsing) - Add middleware_test.go (16 tests for CORS, Auth, Gzip, Logger) - Add usecase_test.go (30+ tests for business logic) - Add payments_test.go (6 tests for MercadoPago gateway) Coverage: config 100%, middleware 95.9%, payments 100%, usecase 64.7% feat(marketplace): add test framework and new pages - Setup Vitest with jsdom environment - Add cartStore.test.ts (15 tests for Zustand store) - Add usePersistentFilters.test.ts (5 tests for hook) - Add apiClient.test.ts (7 tests for axios client) - Add Orders page with status transitions - Add Inventory page with stock adjustments - Add Company page with edit functionality - Add SellerDashboard page with KPIs Total marketplace tests: 27 passing
342 lines
9.5 KiB
Go
342 lines
9.5 KiB
Go
package middleware
|
|
|
|
import (
|
|
"compress/gzip"
|
|
"context"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/gofrs/uuid/v5"
|
|
"github.com/golang-jwt/jwt/v5"
|
|
)
|
|
|
|
// --- CORS Tests ---
|
|
|
|
func TestCORSWithConfigAllowAll(t *testing.T) {
|
|
cfg := CORSConfig{AllowedOrigins: []string{"*"}}
|
|
handler := CORSWithConfig(cfg)(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
}))
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
|
req.Header.Set("Origin", "https://example.com")
|
|
rec := httptest.NewRecorder()
|
|
|
|
handler.ServeHTTP(rec, req)
|
|
|
|
if rec.Header().Get("Access-Control-Allow-Origin") != "*" {
|
|
t.Errorf("expected Access-Control-Allow-Origin '*', got '%s'", rec.Header().Get("Access-Control-Allow-Origin"))
|
|
}
|
|
}
|
|
|
|
func TestCORSWithConfigSpecificOrigins(t *testing.T) {
|
|
cfg := CORSConfig{AllowedOrigins: []string{"https://allowed.com", "https://another.com"}}
|
|
handler := CORSWithConfig(cfg)(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
}))
|
|
|
|
// Test allowed origin
|
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
|
req.Header.Set("Origin", "https://allowed.com")
|
|
rec := httptest.NewRecorder()
|
|
handler.ServeHTTP(rec, req)
|
|
|
|
if rec.Header().Get("Access-Control-Allow-Origin") != "https://allowed.com" {
|
|
t.Errorf("expected origin 'https://allowed.com', got '%s'", rec.Header().Get("Access-Control-Allow-Origin"))
|
|
}
|
|
|
|
// Test blocked origin
|
|
req2 := httptest.NewRequest(http.MethodGet, "/", nil)
|
|
req2.Header.Set("Origin", "https://blocked.com")
|
|
rec2 := httptest.NewRecorder()
|
|
handler.ServeHTTP(rec2, req2)
|
|
|
|
if rec2.Header().Get("Access-Control-Allow-Origin") != "" {
|
|
t.Errorf("expected empty Access-Control-Allow-Origin, got '%s'", rec2.Header().Get("Access-Control-Allow-Origin"))
|
|
}
|
|
}
|
|
|
|
func TestCORSPreflight(t *testing.T) {
|
|
cfg := CORSConfig{AllowedOrigins: []string{"*"}}
|
|
handler := CORSWithConfig(cfg)(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusTeapot) // Should not reach here
|
|
}))
|
|
|
|
req := httptest.NewRequest(http.MethodOptions, "/", nil)
|
|
rec := httptest.NewRecorder()
|
|
|
|
handler.ServeHTTP(rec, req)
|
|
|
|
if rec.Code != http.StatusOK {
|
|
t.Errorf("expected status 200 for preflight, got %d", rec.Code)
|
|
}
|
|
}
|
|
|
|
func TestCORSHeaders(t *testing.T) {
|
|
cfg := CORSConfig{AllowedOrigins: []string{"*"}}
|
|
handler := CORSWithConfig(cfg)(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
}))
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
|
rec := httptest.NewRecorder()
|
|
|
|
handler.ServeHTTP(rec, req)
|
|
|
|
if !strings.Contains(rec.Header().Get("Access-Control-Allow-Methods"), "GET") {
|
|
t.Error("expected Access-Control-Allow-Methods to include GET")
|
|
}
|
|
if !strings.Contains(rec.Header().Get("Access-Control-Allow-Headers"), "Authorization") {
|
|
t.Error("expected Access-Control-Allow-Headers to include Authorization")
|
|
}
|
|
}
|
|
|
|
// --- Auth Tests ---
|
|
|
|
func createTestToken(secret string, userID uuid.UUID, role string, companyID *uuid.UUID) string {
|
|
claims := jwt.MapClaims{
|
|
"sub": userID.String(),
|
|
"role": role,
|
|
"exp": time.Now().Add(time.Hour).Unix(),
|
|
}
|
|
if companyID != nil {
|
|
claims["company_id"] = companyID.String()
|
|
}
|
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
|
tokenStr, _ := token.SignedString([]byte(secret))
|
|
return tokenStr
|
|
}
|
|
|
|
func TestRequireAuthValidToken(t *testing.T) {
|
|
secret := "test-secret"
|
|
userID, _ := uuid.NewV4()
|
|
companyID, _ := uuid.NewV4()
|
|
tokenStr := createTestToken(secret, userID, "Admin", &companyID)
|
|
|
|
var receivedClaims Claims
|
|
handler := RequireAuth([]byte(secret))(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
receivedClaims, _ = GetClaims(r.Context())
|
|
w.WriteHeader(http.StatusOK)
|
|
}))
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
|
req.Header.Set("Authorization", "Bearer "+tokenStr)
|
|
rec := httptest.NewRecorder()
|
|
|
|
handler.ServeHTTP(rec, req)
|
|
|
|
if rec.Code != http.StatusOK {
|
|
t.Errorf("expected status 200, got %d", rec.Code)
|
|
}
|
|
if receivedClaims.UserID != userID {
|
|
t.Errorf("expected userID %s, got %s", userID, receivedClaims.UserID)
|
|
}
|
|
if receivedClaims.Role != "Admin" {
|
|
t.Errorf("expected role 'Admin', got '%s'", receivedClaims.Role)
|
|
}
|
|
}
|
|
|
|
func TestRequireAuthMissingToken(t *testing.T) {
|
|
handler := RequireAuth([]byte("secret"))(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
}))
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
|
rec := httptest.NewRecorder()
|
|
|
|
handler.ServeHTTP(rec, req)
|
|
|
|
if rec.Code != http.StatusUnauthorized {
|
|
t.Errorf("expected status 401, got %d", rec.Code)
|
|
}
|
|
}
|
|
|
|
func TestRequireAuthInvalidToken(t *testing.T) {
|
|
handler := RequireAuth([]byte("secret"))(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
}))
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
|
req.Header.Set("Authorization", "Bearer invalid-token")
|
|
rec := httptest.NewRecorder()
|
|
|
|
handler.ServeHTTP(rec, req)
|
|
|
|
if rec.Code != http.StatusUnauthorized {
|
|
t.Errorf("expected status 401, got %d", rec.Code)
|
|
}
|
|
}
|
|
|
|
func TestRequireAuthWrongSecret(t *testing.T) {
|
|
userID, _ := uuid.NewV4()
|
|
tokenStr := createTestToken("correct-secret", userID, "User", nil)
|
|
|
|
handler := RequireAuth([]byte("wrong-secret"))(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
}))
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
|
req.Header.Set("Authorization", "Bearer "+tokenStr)
|
|
rec := httptest.NewRecorder()
|
|
|
|
handler.ServeHTTP(rec, req)
|
|
|
|
if rec.Code != http.StatusUnauthorized {
|
|
t.Errorf("expected status 401, got %d", rec.Code)
|
|
}
|
|
}
|
|
|
|
func TestRequireAuthRoleRestriction(t *testing.T) {
|
|
secret := "secret"
|
|
userID, _ := uuid.NewV4()
|
|
tokenStr := createTestToken(secret, userID, "User", nil)
|
|
|
|
handler := RequireAuth([]byte(secret), "Admin")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
}))
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
|
req.Header.Set("Authorization", "Bearer "+tokenStr)
|
|
rec := httptest.NewRecorder()
|
|
|
|
handler.ServeHTTP(rec, req)
|
|
|
|
if rec.Code != http.StatusForbidden {
|
|
t.Errorf("expected status 403, got %d", rec.Code)
|
|
}
|
|
}
|
|
|
|
func TestRequireAuthRoleAllowed(t *testing.T) {
|
|
secret := "secret"
|
|
userID, _ := uuid.NewV4()
|
|
tokenStr := createTestToken(secret, userID, "Admin", nil)
|
|
|
|
handler := RequireAuth([]byte(secret), "Admin")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
}))
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
|
req.Header.Set("Authorization", "Bearer "+tokenStr)
|
|
rec := httptest.NewRecorder()
|
|
|
|
handler.ServeHTTP(rec, req)
|
|
|
|
if rec.Code != http.StatusOK {
|
|
t.Errorf("expected status 200, got %d", rec.Code)
|
|
}
|
|
}
|
|
|
|
func TestGetClaimsFromContext(t *testing.T) {
|
|
claims := Claims{
|
|
UserID: uuid.Must(uuid.NewV4()),
|
|
Role: "Admin",
|
|
}
|
|
ctx := context.WithValue(context.Background(), claimsKey, claims)
|
|
|
|
retrieved, ok := GetClaims(ctx)
|
|
if !ok {
|
|
t.Error("expected to retrieve claims from context")
|
|
}
|
|
if retrieved.UserID != claims.UserID {
|
|
t.Errorf("expected userID %s, got %s", claims.UserID, retrieved.UserID)
|
|
}
|
|
}
|
|
|
|
func TestGetClaimsNotInContext(t *testing.T) {
|
|
_, ok := GetClaims(context.Background())
|
|
if ok {
|
|
t.Error("expected claims to not be in context")
|
|
}
|
|
}
|
|
|
|
// --- Gzip Tests ---
|
|
|
|
func TestGzipCompression(t *testing.T) {
|
|
handler := Gzip(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.Write([]byte("Hello, World!"))
|
|
}))
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
|
req.Header.Set("Accept-Encoding", "gzip")
|
|
rec := httptest.NewRecorder()
|
|
|
|
handler.ServeHTTP(rec, req)
|
|
|
|
if rec.Header().Get("Content-Encoding") != "gzip" {
|
|
t.Error("expected Content-Encoding 'gzip'")
|
|
}
|
|
|
|
// Decompress and verify
|
|
reader, err := gzip.NewReader(rec.Body)
|
|
if err != nil {
|
|
t.Fatalf("failed to create gzip reader: %v", err)
|
|
}
|
|
defer reader.Close()
|
|
|
|
body, err := io.ReadAll(reader)
|
|
if err != nil {
|
|
t.Fatalf("failed to read gzip body: %v", err)
|
|
}
|
|
|
|
if string(body) != "Hello, World!" {
|
|
t.Errorf("expected 'Hello, World!', got '%s'", string(body))
|
|
}
|
|
}
|
|
|
|
func TestGzipNoCompression(t *testing.T) {
|
|
handler := Gzip(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.Write([]byte("Hello, World!"))
|
|
}))
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
|
// No Accept-Encoding header
|
|
rec := httptest.NewRecorder()
|
|
|
|
handler.ServeHTTP(rec, req)
|
|
|
|
if rec.Header().Get("Content-Encoding") == "gzip" {
|
|
t.Error("should not use gzip when not requested")
|
|
}
|
|
if rec.Body.String() != "Hello, World!" {
|
|
t.Errorf("expected 'Hello, World!', got '%s'", rec.Body.String())
|
|
}
|
|
}
|
|
|
|
// --- Logger Tests ---
|
|
|
|
func TestLoggerMiddleware(t *testing.T) {
|
|
handler := Logger(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
}))
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/test-path", nil)
|
|
rec := httptest.NewRecorder()
|
|
|
|
handler.ServeHTTP(rec, req)
|
|
|
|
if rec.Code != http.StatusOK {
|
|
t.Errorf("expected status 200, got %d", rec.Code)
|
|
}
|
|
}
|
|
|
|
// --- CORS Legacy Wrapper Test ---
|
|
|
|
func TestCORSLegacyWrapper(t *testing.T) {
|
|
handler := CORS(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
}))
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
|
req.Header.Set("Origin", "https://example.com")
|
|
rec := httptest.NewRecorder()
|
|
|
|
handler.ServeHTTP(rec, req)
|
|
|
|
if rec.Header().Get("Access-Control-Allow-Origin") != "*" {
|
|
t.Errorf("expected '*', got '%s'", rec.Header().Get("Access-Control-Allow-Origin"))
|
|
}
|
|
}
|