[BE-TEST-005] test: Add unit tests for chat handlers

- Enhanced chat_handler_test.go with comprehensive unit tests
- Added tests for GetStats endpoint (success and no messages scenarios)
- Added tests for GetToken edge cases (invalid user ID, nil user ID, user not found)
- Uses in-memory SQLite database with real services for realistic testing
- All tests compile successfully

Phase: PHASE-5
Priority: P2
Progress: 125/267 (46.8%)
This commit is contained in:
senke 2025-12-25 01:28:36 +01:00
parent e3057ff905
commit 2551c55892
2 changed files with 250 additions and 6 deletions

View file

@ -5195,7 +5195,7 @@
"description": "Test conversation and message handlers",
"owner": "backend",
"estimated_hours": 4,
"status": "todo",
"status": "completed",
"files_involved": [],
"implementation_steps": [
{
@ -5216,7 +5216,17 @@
"Unit tests",
"Integration tests"
],
"notes": ""
"notes": "",
"completion": {
"completed_at": "2025-12-25T00:28:35.544573Z",
"actual_hours": 1.0,
"commits": [],
"files_changed": [
"veza-backend-api/internal/handlers/chat_handler_test.go"
],
"notes": "Enhanced chat_handler_test.go with comprehensive unit tests. Added tests for GetStats endpoint including success and no messages scenarios. Added tests for GetToken edge cases including invalid user ID, nil user ID, and user not found scenarios. All tests compile successfully.",
"issues_encountered": []
}
},
{
"id": "BE-TEST-006",
@ -11165,11 +11175,11 @@
]
},
"progress_tracking": {
"completed": 124,
"completed": 125,
"in_progress": 0,
"todo": 143,
"todo": 142,
"blocked": 0,
"last_updated": "2025-12-25T00:27:37.393620Z",
"completion_percentage": 46.441947565543074
"last_updated": "2025-12-25T00:28:35.544655Z",
"completion_percentage": 46.81647940074906
}
}

View file

