package handlers import ( "bytes" "context" "encoding/json" "net/http" "net/http/httptest" "testing" "veza-backend-api/internal/services" "github.com/gin-gonic/gin" "github.com/google/uuid" "go.uber.org/zap" ) // MockRoomService implements RoomServiceInterface for testing type MockRoomService struct { CreateRoomFunc func(ctx context.Context, userID uuid.UUID, req services.CreateRoomRequest) (*services.RoomResponse, error) GetUserRoomsFunc func(ctx context.Context, userID uuid.UUID) ([]*services.RoomResponse, error) GetRoomFunc func(ctx context.Context, roomID uuid.UUID) (*services.RoomResponse, error) UpdateRoomFunc func(ctx context.Context, roomID uuid.UUID, userID uuid.UUID, req services.UpdateRoomRequest) (*services.RoomResponse, error) AddMemberFunc func(ctx context.Context, roomID, userID uuid.UUID) error RemoveMemberFunc func(ctx context.Context, roomID, userID uuid.UUID) error GetRoomHistoryFunc func(ctx context.Context, roomID uuid.UUID, limit, offset int) ([]services.ChatMessageResponse, error) DeleteRoomFunc func(ctx context.Context, roomID uuid.UUID, userID uuid.UUID) error } func (m *MockRoomService) CreateRoom(ctx context.Context, userID uuid.UUID, req services.CreateRoomRequest) (*services.RoomResponse, error) { if m.CreateRoomFunc != nil { return m.CreateRoomFunc(ctx, userID, req) } return nil, nil } func (m *MockRoomService) GetUserRooms(ctx context.Context, userID uuid.UUID) ([]*services.RoomResponse, error) { if m.GetUserRoomsFunc != nil { return m.GetUserRoomsFunc(ctx, userID) } return nil, nil } func (m *MockRoomService) GetRoom(ctx context.Context, roomID uuid.UUID) (*services.RoomResponse, error) { if m.GetRoomFunc != nil { return m.GetRoomFunc(ctx, roomID) } return nil, nil } func (m *MockRoomService) AddMember(ctx context.Context, roomID, userID uuid.UUID) error { if m.AddMemberFunc != nil { return m.AddMemberFunc(ctx, roomID, userID) } return nil } func (m *MockRoomService) GetRoomHistory(ctx context.Context, roomID uuid.UUID, limit, offset int) ([]services.ChatMessageResponse, error) { if m.GetRoomHistoryFunc != nil { return m.GetRoomHistoryFunc(ctx, roomID, limit, offset) } return nil, nil } func (m *MockRoomService) UpdateRoom(ctx context.Context, roomID uuid.UUID, userID uuid.UUID, req services.UpdateRoomRequest) (*services.RoomResponse, error) { if m.UpdateRoomFunc != nil { return m.UpdateRoomFunc(ctx, roomID, userID, req) } return nil, nil } func (m *MockRoomService) RemoveMember(ctx context.Context, roomID, userID uuid.UUID) error { if m.RemoveMemberFunc != nil { return m.RemoveMemberFunc(ctx, roomID, userID) } return nil } func (m *MockRoomService) DeleteRoom(ctx context.Context, roomID uuid.UUID, userID uuid.UUID) error { if m.DeleteRoomFunc != nil { return m.DeleteRoomFunc(ctx, roomID, userID) } return nil } func TestRoomHandler_CreateRoom(t *testing.T) { // Setup gin.SetMode(gin.TestMode) logger := zap.NewNop() userID := uuid.New() tests := []struct { name string setupMock func() *MockRoomService requestBody interface{} setupContext func(*gin.Context) expectedStatus int }{ { name: "Success", setupMock: func() *MockRoomService { return &MockRoomService{ CreateRoomFunc: func(ctx context.Context, uid uuid.UUID, req services.CreateRoomRequest) (*services.RoomResponse, error) { return &services.RoomResponse{ ID: uuid.New(), Name: req.Name, Type: req.Type, }, nil }, } }, requestBody: services.CreateRoomRequest{ Name: "General", Type: "public", }, setupContext: func(c *gin.Context) { c.Set("user_id", userID) }, expectedStatus: http.StatusCreated, }, { name: "Unauthorized", setupMock: func() *MockRoomService { return &MockRoomService{} }, requestBody: services.CreateRoomRequest{Name: "Test"}, setupContext: func(c *gin.Context) { // No user_id set }, expectedStatus: http.StatusUnauthorized, }, { name: "Invalid Payload", setupMock: func() *MockRoomService { return &MockRoomService{} }, requestBody: "invalid-json", // String instead of struct setupContext: func(c *gin.Context) { c.Set("user_id", userID) }, expectedStatus: http.StatusBadRequest, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { mockService := tt.setupMock() handler := NewRoomHandler(mockService, logger) w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) // Setup request c.Request, _ = http.NewRequest(http.MethodPost, "/conversations", nil) if body, ok := tt.requestBody.(string); ok && body == "invalid-json" { c.Request.Body = &closingBuffer{bytes.NewBufferString("invalid-json")} } else { jsonBytes, _ := json.Marshal(tt.requestBody) c.Request.Body = &closingBuffer{bytes.NewBuffer(jsonBytes)} } c.Request.Header.Set("Content-Type", "application/json") // Setup context (auth) tt.setupContext(c) // Execute handler.CreateRoom(c) // Assert if w.Code != tt.expectedStatus { t.Errorf("Expected status %d, got %d. Body: %s", tt.expectedStatus, w.Code, w.Body.String()) } }) } } // closingBuffer helps to mock ReadCloser type closingBuffer struct { *bytes.Buffer } func (cb *closingBuffer) Close() error { return nil }