Files
ginimageApi/app/middleware/auth_test.go
Beyhan Oğur e04ba85564 first commit
2026-04-26 21:40:14 +03:00

232 lines
5.8 KiB
Go

package middleware
import (
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
"ginimageApi/app/accounts/models"
"ginimageApi/configs"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v5"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
func setupMiddlewareTestDB(t *testing.T) {
t.Helper()
prev := configs.DB
dsn := "file:" + t.Name() + "?mode=memory&cache=shared"
db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{})
if err != nil {
t.Fatalf("failed to open sqlite: %v", err)
}
if err := db.AutoMigrate(&models.User{}); err != nil {
t.Fatalf("failed to migrate: %v", err)
}
configs.DB = db
t.Cleanup(func() {
if sqlDB, err := db.DB(); err == nil {
_ = sqlDB.Close()
}
configs.DB = prev
})
}
func TestGenerateAndParseAccessToken(t *testing.T) {
t.Setenv("JWT_SECRET", "test-secret")
token, err := GenerateAccessToken(99, "u@example.com", "u1", time.Minute)
if err != nil {
t.Fatalf("GenerateAccessToken failed: %v", err)
}
if got := len(strings.Split(token, ".")); got != 3 {
t.Fatalf("expected standard JWT with 3 segments, got %d", got)
}
payload, err := parseAccessToken(token)
if err != nil {
t.Fatalf("parseAccessToken failed: %v", err)
}
if payload.UserID != 99 || payload.Email != "u@example.com" || payload.Username != "u1" {
t.Fatalf("unexpected payload: %+v", payload)
}
}
func TestParseAccessTokenExpired(t *testing.T) {
t.Setenv("JWT_SECRET", "test-secret")
token, err := GenerateAccessToken(1, "a@a.com", "a", -time.Second)
if err != nil {
t.Fatalf("GenerateAccessToken failed: %v", err)
}
if _, err := parseAccessToken(token); err == nil {
t.Fatalf("expected parse error for expired token")
}
}
func TestParseAccessTokenRejectsRefreshToken(t *testing.T) {
t.Setenv("JWT_SECRET", "test-secret")
token, _, err := GenerateRefreshToken(1, time.Minute)
if err != nil {
t.Fatalf("GenerateRefreshToken failed: %v", err)
}
if _, err := parseAccessToken(token); err == nil {
t.Fatalf("expected parse error for refresh token")
}
}
func TestParseAccessTokenRequiresUserID(t *testing.T) {
t.Setenv("JWT_SECRET", "test-secret")
claims := accessTokenClaims{
TokenType: "access",
Email: "a@a.com",
Username: "a",
RegisteredClaims: jwt.RegisteredClaims{
IssuedAt: jwt.NewNumericDate(time.Now()),
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Minute)),
},
}
token, err := jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString([]byte("test-secret"))
if err != nil {
t.Fatalf("failed to sign token: %v", err)
}
if _, err := parseAccessToken(token); err == nil {
t.Fatalf("expected parse error for missing user_id")
}
}
func TestAuthRequired(t *testing.T) {
gin.SetMode(gin.TestMode)
t.Setenv("JWT_SECRET", "test-secret")
token, err := GenerateAccessToken(7, "mail@example.com", "user7", time.Minute)
if err != nil {
t.Fatalf("token generate failed: %v", err)
}
r := gin.New()
r.GET("/me", AuthRequired(), func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"user_id": c.GetUint("user_id"),
"email": c.GetString("email"),
"username": c.GetString("username"),
})
})
req := httptest.NewRequest(http.MethodGet, "/me", nil)
req.Header.Set("Authorization", "Bearer "+token)
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
if w.Code != http.StatusOK {
t.Fatalf("expected 200, got %d", w.Code)
}
var body map[string]any
if err := json.Unmarshal(w.Body.Bytes(), &body); err != nil {
t.Fatalf("invalid json: %v", err)
}
if body["email"] != "mail@example.com" {
t.Fatalf("expected email in context")
}
}
func TestAuthRequiredRejectsInvalidToken(t *testing.T) {
gin.SetMode(gin.TestMode)
r := gin.New()
r.GET("/me", AuthRequired(), func(c *gin.Context) {
c.Status(http.StatusOK)
})
req := httptest.NewRequest(http.MethodGet, "/me", nil)
req.Header.Set("Authorization", "Bearer invalid")
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
if w.Code != http.StatusUnauthorized {
t.Fatalf("expected 401, got %d", w.Code)
}
}
func TestAuthRequiredRejectsRawAuthorizationToken(t *testing.T) {
gin.SetMode(gin.TestMode)
t.Setenv("JWT_SECRET", "test-secret")
token, err := GenerateAccessToken(11, "raw@example.com", "rawuser", time.Minute)
if err != nil {
t.Fatalf("token generate failed: %v", err)
}
r := gin.New()
r.GET("/me", AuthRequired(), func(c *gin.Context) {
c.Status(http.StatusOK)
})
req := httptest.NewRequest(http.MethodGet, "/me", nil)
req.Header.Set("Authorization", token)
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
if w.Code != http.StatusUnauthorized {
t.Fatalf("expected 401 for raw token without Bearer, got %d", w.Code)
}
}
func TestAdminRequired(t *testing.T) {
gin.SetMode(gin.TestMode)
setupMiddlewareTestDB(t)
isAdmin := true
isUser := false
admin := models.User{UserName: "admin", Email: "admin@example.com", Password: "x", IsAdmin: &isAdmin}
user := models.User{UserName: "user", Email: "user@example.com", Password: "x", IsAdmin: &isUser}
if err := configs.DB.Create(&admin).Error; err != nil {
t.Fatalf("admin create failed: %v", err)
}
if err := configs.DB.Create(&user).Error; err != nil {
t.Fatalf("user create failed: %v", err)
}
r := gin.New()
r.POST("/admin", func(c *gin.Context) {
c.Set("user_id", user.ID)
c.Next()
}, AdminRequired(), func(c *gin.Context) {
c.Status(http.StatusOK)
})
w := httptest.NewRecorder()
r.ServeHTTP(w, httptest.NewRequest(http.MethodPost, "/admin", nil))
if w.Code != http.StatusForbidden {
t.Fatalf("expected 403 for non-admin, got %d", w.Code)
}
r2 := gin.New()
r2.POST("/admin", func(c *gin.Context) {
c.Set("user_id", admin.ID)
c.Next()
}, AdminRequired(), func(c *gin.Context) {
c.Status(http.StatusOK)
})
w2 := httptest.NewRecorder()
r2.ServeHTTP(w2, httptest.NewRequest(http.MethodPost, "/admin", nil))
if w2.Code != http.StatusOK {
t.Fatalf("expected 200 for admin, got %d", w2.Code)
}
}