306 lines
10 KiB
Go
306 lines
10 KiB
Go
package v1
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/gochat/gochat/internal/service"
|
|
)
|
|
|
|
// setupConversationEdgeRouter creates a test router for conversation handler edge-case tests.
|
|
|
|
|
|
|
|
func setupConversationEdgeRouter(handler *ConversationHandler) *gin.Engine {
|
|
gin.SetMode(gin.TestMode)
|
|
router := gin.New()
|
|
router.Use(gin.Recovery(), mockAuthMiddleware())
|
|
router.POST("/api/v1/accounts/:account_id/conversations", handler.Create)
|
|
router.POST("/api/v1/accounts/:account_id/conversations/filter", handler.Filter)
|
|
router.PATCH("/api/v1/accounts/:account_id/conversations/:id", handler.Update)
|
|
router.GET("/api/v1/accounts/:account_id/conversations", handler.List)
|
|
router.GET("/api/v1/accounts/:account_id/conversations/:id", handler.Get)
|
|
router.DELETE("/api/v1/accounts/:account_id/conversations/:id", handler.Delete)
|
|
return router
|
|
}
|
|
|
|
func newConversationEdgeHandler() *ConversationHandler {
|
|
return NewConversationHandler(&service.ConversationService{}, &service.MessageService{})
|
|
}
|
|
|
|
// ===========================
|
|
// Create Conversation edge cases
|
|
// ===========================
|
|
|
|
func TestConversationCreate_EmptyBody(t *testing.T) {
|
|
handler := newConversationEdgeHandler()
|
|
router := setupConversationEdgeRouter(handler)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("POST", "/api/v1/accounts/1/conversations", nil)
|
|
req.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(w, req)
|
|
|
|
// ShouldBindJSON on nil body returns EOF error → 400
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
var resp map[string]interface{}
|
|
json.Unmarshal(w.Body.Bytes(), &resp)
|
|
assert.Contains(t, resp, "error")
|
|
}
|
|
|
|
func TestConversationCreate_InvalidJSON(t *testing.T) {
|
|
handler := newConversationEdgeHandler()
|
|
router := setupConversationEdgeRouter(handler)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("POST", "/api/v1/accounts/1/conversations", bytes.NewBufferString(`{invalid json`))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
var resp map[string]interface{}
|
|
json.Unmarshal(w.Body.Bytes(), &resp)
|
|
assert.Contains(t, resp, "error")
|
|
}
|
|
|
|
func TestConversationCreate_MissingRequiredFields(t *testing.T) {
|
|
handler := newConversationEdgeHandler()
|
|
router := setupConversationEdgeRouter(handler)
|
|
|
|
// inbox_id and contact_id are required (validate:"required")
|
|
w := httptest.NewRecorder()
|
|
body := `{"status":"open"}`
|
|
req, _ := http.NewRequest("POST", "/api/v1/accounts/1/conversations", bytes.NewBufferString(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(w, req)
|
|
|
|
// ShouldBindJSON succeeds (no syntax error), but the zero-service will fail when calling Create
|
|
// with validation error. Since service is nil, it panics → Recovery returns 500.
|
|
// But the handler should at least not crash. We just check it doesn't return 200.
|
|
assert.NotEqual(t, http.StatusOK, w.Code)
|
|
}
|
|
|
|
func TestConversationCreate_InvalidAccountID(t *testing.T) {
|
|
handler := newConversationEdgeHandler()
|
|
router := setupConversationEdgeRouter(handler)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("POST", "/api/v1/accounts/abc/conversations", bytes.NewBufferString(`{"inbox_id":1,"contact_id":1}`))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
func TestConversationCreate_OutOfRangeStatus(t *testing.T) {
|
|
handler := newConversationEdgeHandler()
|
|
router := setupConversationEdgeRouter(handler)
|
|
|
|
// status must be oneof=open resolved pending snoozed; "invalid_status" is out of range
|
|
w := httptest.NewRecorder()
|
|
body := `{"inbox_id":1,"contact_id":1,"status":"invalid_status"}`
|
|
req, _ := http.NewRequest("POST", "/api/v1/accounts/1/conversations", bytes.NewBufferString(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(w, req)
|
|
|
|
// ShouldBindJSON succeeds (JSON syntax is valid), but service validation should reject
|
|
// With nil service, handler calls service.Create which panics → 500 from Recovery
|
|
assert.NotEqual(t, http.StatusOK, w.Code)
|
|
}
|
|
|
|
// ===========================
|
|
// Filter Conversation edge cases
|
|
// ===========================
|
|
|
|
func TestConversationFilter_EmptyBody(t *testing.T) {
|
|
handler := newConversationEdgeHandler()
|
|
router := setupConversationEdgeRouter(handler)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("POST", "/api/v1/accounts/1/conversations/filter", nil)
|
|
req.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
func TestConversationFilter_InvalidJSON(t *testing.T) {
|
|
handler := newConversationEdgeHandler()
|
|
router := setupConversationEdgeRouter(handler)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("POST", "/api/v1/accounts/1/conversations/filter", bytes.NewBufferString(`{broken`))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
func TestConversationFilter_InvalidAccountID(t *testing.T) {
|
|
handler := newConversationEdgeHandler()
|
|
router := setupConversationEdgeRouter(handler)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("POST", "/api/v1/accounts/notanumber/conversations/filter", bytes.NewBufferString(`{"status":"open"}`))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
func TestConversationFilter_OutOfRangeParams(t *testing.T) {
|
|
handler := newConversationEdgeHandler()
|
|
router := setupConversationEdgeRouter(handler)
|
|
|
|
// status must be oneof=open resolved pending snoozed; "closed" is invalid
|
|
w := httptest.NewRecorder()
|
|
body := `{"status":"closed"}`
|
|
req, _ := http.NewRequest("POST", "/api/v1/accounts/1/conversations/filter", bytes.NewBufferString(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(w, req)
|
|
|
|
// ShouldBindJSON succeeds, but service.Filter will fail with nil db → 500 from Recovery
|
|
assert.NotEqual(t, http.StatusOK, w.Code)
|
|
}
|
|
|
|
// ===========================
|
|
// Update Conversation edge cases
|
|
// ===========================
|
|
|
|
func TestConversationUpdate_EmptyBody(t *testing.T) {
|
|
handler := newConversationEdgeHandler()
|
|
router := setupConversationEdgeRouter(handler)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("PATCH", "/api/v1/accounts/1/conversations/1", nil)
|
|
req.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
func TestConversationUpdate_InvalidJSON(t *testing.T) {
|
|
handler := newConversationEdgeHandler()
|
|
router := setupConversationEdgeRouter(handler)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("PATCH", "/api/v1/accounts/1/conversations/1", bytes.NewBufferString(`{"status":`))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
func TestConversationUpdate_InvalidAccountID(t *testing.T) {
|
|
handler := newConversationEdgeHandler()
|
|
router := setupConversationEdgeRouter(handler)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("PATCH", "/api/v1/accounts/abc/conversations/1", bytes.NewBufferString(`{"status":"open"}`))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
func TestConversationUpdate_InvalidConversationID(t *testing.T) {
|
|
handler := newConversationEdgeHandler()
|
|
router := setupConversationEdgeRouter(handler)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("PATCH", "/api/v1/accounts/1/conversations/xyz", bytes.NewBufferString(`{"status":"open"}`))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
func TestConversationUpdate_OutOfRangePriority(t *testing.T) {
|
|
handler := newConversationEdgeHandler()
|
|
router := setupConversationEdgeRouter(handler)
|
|
|
|
// priority must be oneof=urgent high medium low; "extreme" is invalid
|
|
w := httptest.NewRecorder()
|
|
body := `{"priority":"extreme"}`
|
|
req, _ := http.NewRequest("PATCH", "/api/v1/accounts/1/conversations/1", bytes.NewBufferString(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(w, req)
|
|
|
|
// ShouldBindJSON succeeds, but service rejects via validation → 500 from nil service
|
|
assert.NotEqual(t, http.StatusOK, w.Code)
|
|
}
|
|
|
|
// ===========================
|
|
// List Conversation edge cases
|
|
// ===========================
|
|
|
|
func TestConversationList_InvalidAccountID(t *testing.T) {
|
|
handler := newConversationEdgeHandler()
|
|
router := setupConversationEdgeRouter(handler)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("GET", "/api/v1/accounts/invalid/conversations", nil)
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
func TestConversationList_WrongMethod(t *testing.T) {
|
|
handler := newConversationEdgeHandler()
|
|
router := setupConversationEdgeRouter(handler)
|
|
|
|
// List is GET-only; sending DELETE should return 404 (no route match)
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("DELETE", "/api/v1/accounts/1/conversations", nil)
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusNotFound, w.Code)
|
|
}
|
|
|
|
// ===========================
|
|
// Get Conversation edge cases
|
|
// ===========================
|
|
|
|
func TestConversationGet_InvalidAccountID(t *testing.T) {
|
|
handler := newConversationEdgeHandler()
|
|
router := setupConversationEdgeRouter(handler)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("GET", "/api/v1/accounts/abc/conversations/1", nil)
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
func TestConversationGet_InvalidConversationID(t *testing.T) {
|
|
handler := newConversationEdgeHandler()
|
|
router := setupConversationEdgeRouter(handler)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("GET", "/api/v1/accounts/1/conversations/notanumber", nil)
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
// ===========================
|
|
// Delete Conversation edge cases
|
|
// ===========================
|
|
|
|
func TestConversationDelete_WrongMethod(t *testing.T) {
|
|
handler := newConversationEdgeHandler()
|
|
router := setupConversationEdgeRouter(handler)
|
|
|
|
// PUT on the DELETE-only route returns 404 (no route match)
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("PUT", "/api/v1/accounts/1/conversations/1", nil)
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusNotFound, w.Code)
|
|
} |