- 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
306 lines
8.7 KiB
Go
306 lines
8.7 KiB
Go
package handlers
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"mime/multipart"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"veza-backend-api/internal/services"
|
|
"veza-backend-api/internal/upload"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/mock"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
// MockUploadValidator implements UploadValidatorInterface
|
|
type MockUploadValidator struct {
|
|
mock.Mock
|
|
}
|
|
|
|
func (m *MockUploadValidator) ValidateFile(ctx context.Context, fileHeader *multipart.FileHeader, fileType string) (*services.ValidationResult, error) {
|
|
args := m.Called(ctx, fileHeader, fileType)
|
|
if args.Get(0) == nil {
|
|
return nil, args.Error(1)
|
|
}
|
|
return args.Get(0).(*services.ValidationResult), args.Error(1)
|
|
}
|
|
|
|
func (m *MockUploadValidator) GetFileTypeFromPath(filename string) string {
|
|
args := m.Called(filename)
|
|
return args.String(0)
|
|
}
|
|
|
|
// MockUploadAuditService implements UploadAuditServiceInterface
|
|
type MockUploadAuditService struct {
|
|
mock.Mock
|
|
}
|
|
|
|
func (m *MockUploadAuditService) LogUpload(ctx context.Context, userID uuid.UUID, resourceID uuid.UUID, fileName string, fileSize int64, ipAddress, userAgent string) error {
|
|
args := m.Called(ctx, userID, resourceID, fileName, fileSize, ipAddress, userAgent)
|
|
return args.Error(0)
|
|
}
|
|
|
|
func (m *MockUploadAuditService) LogDeletion(ctx context.Context, userID uuid.UUID, resource string, resourceID uuid.UUID, ipAddress, userAgent string) error {
|
|
args := m.Called(ctx, userID, resource, resourceID, ipAddress, userAgent)
|
|
return args.Error(0)
|
|
}
|
|
|
|
// MockTrackUploadService implements TrackUploadServiceInterface
|
|
type MockTrackUploadService struct {
|
|
mock.Mock
|
|
}
|
|
|
|
func (m *MockTrackUploadService) GetUploadStats(ctx context.Context, userID uuid.UUID) (map[string]interface{}, error) {
|
|
args := m.Called(ctx, userID)
|
|
if args.Get(0) == nil {
|
|
return nil, args.Error(1)
|
|
}
|
|
return args.Get(0).(map[string]interface{}), args.Error(1)
|
|
}
|
|
|
|
// Setup helper
|
|
func setupTestUploadHandler(t *testing.T) (*UploadHandler, *MockUploadValidator, *MockUploadAuditService, *MockTrackUploadService) {
|
|
mockValidator := new(MockUploadValidator)
|
|
mockAudit := new(MockUploadAuditService)
|
|
mockTrackUpload := new(MockTrackUploadService)
|
|
logger := zap.NewNop()
|
|
|
|
handler := NewUploadHandlerWithInterface(mockValidator, mockAudit, mockTrackUpload, logger, 10)
|
|
return handler, mockValidator, mockAudit, mockTrackUpload
|
|
}
|
|
|
|
func TestUploadFile_Success(t *testing.T) {
|
|
handler, mockValidator, mockAudit, _ := setupTestUploadHandler(t)
|
|
|
|
gin.SetMode(gin.TestMode)
|
|
w := httptest.NewRecorder()
|
|
c, _ := gin.CreateTestContext(w)
|
|
|
|
// Create multipart request
|
|
body := new(bytes.Buffer)
|
|
writer := multipart.NewWriter(body)
|
|
part, _ := writer.CreateFormFile("file", "test.mp3")
|
|
part.Write([]byte("dummy content"))
|
|
writer.WriteField("track_id", uuid.New().String())
|
|
writer.WriteField("file_type", "audio")
|
|
writer.WriteField("title", "Test Title")
|
|
writer.WriteField("artist", "Test Artist")
|
|
writer.WriteField("duration", "120")
|
|
writer.Close()
|
|
|
|
req, _ := http.NewRequest("POST", "/upload", body)
|
|
req.Header.Set("Content-Type", writer.FormDataContentType())
|
|
c.Request = req
|
|
|
|
userID := uuid.New()
|
|
c.Set("user_id", userID)
|
|
|
|
// Mocks
|
|
expectedResult := &services.ValidationResult{
|
|
Valid: true,
|
|
FileType: "audio",
|
|
FileSize: 100,
|
|
Checksum: "abc",
|
|
}
|
|
mockValidator.On("ValidateFile", mock.Anything, mock.Anything, "audio").Return(expectedResult, nil)
|
|
mockAudit.On("LogUpload", mock.Anything, userID, mock.Anything, "test.mp3", int64(100), mock.Anything, mock.Anything).Return(nil)
|
|
|
|
// Execute
|
|
handler.UploadFile()(c)
|
|
|
|
// Assert
|
|
if w.Code != http.StatusCreated {
|
|
t.Logf("Response Body: %s", w.Body.String())
|
|
}
|
|
assert.Equal(t, http.StatusCreated, w.Code)
|
|
|
|
var respWrapper struct {
|
|
Success bool `json:"success"`
|
|
Data upload.StandardUploadResponse `json:"data"`
|
|
}
|
|
err := json.Unmarshal(w.Body.Bytes(), &respWrapper)
|
|
assert.NoError(t, err)
|
|
assert.True(t, respWrapper.Success)
|
|
assert.Equal(t, "test.mp3", respWrapper.Data.FileName)
|
|
assert.Equal(t, int64(100), respWrapper.Data.FileSize)
|
|
|
|
mockValidator.AssertExpectations(t)
|
|
mockAudit.AssertExpectations(t)
|
|
}
|
|
|
|
func TestUploadFile_ValidationFailed(t *testing.T) {
|
|
handler, mockValidator, _, _ := setupTestUploadHandler(t)
|
|
|
|
gin.SetMode(gin.TestMode)
|
|
w := httptest.NewRecorder()
|
|
c, _ := gin.CreateTestContext(w)
|
|
|
|
// Create multipart request
|
|
body := new(bytes.Buffer)
|
|
writer := multipart.NewWriter(body)
|
|
part, _ := writer.CreateFormFile("file", "test.mp3")
|
|
part.Write([]byte("dummy content"))
|
|
writer.WriteField("track_id", uuid.New().String())
|
|
writer.WriteField("file_type", "audio")
|
|
writer.WriteField("title", "Test Title")
|
|
writer.WriteField("artist", "Test Artist")
|
|
writer.Close()
|
|
|
|
req, _ := http.NewRequest("POST", "/upload", body)
|
|
req.Header.Set("Content-Type", writer.FormDataContentType())
|
|
c.Request = req
|
|
|
|
userID := uuid.New()
|
|
c.Set("user_id", userID)
|
|
|
|
// Mocks
|
|
expectedResult := &services.ValidationResult{
|
|
Valid: false,
|
|
Error: "Invalid format",
|
|
}
|
|
mockValidator.On("ValidateFile", mock.Anything, mock.Anything, "audio").Return(expectedResult, nil)
|
|
|
|
// Execute
|
|
handler.UploadFile()(c)
|
|
|
|
// Assert
|
|
assert.Equal(t, http.StatusBadRequest, w.Code) // AppError converts ErrCodeValidation to 400
|
|
}
|
|
|
|
func TestUploadFile_ClamAVUnavailable(t *testing.T) {
|
|
handler, mockValidator, _, _ := setupTestUploadHandler(t)
|
|
|
|
gin.SetMode(gin.TestMode)
|
|
w := httptest.NewRecorder()
|
|
c, _ := gin.CreateTestContext(w)
|
|
|
|
// Create multipart request
|
|
body := new(bytes.Buffer)
|
|
writer := multipart.NewWriter(body)
|
|
part, _ := writer.CreateFormFile("file", "test.mp3")
|
|
part.Write([]byte("dummy content"))
|
|
writer.WriteField("track_id", uuid.New().String())
|
|
writer.WriteField("file_type", "audio")
|
|
writer.WriteField("title", "Test Title")
|
|
writer.WriteField("artist", "Test Artist")
|
|
writer.Close()
|
|
|
|
req, _ := http.NewRequest("POST", "/upload", body)
|
|
req.Header.Set("Content-Type", writer.FormDataContentType())
|
|
c.Request = req
|
|
|
|
userID := uuid.New()
|
|
c.Set("user_id", userID)
|
|
|
|
// Mocks
|
|
mockValidator.On("ValidateFile", mock.Anything, mock.Anything, "audio").Return(nil, errors.New("clamav_unavailable"))
|
|
|
|
// Execute
|
|
handler.UploadFile()(c)
|
|
|
|
// Assert
|
|
assert.Equal(t, http.StatusServiceUnavailable, w.Code)
|
|
}
|
|
|
|
func TestUploadFile_VirusDetected(t *testing.T) {
|
|
handler, mockValidator, _, _ := setupTestUploadHandler(t)
|
|
|
|
gin.SetMode(gin.TestMode)
|
|
w := httptest.NewRecorder()
|
|
c, _ := gin.CreateTestContext(w)
|
|
|
|
// Create multipart request
|
|
body := new(bytes.Buffer)
|
|
writer := multipart.NewWriter(body)
|
|
part, _ := writer.CreateFormFile("file", "virus.mp3")
|
|
part.Write([]byte("virus signature"))
|
|
writer.WriteField("track_id", uuid.New().String())
|
|
writer.WriteField("file_type", "audio")
|
|
writer.WriteField("title", "Test Title")
|
|
writer.WriteField("artist", "Test Artist")
|
|
writer.Close()
|
|
|
|
req, _ := http.NewRequest("POST", "/upload", body)
|
|
req.Header.Set("Content-Type", writer.FormDataContentType())
|
|
c.Request = req
|
|
|
|
userID := uuid.New()
|
|
c.Set("user_id", userID)
|
|
|
|
// Mocks
|
|
expectedResult := &services.ValidationResult{
|
|
Valid: false,
|
|
Quarantined: true,
|
|
Error: "Virus detected",
|
|
}
|
|
mockValidator.On("ValidateFile", mock.Anything, mock.Anything, "audio").Return(expectedResult, nil)
|
|
|
|
// Execute
|
|
handler.UploadFile()(c)
|
|
|
|
// Assert
|
|
assert.Equal(t, http.StatusUnprocessableEntity, w.Code)
|
|
}
|
|
|
|
func TestDeleteUpload_Success(t *testing.T) {
|
|
handler, _, mockAudit, _ := setupTestUploadHandler(t)
|
|
|
|
gin.SetMode(gin.TestMode)
|
|
w := httptest.NewRecorder()
|
|
c, _ := gin.CreateTestContext(w)
|
|
|
|
uploadID := uuid.New()
|
|
c.Params = []gin.Param{{Key: "id", Value: uploadID.String()}}
|
|
|
|
userID := uuid.New()
|
|
c.Set("user_id", userID)
|
|
|
|
req, _ := http.NewRequest("DELETE", "/upload/"+uploadID.String(), nil)
|
|
c.Request = req
|
|
|
|
// Mocks
|
|
mockAudit.On("LogDeletion", mock.Anything, userID, "upload", uploadID, mock.Anything, mock.Anything).Return(nil)
|
|
|
|
// Execute
|
|
handler.DeleteUpload()(c)
|
|
|
|
// Assert
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
mockAudit.AssertExpectations(t)
|
|
}
|
|
|
|
func TestGetUploadStats_Success(t *testing.T) {
|
|
handler, _, _, mockTrackUpload := setupTestUploadHandler(t)
|
|
|
|
gin.SetMode(gin.TestMode)
|
|
w := httptest.NewRecorder()
|
|
c, _ := gin.CreateTestContext(w)
|
|
|
|
userID := uuid.New()
|
|
c.Set("user_id", userID) // GetUserIDUUID looks for "user_id" in context which is UUID
|
|
|
|
req, _ := http.NewRequest("GET", "/stats", nil)
|
|
c.Request = req
|
|
|
|
// Mocks
|
|
stats := map[string]interface{}{"total_uploads": int64(5)}
|
|
mockTrackUpload.On("GetUploadStats", mock.Anything, userID).Return(stats, nil)
|
|
|
|
// Execute
|
|
handler.GetUploadStats()(c)
|
|
|
|
// Assert
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
|
|
var resp map[string]interface{}
|
|
json.Unmarshal(w.Body.Bytes(), &resp)
|
|
assert.Equal(t, float64(5), resp["stats"].(map[string]interface{})["total_uploads"])
|
|
}
|