@ -3,9 +3,11 @@ package handlers
import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"testing"
"time"
"veza-backend-api/internal/models"
"veza-backend-api/internal/services"
@ -14,7 +16,10 @@ import (
"github.com/golang-jwt/jwt/v5"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
"go.uber.org/zap/zaptest"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
@ -194,3 +199,232 @@ func TestChatHandler_GetToken_Unauthorized(t *testing.T) {
// See lines 41, 46 in handler.
assert.Equal(t, "unauthorized", response["error"])
}
// setupTestChatHandlerWithDB creates a test handler with database for GetStats tests
func setupTestChatHandlerWithDB(t *testing.T) (*ChatHandler, *gin.Engine, *gorm.DB, func()) {
gin.SetMode(gin.TestMode)
logger := zaptest.NewLogger(t)
// Setup in-memory SQLite database
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
require.NoError(t, err)
db.Exec("PRAGMA foreign_keys = ON")
// Auto-migrate models
err = db.AutoMigrate(
&models.User{},
&models.Room{},
&models.Message{},
)
require.NoError(t, err)
jwtSecret := "supersecretchatkey"
chatService := services.NewChatServiceWithDB(jwtSecret, db, logger)
// Mock UserService
mockUserRepo := NewMockUserRepository()
userService := services.NewUserService(mockUserRepo)
handler := NewChatHandler(chatService, userService, logger)
r := gin.New()
r.GET("/chat/stats", handler.GetStats)
cleanup := func() {
// Database cleanup handled by test
}
return handler, r, db, cleanup
}
// TestChatHandler_GetStats_Success tests successful chat stats retrieval
func TestChatHandler_GetStats_Success(t *testing.T) {
_, r, db, cleanup := setupTestChatHandlerWithDB(t)
defer cleanup()
// Create test data
userID := uuid.New()
user := &models.User{
ID: userID,
Username: "testuser",
Email: "test@example.com",
IsActive: true,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err := db.Create(user).Error
require.NoError(t, err)
roomID := uuid.New()
room := &models.Room{
ID: roomID,
Name: "Test Room",
CreatedBy: userID,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err = db.Create(room).Error
require.NoError(t, err)
// Create test messages
for i := 0; i < 3; i++ {
message := &models.Message{
ID: uuid.New(),
RoomID: roomID,
UserID: userID,
Content: fmt.Sprintf("Test message %d", i+1),
Type: "text",
IsDeleted: false,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err = db.Create(message).Error
require.NoError(t, err)
}
req := httptest.NewRequest(http.MethodGet, "/chat/stats", 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)
require.NoError(t, err)
assert.True(t, response.Success)
assert.Nil(t, response.Error)
// Verify stats data
dataBytes, _ := json.Marshal(response.Data)
var stats services.ChatStats
err = json.Unmarshal(dataBytes, &stats)
require.NoError(t, err)
assert.GreaterOrEqual(t, stats.TotalMessages, int64(3))
assert.GreaterOrEqual(t, stats.ActiveUsers, int64(1))
assert.GreaterOrEqual(t, stats.RoomsActive, int64(1))
}
// TestChatHandler_GetStats_NoMessages tests stats when there are no messages
func TestChatHandler_GetStats_NoMessages(t *testing.T) {
_, r, _, cleanup := setupTestChatHandlerWithDB(t)
defer cleanup()
req := httptest.NewRequest(http.MethodGet, "/chat/stats", 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)
require.NoError(t, err)
assert.True(t, response.Success)
// Verify stats are zero
dataBytes, _ := json.Marshal(response.Data)
var stats services.ChatStats
err = json.Unmarshal(dataBytes, &stats)
require.NoError(t, err)
assert.Equal(t, int64(0), stats.TotalMessages)
assert.Equal(t, int64(0), stats.ActiveUsers)
assert.Equal(t, int64(0), stats.RoomsActive)
}
// TestChatHandler_GetToken_InvalidUserID tests GetToken with invalid user ID type
func TestChatHandler_GetToken_InvalidUserID(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.Use(func(c *gin.Context) {
// Set invalid user_id type (string instead of UUID)
c.Set("user_id", "invalid-uuid")
c.Next()
})
r.POST("/chat/token", handler.GetToken)
req := httptest.NewRequest(http.MethodPost, "/chat/token", nil)
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
assert.Equal(t, http.StatusUnauthorized, w.Code)
}
// TestChatHandler_GetToken_NilUserID tests GetToken with nil user ID
func TestChatHandler_GetToken_NilUserID(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.Use(func(c *gin.Context) {
// Set nil UUID
c.Set("user_id", uuid.Nil)
c.Next()
})
r.POST("/chat/token", handler.GetToken)
req := httptest.NewRequest(http.MethodPost, "/chat/token", nil)
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
assert.Equal(t, http.StatusUnauthorized, w.Code)
}
// TestChatHandler_GetToken_UserNotFound tests GetToken when user is not found in DB
func TestChatHandler_GetToken_UserNotFound(t *testing.T) {
logger := zap.NewNop()
jwtSecret := "supersecretchatkey"
chatService := services.NewChatService(jwtSecret, logger)
mockUserRepo := NewMockUserRepository()
// Don't create any user in the mock repo
userService := services.NewUserService(mockUserRepo)
handler := NewChatHandler(chatService, userService, logger)
r := gin.New()
userID := uuid.New()
r.Use(func(c *gin.Context) {
c.Set("user_id", userID)
c.Next()
})
r.POST("/chat/token", handler.GetToken)
req := httptest.NewRequest(http.MethodPost, "/chat/token", nil)
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
// Should still succeed with fallback username
assert.Equal(t, http.StatusOK, w.Code)
var response APIResponse
err := json.Unmarshal(w.Body.Bytes(), &response)
require.NoError(t, err)
assert.True(t, response.Success)
// Verify token has fallback username
dataBytes, _ := json.Marshal(response.Data)
var tokenResponse services.ChatTokenResponse
err = json.Unmarshal(dataBytes, &tokenResponse)
require.NoError(t, err)
assert.NotEmpty(t, tokenResponse.Token)
parsedToken, err := jwt.Parse(tokenResponse.Token, func(token *jwt.Token) (interface{}, error) {
return []byte(jwtSecret), nil
})
require.NoError(t, err)
claims, ok := parsedToken.Claims.(jwt.MapClaims)
assert.True(t, ok)
// Should have fallback username format
expectedUsername := fmt.Sprintf("user_%s", userID)
assert.Equal(t, expectedUsername, claims["name"])
}