chore: remove cached
This commit is contained in:
@@ -1,388 +0,0 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/Wikid82/CaddyProxyManagerPlus/backend/internal/models"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func setupUserHandler(t *testing.T) (*UserHandler, *gorm.DB) {
|
||||
// Use unique DB for each test to avoid pollution
|
||||
dbName := "file:" + t.Name() + "?mode=memory&cache=shared"
|
||||
db, err := gorm.Open(sqlite.Open(dbName), &gorm.Config{})
|
||||
require.NoError(t, err)
|
||||
db.AutoMigrate(&models.User{}, &models.Setting{})
|
||||
return NewUserHandler(db), db
|
||||
}
|
||||
|
||||
func TestUserHandler_GetSetupStatus(t *testing.T) {
|
||||
handler, db := setupUserHandler(t)
|
||||
gin.SetMode(gin.TestMode)
|
||||
r := gin.New()
|
||||
r.GET("/setup", handler.GetSetupStatus)
|
||||
|
||||
// No users -> setup required
|
||||
req, _ := http.NewRequest("GET", "/setup", nil)
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
assert.Contains(t, w.Body.String(), "\"setupRequired\":true")
|
||||
|
||||
// Create user -> setup not required
|
||||
db.Create(&models.User{Email: "test@example.com"})
|
||||
w = httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
assert.Contains(t, w.Body.String(), "\"setupRequired\":false")
|
||||
}
|
||||
|
||||
func TestUserHandler_Setup(t *testing.T) {
|
||||
handler, _ := setupUserHandler(t)
|
||||
gin.SetMode(gin.TestMode)
|
||||
r := gin.New()
|
||||
r.POST("/setup", handler.Setup)
|
||||
|
||||
// 1. Invalid JSON (Before setup is done)
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("POST", "/setup", bytes.NewBuffer([]byte("invalid json")))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
r.ServeHTTP(w, req)
|
||||
assert.Equal(t, http.StatusBadRequest, w.Code)
|
||||
|
||||
// 2. Valid Setup
|
||||
body := map[string]string{
|
||||
"name": "Admin",
|
||||
"email": "admin@example.com",
|
||||
"password": "password123",
|
||||
}
|
||||
jsonBody, _ := json.Marshal(body)
|
||||
req, _ = http.NewRequest("POST", "/setup", bytes.NewBuffer(jsonBody))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
w = httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusCreated, w.Code)
|
||||
assert.Contains(t, w.Body.String(), "Setup completed successfully")
|
||||
|
||||
// 3. Try again -> should fail (already setup)
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("POST", "/setup", bytes.NewBuffer(jsonBody))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
r.ServeHTTP(w, req)
|
||||
assert.Equal(t, http.StatusForbidden, w.Code)
|
||||
}
|
||||
|
||||
func TestUserHandler_Setup_DBError(t *testing.T) {
|
||||
// Can't easily mock DB error with sqlite memory unless we close it or something.
|
||||
// But we can try to insert duplicate email if we had a unique constraint and pre-seeded data,
|
||||
// but Setup checks if ANY user exists first.
|
||||
// So if we have a user, it returns Forbidden.
|
||||
// If we don't, it tries to create.
|
||||
// If we want Create to fail, maybe invalid data that passes binding but fails DB constraint?
|
||||
// User model has validation?
|
||||
// Let's try empty password if allowed by binding but rejected by DB?
|
||||
// Or very long string?
|
||||
}
|
||||
|
||||
func TestUserHandler_RegenerateAPIKey(t *testing.T) {
|
||||
handler, db := setupUserHandler(t)
|
||||
|
||||
user := &models.User{Email: "api@example.com"}
|
||||
db.Create(user)
|
||||
|
||||
gin.SetMode(gin.TestMode)
|
||||
r := gin.New()
|
||||
r.Use(func(c *gin.Context) {
|
||||
c.Set("userID", user.ID)
|
||||
c.Next()
|
||||
})
|
||||
r.POST("/api-key", handler.RegenerateAPIKey)
|
||||
|
||||
req, _ := http.NewRequest("POST", "/api-key", nil)
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var resp map[string]string
|
||||
json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
assert.NotEmpty(t, resp["api_key"])
|
||||
|
||||
// Verify DB
|
||||
var updatedUser models.User
|
||||
db.First(&updatedUser, user.ID)
|
||||
assert.Equal(t, resp["api_key"], updatedUser.APIKey)
|
||||
}
|
||||
|
||||
func TestUserHandler_GetProfile(t *testing.T) {
|
||||
handler, db := setupUserHandler(t)
|
||||
|
||||
user := &models.User{
|
||||
Email: "profile@example.com",
|
||||
Name: "Profile User",
|
||||
APIKey: "existing-key",
|
||||
}
|
||||
db.Create(user)
|
||||
|
||||
gin.SetMode(gin.TestMode)
|
||||
r := gin.New()
|
||||
r.Use(func(c *gin.Context) {
|
||||
c.Set("userID", user.ID)
|
||||
c.Next()
|
||||
})
|
||||
r.GET("/profile", handler.GetProfile)
|
||||
|
||||
req, _ := http.NewRequest("GET", "/profile", nil)
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var resp models.User
|
||||
json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
assert.Equal(t, user.Email, resp.Email)
|
||||
assert.Equal(t, user.APIKey, resp.APIKey)
|
||||
}
|
||||
|
||||
func TestUserHandler_RegisterRoutes(t *testing.T) {
|
||||
handler, _ := setupUserHandler(t)
|
||||
gin.SetMode(gin.TestMode)
|
||||
r := gin.New()
|
||||
api := r.Group("/api")
|
||||
handler.RegisterRoutes(api)
|
||||
|
||||
routes := r.Routes()
|
||||
expectedRoutes := map[string]string{
|
||||
"/api/setup": "GET,POST",
|
||||
"/api/profile": "GET",
|
||||
"/api/regenerate-api-key": "POST",
|
||||
}
|
||||
|
||||
for path := range expectedRoutes {
|
||||
found := false
|
||||
for _, route := range routes {
|
||||
if route.Path == path {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
assert.True(t, found, "Route %s not found", path)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserHandler_Errors(t *testing.T) {
|
||||
handler, db := setupUserHandler(t)
|
||||
gin.SetMode(gin.TestMode)
|
||||
r := gin.New()
|
||||
|
||||
// Middleware to simulate missing userID
|
||||
r.GET("/profile-no-auth", func(c *gin.Context) {
|
||||
// No userID set
|
||||
handler.GetProfile(c)
|
||||
})
|
||||
r.POST("/api-key-no-auth", func(c *gin.Context) {
|
||||
// No userID set
|
||||
handler.RegenerateAPIKey(c)
|
||||
})
|
||||
|
||||
// Middleware to simulate non-existent user
|
||||
r.GET("/profile-not-found", func(c *gin.Context) {
|
||||
c.Set("userID", uint(99999))
|
||||
handler.GetProfile(c)
|
||||
})
|
||||
r.POST("/api-key-not-found", func(c *gin.Context) {
|
||||
c.Set("userID", uint(99999))
|
||||
handler.RegenerateAPIKey(c)
|
||||
})
|
||||
|
||||
// Test Unauthorized
|
||||
req, _ := http.NewRequest("GET", "/profile-no-auth", nil)
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
assert.Equal(t, http.StatusUnauthorized, w.Code)
|
||||
|
||||
req, _ = http.NewRequest("POST", "/api-key-no-auth", nil)
|
||||
w = httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
assert.Equal(t, http.StatusUnauthorized, w.Code)
|
||||
|
||||
// Test Not Found (GetProfile)
|
||||
req, _ = http.NewRequest("GET", "/profile-not-found", nil)
|
||||
w = httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
assert.Equal(t, http.StatusNotFound, w.Code)
|
||||
|
||||
// Test DB Error (RegenerateAPIKey) - Hard to mock DB error on update with sqlite memory,
|
||||
// but we can try to update a non-existent user which GORM Update might not treat as error unless we check RowsAffected.
|
||||
// The handler code: if err := h.DB.Model(&models.User{}).Where("id = ?", userID).Update("api_key", apiKey).Error; err != nil
|
||||
// Update on non-existent record usually returns nil error in GORM unless configured otherwise.
|
||||
// However, let's see if we can force an error by closing DB? No, shared DB.
|
||||
// We can drop the table?
|
||||
db.Migrator().DropTable(&models.User{})
|
||||
req, _ = http.NewRequest("POST", "/api-key-not-found", nil)
|
||||
w = httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
// If table missing, Update should fail
|
||||
assert.Equal(t, http.StatusInternalServerError, w.Code)
|
||||
}
|
||||
|
||||
func TestUserHandler_UpdateProfile(t *testing.T) {
|
||||
handler, db := setupUserHandler(t)
|
||||
|
||||
// Create user
|
||||
user := &models.User{
|
||||
UUID: uuid.NewString(),
|
||||
Email: "test@example.com",
|
||||
Name: "Test User",
|
||||
APIKey: uuid.NewString(),
|
||||
}
|
||||
user.SetPassword("password123")
|
||||
db.Create(user)
|
||||
|
||||
gin.SetMode(gin.TestMode)
|
||||
r := gin.New()
|
||||
r.Use(func(c *gin.Context) {
|
||||
c.Set("userID", user.ID)
|
||||
c.Next()
|
||||
})
|
||||
r.PUT("/profile", handler.UpdateProfile)
|
||||
|
||||
// 1. Success - Name only
|
||||
t.Run("Success Name Only", func(t *testing.T) {
|
||||
body := map[string]string{
|
||||
"name": "Updated Name",
|
||||
"email": "test@example.com",
|
||||
}
|
||||
jsonBody, _ := json.Marshal(body)
|
||||
req := httptest.NewRequest("PUT", "/profile", bytes.NewBuffer(jsonBody))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var updatedUser models.User
|
||||
db.First(&updatedUser, user.ID)
|
||||
assert.Equal(t, "Updated Name", updatedUser.Name)
|
||||
})
|
||||
|
||||
// 2. Success - Email change with password
|
||||
t.Run("Success Email Change", func(t *testing.T) {
|
||||
body := map[string]string{
|
||||
"name": "Updated Name",
|
||||
"email": "newemail@example.com",
|
||||
"current_password": "password123",
|
||||
}
|
||||
jsonBody, _ := json.Marshal(body)
|
||||
req := httptest.NewRequest("PUT", "/profile", bytes.NewBuffer(jsonBody))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var updatedUser models.User
|
||||
db.First(&updatedUser, user.ID)
|
||||
assert.Equal(t, "newemail@example.com", updatedUser.Email)
|
||||
})
|
||||
|
||||
// 3. Fail - Email change without password
|
||||
t.Run("Fail Email Change No Password", func(t *testing.T) {
|
||||
// Reset email
|
||||
db.Model(user).Update("email", "test@example.com")
|
||||
|
||||
body := map[string]string{
|
||||
"name": "Updated Name",
|
||||
"email": "another@example.com",
|
||||
}
|
||||
jsonBody, _ := json.Marshal(body)
|
||||
req := httptest.NewRequest("PUT", "/profile", bytes.NewBuffer(jsonBody))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, w.Code)
|
||||
})
|
||||
|
||||
// 4. Fail - Email change wrong password
|
||||
t.Run("Fail Email Change Wrong Password", func(t *testing.T) {
|
||||
body := map[string]string{
|
||||
"name": "Updated Name",
|
||||
"email": "another@example.com",
|
||||
"current_password": "wrongpassword",
|
||||
}
|
||||
jsonBody, _ := json.Marshal(body)
|
||||
req := httptest.NewRequest("PUT", "/profile", bytes.NewBuffer(jsonBody))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusUnauthorized, w.Code)
|
||||
})
|
||||
|
||||
// 5. Fail - Email already in use
|
||||
t.Run("Fail Email In Use", func(t *testing.T) {
|
||||
// Create another user
|
||||
otherUser := &models.User{
|
||||
UUID: uuid.NewString(),
|
||||
Email: "other@example.com",
|
||||
Name: "Other User",
|
||||
APIKey: uuid.NewString(),
|
||||
}
|
||||
db.Create(otherUser)
|
||||
|
||||
body := map[string]string{
|
||||
"name": "Updated Name",
|
||||
"email": "other@example.com",
|
||||
}
|
||||
jsonBody, _ := json.Marshal(body)
|
||||
req := httptest.NewRequest("PUT", "/profile", bytes.NewBuffer(jsonBody))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusConflict, w.Code)
|
||||
})
|
||||
}
|
||||
|
||||
func TestUserHandler_UpdateProfile_Errors(t *testing.T) {
|
||||
handler, _ := setupUserHandler(t)
|
||||
gin.SetMode(gin.TestMode)
|
||||
r := gin.New()
|
||||
|
||||
// 1. Unauthorized (no userID)
|
||||
r.PUT("/profile-no-auth", handler.UpdateProfile)
|
||||
req, _ := http.NewRequest("PUT", "/profile-no-auth", nil)
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
assert.Equal(t, http.StatusUnauthorized, w.Code)
|
||||
|
||||
// Middleware for subsequent tests
|
||||
r.Use(func(c *gin.Context) {
|
||||
c.Set("userID", uint(999)) // Non-existent ID
|
||||
c.Next()
|
||||
})
|
||||
r.PUT("/profile", handler.UpdateProfile)
|
||||
|
||||
// 2. BindJSON error
|
||||
req, _ = http.NewRequest("PUT", "/profile", bytes.NewBufferString("invalid"))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
w = httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
assert.Equal(t, http.StatusBadRequest, w.Code)
|
||||
|
||||
// 3. User not found
|
||||
body := map[string]string{"name": "New Name", "email": "new@example.com"}
|
||||
jsonBody, _ := json.Marshal(body)
|
||||
req, _ = http.NewRequest("PUT", "/profile", bytes.NewBuffer(jsonBody))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
w = httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
assert.Equal(t, http.StatusNotFound, w.Code)
|
||||
}
|
||||
Reference in New Issue
Block a user