195 lines
5.8 KiB
Go
195 lines
5.8 KiB
Go
package handlers
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/golang-jwt/jwt/v5"
|
|
"github.com/google/uuid"
|
|
"github.com/stretchr/testify/assert"
|
|
"go.uber.org/zap"
|
|
"gorm.io/gorm"
|
|
"veza-backend-api/internal/models"
|
|
"veza-backend-api/internal/services"
|
|
)
|
|
|
|
type MockUserRepository struct {
|
|
users map[uuid.UUID]*models.User
|
|
}
|
|
|
|
func NewMockUserRepository() *MockUserRepository {
|
|
return &MockUserRepository{
|
|
users: make(map[uuid.UUID]*models.User),
|
|
}
|
|
}
|
|
|
|
func (m *MockUserRepository) CreateUser(ctx context.Context, user *models.User) error {
|
|
m.users[user.ID] = user
|
|
return nil
|
|
}
|
|
func (m *MockUserRepository) GetUserByID(ctx context.Context, id uuid.UUID) (*models.User, error) {
|
|
user, ok := m.users[id]
|
|
if !ok {
|
|
return nil, gorm.ErrRecordNotFound
|
|
}
|
|
return user, nil
|
|
}
|
|
func (m *MockUserRepository) GetUserByEmail(ctx context.Context, email string) (*models.User, error) {
|
|
panic("not implemented")
|
|
}
|
|
func (m *MockUserRepository) GetUserByUsername(ctx context.Context, username string) (*models.User, error) {
|
|
for _, user := range m.users {
|
|
if user.Username == username {
|
|
return user, nil
|
|
}
|
|
}
|
|
return nil, gorm.ErrRecordNotFound
|
|
}
|
|
func (m *MockUserRepository) UpdateUser(ctx context.Context, user *models.User) error {
|
|
m.users[user.ID] = user
|
|
return nil
|
|
}
|
|
func (m *MockUserRepository) DeleteUser(ctx context.Context, id uuid.UUID) error {
|
|
panic("not implemented")
|
|
}
|
|
func (m *MockUserRepository) UpdateLastLoginAt(ctx context.Context, userID uuid.UUID) error {
|
|
panic("not implemented")
|
|
}
|
|
func (m *MockUserRepository) IncrementTokenVersion(ctx context.Context, userID uuid.UUID) error {
|
|
panic("not implemented")
|
|
}
|
|
|
|
// Compatibility methods for services.UserRepository interface
|
|
func (m *MockUserRepository) GetByID(id string) (*models.User, error) {
|
|
idUUID, err := uuid.Parse(id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return m.GetUserByID(context.Background(), idUUID)
|
|
}
|
|
func (m *MockUserRepository) GetByEmail(email string) (*models.User, error) {
|
|
return m.GetUserByEmail(context.Background(), email)
|
|
}
|
|
func (m *MockUserRepository) GetByUsername(username string) (*models.User, error) {
|
|
return m.GetUserByUsername(context.Background(), username)
|
|
}
|
|
func (m *MockUserRepository) Create(user *models.User) error {
|
|
return m.CreateUser(context.Background(), user)
|
|
}
|
|
func (m *MockUserRepository) Update(user *models.User) error {
|
|
return m.UpdateUser(context.Background(), user)
|
|
}
|
|
func (m *MockUserRepository) Delete(id string) error {
|
|
idUUID, _ := uuid.Parse(id)
|
|
return m.DeleteUser(context.Background(), idUUID)
|
|
}
|
|
|
|
func setupTestChatHandler(t *testing.T) (*ChatHandler, *gin.Engine, func(), uuid.UUID) {
|
|
gin.SetMode(gin.TestMode)
|
|
|
|
logger := zap.NewNop()
|
|
jwtSecret := "supersecretchatkey"
|
|
|
|
chatService := services.NewChatService(jwtSecret, logger)
|
|
|
|
// Mock UserService
|
|
mockUserRepo := NewMockUserRepository()
|
|
userID := uuid.New()
|
|
mockUser := &models.User{
|
|
ID: userID,
|
|
Username: "testuser",
|
|
Email: "test@example.com",
|
|
// ... other fields as needed
|
|
}
|
|
mockUserRepo.CreateUser(context.Background(), mockUser)
|
|
userService := services.NewUserService(mockUserRepo)
|
|
|
|
handler := NewChatHandler(chatService, userService, logger)
|
|
|
|
r := gin.New()
|
|
// Simulate auth middleware setting user_id
|
|
r.Use(func(c *gin.Context) {
|
|
c.Set("user_id", userID) // Pass UUID object as middleware does
|
|
c.Set("username", "testuser")
|
|
c.Next()
|
|
})
|
|
r.POST("/chat/token", handler.GetToken)
|
|
|
|
cleanup := func() {
|
|
// No specific cleanup needed for these tests
|
|
}
|
|
|
|
return handler, r, cleanup, userID
|
|
}
|
|
|
|
func TestChatHandler_GetToken_Success(t *testing.T) {
|
|
_, r, cleanup, userID := setupTestChatHandler(t)
|
|
defer cleanup()
|
|
|
|
req := httptest.NewRequest(http.MethodPost, "/chat/token", nil)
|
|
w := httptest.NewRecorder()
|
|
r.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
|
|
var response APIResponse
|
|
err := json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
assert.True(t, response.Success)
|
|
assert.Nil(t, response.Error)
|
|
|
|
// Data should be map/struct. Since it is interface{}, we need to marshal/unmarshal or type assert carefully.
|
|
// API sends ChatTokenResponse struct.
|
|
// Let's re-marshal Data to get ChatTokenResponse
|
|
dataBytes, _ := json.Marshal(response.Data)
|
|
var tokenResponse services.ChatTokenResponse
|
|
err = json.Unmarshal(dataBytes, &tokenResponse)
|
|
assert.NoError(t, err)
|
|
|
|
assert.NotEmpty(t, tokenResponse.Token)
|
|
assert.Greater(t, tokenResponse.ExpiresIn, int64(0))
|
|
assert.Equal(t, "/ws", tokenResponse.WSUrl)
|
|
|
|
// Optionally, verify token content
|
|
parsedToken, err := jwt.Parse(tokenResponse.Token, func(token *jwt.Token) (interface{}, error) {
|
|
assert.Equal(t, jwt.SigningMethodHS256, token.Method)
|
|
return []byte("supersecretchatkey"), nil
|
|
})
|
|
assert.NoError(t, err)
|
|
claims, ok := parsedToken.Claims.(jwt.MapClaims)
|
|
assert.True(t, ok)
|
|
assert.Equal(t, userID.String(), claims["sub"])
|
|
assert.Equal(t, "testuser", claims["name"])
|
|
}
|
|
|
|
func TestChatHandler_GetToken_Unauthorized(t *testing.T) {
|
|
logger := zap.NewNop()
|
|
jwtSecret := "supersecretchatkey"
|
|
|
|
chatService := services.NewChatService(jwtSecret, logger)
|
|
mockUserRepo := NewMockUserRepository()
|
|
userService := services.NewUserService(mockUserRepo)
|
|
|
|
handler := NewChatHandler(chatService, userService, logger)
|
|
|
|
r := gin.New()
|
|
r.POST("/chat/token", handler.GetToken) // No auth middleware
|
|
|
|
req := httptest.NewRequest(http.MethodPost, "/chat/token", nil)
|
|
w := httptest.NewRecorder()
|
|
r.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusUnauthorized, w.Code)
|
|
|
|
var response map[string]interface{}
|
|
err := json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
// API might return standard error JSON or APIResponse depending on middleware
|
|
// The handler uses c.JSON(Unauthorized, gin.H{"error":...}) directly in manual checks
|
|
// See lines 41, 46 in handler.
|
|
assert.Equal(t, "unauthorized", response["error"])
|
|
}
|