267 lines
9.0 KiB
Go
267 lines
9.0 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"
|
|
)
|
|
|
|
// setupMessageEdgeRouter creates a test router for message handler edge-case tests.
|
|
|
|
|
|
|
|
func setupMessageEdgeRouter(handler *MessageHandler) *gin.Engine {
|
|
gin.SetMode(gin.TestMode)
|
|
router := gin.New()
|
|
router.Use(gin.Recovery(), mockAuthMiddleware())
|
|
router.POST("/api/v1/accounts/:account_id/conversations/:conversation_id/messages", handler.Create)
|
|
router.GET("/api/v1/accounts/:account_id/conversations/:conversation_id/messages", handler.List)
|
|
router.GET("/api/v1/accounts/:account_id/messages/search", handler.Search)
|
|
router.PATCH("/api/v1/accounts/:account_id/conversations/:conversation_id/messages/:id", handler.Update)
|
|
router.DELETE("/api/v1/accounts/:account_id/conversations/:conversation_id/messages/:id", handler.Delete)
|
|
return router
|
|
}
|
|
|
|
func newMessageEdgeHandler() *MessageHandler {
|
|
return NewMessageHandler(&service.MessageService{})
|
|
}
|
|
|
|
// ===========================
|
|
// Create Message edge cases
|
|
// ===========================
|
|
|
|
func TestMessageCreate_EmptyBody(t *testing.T) {
|
|
handler := newMessageEdgeHandler()
|
|
router := setupMessageEdgeRouter(handler)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("POST", "/api/v1/accounts/1/conversations/1/messages", nil)
|
|
req.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(w, req)
|
|
|
|
// ShouldBindJSON on empty body returns EOF → 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 TestMessageCreate_InvalidJSON(t *testing.T) {
|
|
handler := newMessageEdgeHandler()
|
|
router := setupMessageEdgeRouter(handler)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("POST", "/api/v1/accounts/1/conversations/1/messages", bytes.NewBufferString(`{broken json`))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
func TestMessageCreate_MissingRequiredFields(t *testing.T) {
|
|
handler := newMessageEdgeHandler()
|
|
router := setupMessageEdgeRouter(handler)
|
|
|
|
// content is required (validate:"required,min=1"); message_type is required (validate:"required,oneof=...")
|
|
w := httptest.NewRecorder()
|
|
body := `{"conversation_id":1}`
|
|
req, _ := http.NewRequest("POST", "/api/v1/accounts/1/conversations/1/messages", bytes.NewBufferString(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(w, req)
|
|
|
|
// ShouldBindJSON succeeds, but service.Create rejects via validation → nil service panics → 500
|
|
assert.NotEqual(t, http.StatusOK, w.Code)
|
|
assert.NotEqual(t, http.StatusCreated, w.Code)
|
|
}
|
|
|
|
func TestMessageCreate_InvalidMessageType(t *testing.T) {
|
|
handler := newMessageEdgeHandler()
|
|
router := setupMessageEdgeRouter(handler)
|
|
|
|
// message_type must be oneof=outgoing incoming activity template private_note; "invalid" is not valid
|
|
w := httptest.NewRecorder()
|
|
body := `{"conversation_id":1,"content":"hello","message_type":"invalid_type"}`
|
|
req, _ := http.NewRequest("POST", "/api/v1/accounts/1/conversations/1/messages", bytes.NewBufferString(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(w, req)
|
|
|
|
// ShouldBindJSON succeeds, but service rejects via validation → nil service panics → 500
|
|
assert.NotEqual(t, http.StatusCreated, w.Code)
|
|
}
|
|
|
|
func TestMessageCreate_InvalidContentType(t *testing.T) {
|
|
handler := newMessageEdgeHandler()
|
|
router := setupMessageEdgeRouter(handler)
|
|
|
|
// content_type must be oneof=text input_text input_email input_phone select card private_note; "video" is not valid
|
|
w := httptest.NewRecorder()
|
|
body := `{"conversation_id":1,"content":"hello","message_type":"outgoing","content_type":"video"}`
|
|
req, _ := http.NewRequest("POST", "/api/v1/accounts/1/conversations/1/messages", bytes.NewBufferString(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.NotEqual(t, http.StatusCreated, w.Code)
|
|
}
|
|
|
|
func TestMessageCreate_InvalidAccountID(t *testing.T) {
|
|
handler := newMessageEdgeHandler()
|
|
router := setupMessageEdgeRouter(handler)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("POST", "/api/v1/accounts/abc/conversations/1/messages", bytes.NewBufferString(`{"content":"hi","message_type":"outgoing","conversation_id":1}`))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
func TestMessageCreate_InvalidConversationID(t *testing.T) {
|
|
handler := newMessageEdgeHandler()
|
|
router := setupMessageEdgeRouter(handler)
|
|
|
|
// conversation_id in URL is not a uint
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("POST", "/api/v1/accounts/1/conversations/xyz/messages", bytes.NewBufferString(`{"content":"hi","message_type":"outgoing"}`))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
func TestMessageCreate_WrongMethod(t *testing.T) {
|
|
handler := newMessageEdgeHandler()
|
|
router := setupMessageEdgeRouter(handler)
|
|
|
|
// Create is POST-only; GET should hit List route (different behavior)
|
|
// Let's test DELETE on a POST-only sub-route → 404 for messages/:id with DELETE is valid
|
|
// Let's test PATCH on the Create route → 404
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("PATCH", "/api/v1/accounts/1/conversations/1/messages", nil)
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusNotFound, w.Code)
|
|
}
|
|
|
|
// ===========================
|
|
// Search Message edge cases
|
|
// ===========================
|
|
|
|
func TestMessageSearch_InvalidAccountID(t *testing.T) {
|
|
handler := newMessageEdgeHandler()
|
|
router := setupMessageEdgeRouter(handler)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("GET", "/api/v1/accounts/abc/messages/search?q=test", nil)
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
func TestMessageSearch_MissingQueryParam(t *testing.T) {
|
|
handler := newMessageEdgeHandler()
|
|
router := setupMessageEdgeRouter(handler)
|
|
|
|
// q is required for search
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("GET", "/api/v1/accounts/1/messages/search", nil)
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
func TestMessageSearch_WrongMethod(t *testing.T) {
|
|
handler := newMessageEdgeHandler()
|
|
router := setupMessageEdgeRouter(handler)
|
|
|
|
// Search is GET-only; POST should return 404
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("POST", "/api/v1/accounts/1/messages/search?q=test", nil)
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusNotFound, w.Code)
|
|
}
|
|
|
|
// ===========================
|
|
// Update Message edge cases
|
|
// ===========================
|
|
|
|
func TestMessageUpdate_EmptyBody(t *testing.T) {
|
|
handler := newMessageEdgeHandler()
|
|
router := setupMessageEdgeRouter(handler)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("PATCH", "/api/v1/accounts/1/conversations/1/messages/1", nil)
|
|
req.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
func TestMessageUpdate_InvalidJSON(t *testing.T) {
|
|
handler := newMessageEdgeHandler()
|
|
router := setupMessageEdgeRouter(handler)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("PATCH", "/api/v1/accounts/1/conversations/1/messages/1", bytes.NewBufferString(`{broken`))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
func TestMessageUpdate_InvalidAccountID(t *testing.T) {
|
|
handler := newMessageEdgeHandler()
|
|
router := setupMessageEdgeRouter(handler)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("PATCH", "/api/v1/accounts/notvalid/conversations/1/messages/1", bytes.NewBufferString(`{"content":"updated"}`))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
func TestMessageUpdate_InvalidMessageID(t *testing.T) {
|
|
handler := newMessageEdgeHandler()
|
|
router := setupMessageEdgeRouter(handler)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("PATCH", "/api/v1/accounts/1/conversations/1/messages/abc", bytes.NewBufferString(`{"content":"updated"}`))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
// ===========================
|
|
// Delete Message edge cases
|
|
// ===========================
|
|
|
|
func TestMessageDelete_InvalidAccountID(t *testing.T) {
|
|
handler := newMessageEdgeHandler()
|
|
router := setupMessageEdgeRouter(handler)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("DELETE", "/api/v1/accounts/xyz/conversations/1/messages/1", nil)
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
func TestMessageDelete_InvalidMessageID(t *testing.T) {
|
|
handler := newMessageEdgeHandler()
|
|
router := setupMessageEdgeRouter(handler)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("DELETE", "/api/v1/accounts/1/conversations/1/messages/abc", nil)
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
} |