second commit
This commit is contained in:
@@ -0,0 +1,267 @@
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user