veza/veza-backend-api/internal/handlers/frontend_log_handler_test.go
senke b28d0e7eac [T0-006] test(backend): Ajout tests pour frontend_log_handler
- Tests complets pour frontend_log_handler.go (12 tests)
- Tests couvrent NewFrontendLogHandler et ReceiveLog
- Tests pour tous les niveaux de log (DEBUG, INFO, WARN, ERROR)
- Tests pour gestion des erreurs et validation JSON
- Couverture actuelle: 30.6% (objectif: 80%)

Files: veza-backend-api/internal/handlers/frontend_log_handler_test.go
       VEZA_ROADMAP.json
Hours: 16 estimated, 23 actual
2026-01-04 01:44:22 +01:00

388 lines
10 KiB
Go

package handlers
import (
"bytes"
"encoding/json"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"testing"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
"go.uber.org/zap/zaptest"
"veza-backend-api/internal/config"
)
func setupTestFrontendLogHandler(t *testing.T) (*FrontendLogHandler, string, func()) {
// Create a temporary directory for logs
tempDir := filepath.Join(os.TempDir(), "veza_test_logs_"+t.Name())
err := os.MkdirAll(tempDir, 0755)
require.NoError(t, err)
// Setup test config
cfg := &config.Config{
LogDir: tempDir,
Env: "test",
LogLevel: "debug",
}
logger := zaptest.NewLogger(t, zaptest.Level(zap.InfoLevel))
handler, err := NewFrontendLogHandler(cfg, logger)
require.NoError(t, err)
cleanup := func() {
os.RemoveAll(tempDir)
}
return handler, tempDir, cleanup
}
func TestNewFrontendLogHandler_Success(t *testing.T) {
// Setup
tempDir := filepath.Join(os.TempDir(), "veza_test_logs_"+t.Name())
defer os.RemoveAll(tempDir)
cfg := &config.Config{
LogDir: tempDir,
Env: "test",
LogLevel: "debug",
}
logger := zaptest.NewLogger(t, zaptest.Level(zap.InfoLevel))
// Execute
handler, err := NewFrontendLogHandler(cfg, logger)
// Assert
assert.NoError(t, err)
assert.NotNil(t, handler)
assert.Equal(t, tempDir, handler.logDir)
assert.NotNil(t, handler.frontendLogger)
assert.NotNil(t, handler.commonHandler)
}
func TestNewFrontendLogHandler_DefaultLogDir(t *testing.T) {
// Setup
cfg := &config.Config{
LogDir: "", // Empty log dir
Env: "development",
LogLevel: "debug",
}
logger := zaptest.NewLogger(t, zaptest.Level(zap.InfoLevel))
// Execute - Should fallback to ./logs in development
handler, err := NewFrontendLogHandler(cfg, logger)
// Assert
if err == nil {
// If no error, verify handler is created
assert.NotNil(t, handler)
// Cleanup
if handler != nil {
os.RemoveAll(handler.logDir)
}
} else {
// If error, it's expected in test environment
assert.Error(t, err)
}
}
func TestNewFrontendLogHandler_DevFallback(t *testing.T) {
// Setup - Use a non-writable directory to trigger fallback
cfg := &config.Config{
LogDir: "/root/nonexistent", // Non-writable in test
Env: "development",
LogLevel: "debug",
}
logger := zaptest.NewLogger(t, zaptest.Level(zap.InfoLevel))
// Execute - Should fallback to ./logs
handler, err := NewFrontendLogHandler(cfg, logger)
// Assert
if err == nil {
assert.NotNil(t, handler)
// Should use fallback directory
assert.Contains(t, handler.logDir, "logs")
// Cleanup
os.RemoveAll(handler.logDir)
} else {
// Error is acceptable in test environment
assert.Error(t, err)
}
}
func TestFrontendLogHandler_ReceiveLog_Success(t *testing.T) {
// Setup
handler, tempDir, cleanup := setupTestFrontendLogHandler(t)
defer cleanup()
gin.SetMode(gin.TestMode)
router := gin.New()
router.POST("/api/v1/logs/frontend", handler.ReceiveLog)
// Execute
logReq := FrontendLogRequest{
Timestamp: "2024-01-01T00:00:00Z",
Level: "INFO",
Message: "Test log message",
Context: map[string]interface{}{
"user_id": "123",
},
}
body, _ := json.Marshal(logReq)
req, _ := http.NewRequest("POST", "/api/v1/logs/frontend", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
// Assert
assert.Equal(t, http.StatusOK, w.Code)
var response map[string]interface{}
err := json.Unmarshal(w.Body.Bytes(), &response)
assert.NoError(t, err)
assert.True(t, response["success"].(bool))
data := response["data"].(map[string]interface{})
assert.True(t, data["received"].(bool))
assert.Equal(t, "INFO", data["level"].(string))
// Verify log file was created
logFiles, err := filepath.Glob(filepath.Join(tempDir, "frontend*.log"))
assert.NoError(t, err)
assert.NotEmpty(t, logFiles)
}
func TestFrontendLogHandler_ReceiveLog_AllLevels(t *testing.T) {
// Setup
handler, _, cleanup := setupTestFrontendLogHandler(t)
defer cleanup()
gin.SetMode(gin.TestMode)
router := gin.New()
router.POST("/api/v1/logs/frontend", handler.ReceiveLog)
levels := []string{"DEBUG", "INFO", "WARN", "ERROR"}
for _, level := range levels {
t.Run(level, func(t *testing.T) {
logReq := FrontendLogRequest{
Timestamp: "2024-01-01T00:00:00Z",
Level: level,
Message: "Test " + level + " message",
}
body, _ := json.Marshal(logReq)
req, _ := http.NewRequest("POST", "/api/v1/logs/frontend", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
var response map[string]interface{}
err := json.Unmarshal(w.Body.Bytes(), &response)
assert.NoError(t, err)
assert.True(t, response["success"].(bool))
data := response["data"].(map[string]interface{})
assert.Equal(t, level, data["level"].(string))
})
}
}
func TestFrontendLogHandler_ReceiveLog_DefaultLevel(t *testing.T) {
// Setup
handler, _, cleanup := setupTestFrontendLogHandler(t)
defer cleanup()
gin.SetMode(gin.TestMode)
router := gin.New()
router.POST("/api/v1/logs/frontend", handler.ReceiveLog)
// Execute - No level specified
logReq := FrontendLogRequest{
Timestamp: "2024-01-01T00:00:00Z",
Message: "Test message without level",
}
body, _ := json.Marshal(logReq)
req, _ := http.NewRequest("POST", "/api/v1/logs/frontend", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
// Assert
assert.Equal(t, http.StatusOK, w.Code)
var response map[string]interface{}
err := json.Unmarshal(w.Body.Bytes(), &response)
assert.NoError(t, err)
assert.True(t, response["success"].(bool))
data := response["data"].(map[string]interface{})
assert.Equal(t, "INFO", data["level"].(string)) // Default level
}
func TestFrontendLogHandler_ReceiveLog_WithRequestID(t *testing.T) {
// Setup
handler, _, cleanup := setupTestFrontendLogHandler(t)
defer cleanup()
gin.SetMode(gin.TestMode)
router := gin.New()
router.POST("/api/v1/logs/frontend", handler.ReceiveLog)
// Execute
logReq := FrontendLogRequest{
Timestamp: "2024-01-01T00:00:00Z",
Level: "INFO",
Message: "Test message with request ID",
Context: map[string]interface{}{
"request_id": "req-123-456",
"user_id": "user-789",
},
}
body, _ := json.Marshal(logReq)
req, _ := http.NewRequest("POST", "/api/v1/logs/frontend", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
// Assert
assert.Equal(t, http.StatusOK, w.Code)
var response map[string]interface{}
err := json.Unmarshal(w.Body.Bytes(), &response)
assert.NoError(t, err)
assert.True(t, response["success"].(bool))
}
func TestFrontendLogHandler_ReceiveLog_WithData(t *testing.T) {
// Setup
handler, _, cleanup := setupTestFrontendLogHandler(t)
defer cleanup()
gin.SetMode(gin.TestMode)
router := gin.New()
router.POST("/api/v1/logs/frontend", handler.ReceiveLog)
// Execute
logReq := FrontendLogRequest{
Timestamp: "2024-01-01T00:00:00Z",
Level: "ERROR",
Message: "Test error with data",
Data: map[string]interface{}{
"error_code": "E001",
"stack": "Error stack trace",
},
}
body, _ := json.Marshal(logReq)
req, _ := http.NewRequest("POST", "/api/v1/logs/frontend", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
// Assert
assert.Equal(t, http.StatusOK, w.Code)
var response map[string]interface{}
err := json.Unmarshal(w.Body.Bytes(), &response)
assert.NoError(t, err)
assert.True(t, response["success"].(bool))
}
func TestFrontendLogHandler_ReceiveLog_InvalidJSON(t *testing.T) {
// Setup
handler, _, cleanup := setupTestFrontendLogHandler(t)
defer cleanup()
gin.SetMode(gin.TestMode)
router := gin.New()
router.POST("/api/v1/logs/frontend", handler.ReceiveLog)
// Execute - Invalid JSON
req, _ := http.NewRequest("POST", "/api/v1/logs/frontend", bytes.NewBufferString("invalid json"))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
// Assert
assert.Equal(t, http.StatusBadRequest, w.Code)
var response map[string]interface{}
err := json.Unmarshal(w.Body.Bytes(), &response)
assert.NoError(t, err)
assert.False(t, response["success"].(bool))
}
func TestFrontendLogHandler_ReceiveLog_EmptyBody(t *testing.T) {
// Setup
handler, _, cleanup := setupTestFrontendLogHandler(t)
defer cleanup()
gin.SetMode(gin.TestMode)
router := gin.New()
router.POST("/api/v1/logs/frontend", handler.ReceiveLog)
// Execute - Empty body
req, _ := http.NewRequest("POST", "/api/v1/logs/frontend", bytes.NewBufferString("{}"))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
// Assert
assert.Equal(t, http.StatusOK, w.Code) // Empty body is valid, defaults are used
var response map[string]interface{}
err := json.Unmarshal(w.Body.Bytes(), &response)
assert.NoError(t, err)
assert.True(t, response["success"].(bool))
}
func TestFrontendLogHandler_ReceiveLog_UnknownLevel(t *testing.T) {
// Setup
handler, _, cleanup := setupTestFrontendLogHandler(t)
defer cleanup()
gin.SetMode(gin.TestMode)
router := gin.New()
router.POST("/api/v1/logs/frontend", handler.ReceiveLog)
// Execute - Unknown level
logReq := FrontendLogRequest{
Timestamp: "2024-01-01T00:00:00Z",
Level: "UNKNOWN",
Message: "Test message with unknown level",
}
body, _ := json.Marshal(logReq)
req, _ := http.NewRequest("POST", "/api/v1/logs/frontend", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
// Assert - Should default to INFO
assert.Equal(t, http.StatusOK, w.Code)
var response map[string]interface{}
err := json.Unmarshal(w.Body.Bytes(), &response)
assert.NoError(t, err)
assert.True(t, response["success"].(bool))
data := response["data"].(map[string]interface{})
assert.Equal(t, "UNKNOWN", data["level"].(string)) // Level is preserved in response
}