Files
gochat/internal/handler/api/v1/conversation_participant_handler_test.go
T
2026-06-04 15:44:48 +08:00

262 lines
8.9 KiB
Go

package v1
import (
"bytes"
"encoding/json"
"net/http"
"net/http/httptest"
"strconv"
"testing"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"github.com/gochat/gochat/internal/model"
"github.com/gochat/gochat/internal/repository"
"github.com/gochat/gochat/internal/service"
)
// --- ConversationParticipant Handler Test Suite ---
type ConversationParticipantHandlerTestSuite struct {
suite.Suite
router *gin.Engine
db *gorm.DB
testAccount *model.Account
testUser *model.User
testConv *model.Conversation
}
func (s *ConversationParticipantHandlerTestSuite) SetupSuite() {
gin.SetMode(gin.TestMode)
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
Logger: logger.Default.LogMode(logger.Silent),
})
s.Require().NoError(err)
s.db = db
err = db.AutoMigrate(
&model.Account{},
&model.User{},
&model.Inbox{},
&model.Contact{},
&model.ContactInbox{},
&model.Conversation{},
&model.ConversationParticipant{},
)
s.Require().NoError(err)
// Create test account
account := &model.Account{Name: "ParticipantHandlerOrg", Locale: "en", Active: true}
s.Require().NoError(db.Create(account).Error)
s.testAccount = account
// Create test user
user := &model.User{Name: "ParticipantHandlerUser", Email: "participant-handler@test.com", Password: "hashed", Role: "agent", Active: true}
s.Require().NoError(db.Create(user).Error)
s.testUser = user
// Create inbox and contact
inbox := &model.Inbox{AccountID: account.ID, Name: "ParticipantHandlerInbox", ChannelType: "web_widget", ChannelID: 1}
s.Require().NoError(db.Create(inbox).Error)
contact := &model.Contact{AccountID: account.ID, Name: "ParticipantHandlerContact"}
s.Require().NoError(db.Create(contact).Error)
// Create conversation
conv := &model.Conversation{AccountID: account.ID, InboxID: inbox.ID, ContactID: contact.ID, Status: "open", ChannelType: "web_widget", Channel: "web_widget"}
s.Require().NoError(db.Create(conv).Error)
s.testConv = conv
// Wire up repos, services, handlers
convRepo := repository.NewConversationRepo(db)
participantRepo := repository.NewConversationParticipantRepo(db)
participantSvc := service.NewConversationParticipantService(participantRepo, convRepo)
handler := NewConversationParticipantHandler(participantSvc)
// Setup router
r := gin.New()
s.router = r
// Register routes
accountGroup := r.Group("/api/v1/accounts/:account_id")
{
convGroup := accountGroup.Group("/conversations/:conversation_id")
{
participants := convGroup.Group("/participants")
{
participants.GET("", handler.List)
participants.POST("", handler.Add)
participants.PATCH("", handler.BatchUpdate)
participants.PATCH("/:user_id", handler.Update)
participants.DELETE("/:user_id", handler.Remove)
}
}
}
}
func (s *ConversationParticipantHandlerTestSuite) Test_AddParticipant() {
body := map[string]interface{}{
"user_id": s.testUser.ID,
"role": "assignee",
}
b, _ := json.Marshal(body)
w := httptest.NewRecorder()
req, _ := http.NewRequest("POST", "/api/v1/accounts/"+strconv.FormatUint(uint64(s.testAccount.ID), 10)+"/conversations/"+strconv.FormatUint(uint64(s.testConv.ID), 10)+"/participants", bytes.NewReader(b))
req.Header.Set("Content-Type", "application/json")
s.router.ServeHTTP(w, req)
assert.Equal(s.T(), http.StatusOK, w.Code)
var resp map[string]interface{}
json.Unmarshal(w.Body.Bytes(), &resp)
data, ok := resp["data"].([]interface{})
if ok && len(data) > 0 {
item := data[0].(map[string]interface{})
assert.Equal(s.T(), "assignee", item["role"])
}
}
func (s *ConversationParticipantHandlerTestSuite) Test_ListParticipants() {
// First add a participant
body := map[string]interface{}{
"user_id": s.testUser.ID,
"role": "participant",
}
b, _ := json.Marshal(body)
w := httptest.NewRecorder()
req, _ := http.NewRequest("POST", "/api/v1/accounts/"+strconv.FormatUint(uint64(s.testAccount.ID), 10)+"/conversations/"+strconv.FormatUint(uint64(s.testConv.ID), 10)+"/participants", bytes.NewReader(b))
req.Header.Set("Content-Type", "application/json")
s.router.ServeHTTP(w, req)
// Now list
w = httptest.NewRecorder()
req, _ = http.NewRequest("GET", "/api/v1/accounts/"+strconv.FormatUint(uint64(s.testAccount.ID), 10)+"/conversations/"+strconv.FormatUint(uint64(s.testConv.ID), 10)+"/participants", nil)
s.router.ServeHTTP(w, req)
assert.Equal(s.T(), http.StatusOK, w.Code)
}
func (s *ConversationParticipantHandlerTestSuite) Test_RemoveParticipant() {
// First add a participant
body := map[string]interface{}{
"user_id": s.testUser.ID,
"role": "participant",
}
b, _ := json.Marshal(body)
w := httptest.NewRecorder()
req, _ := http.NewRequest("POST", "/api/v1/accounts/"+strconv.FormatUint(uint64(s.testAccount.ID), 10)+"/conversations/"+strconv.FormatUint(uint64(s.testConv.ID), 10)+"/participants", bytes.NewReader(b))
req.Header.Set("Content-Type", "application/json")
s.router.ServeHTTP(w, req)
// Now remove
w = httptest.NewRecorder()
req, _ = http.NewRequest("DELETE", "/api/v1/accounts/"+strconv.FormatUint(uint64(s.testAccount.ID), 10)+"/conversations/"+strconv.FormatUint(uint64(s.testConv.ID), 10)+"/participants/"+strconv.FormatUint(uint64(s.testUser.ID), 10), nil)
s.router.ServeHTTP(w, req)
assert.Equal(s.T(), http.StatusOK, w.Code)
}
func (s *ConversationParticipantHandlerTestSuite) Test_UpdateParticipantRole() {
// First add a participant
body := map[string]interface{}{
"user_id": s.testUser.ID,
"role": "participant",
}
b, _ := json.Marshal(body)
w := httptest.NewRecorder()
req, _ := http.NewRequest("POST", "/api/v1/accounts/"+strconv.FormatUint(uint64(s.testAccount.ID), 10)+"/conversations/"+strconv.FormatUint(uint64(s.testConv.ID), 10)+"/participants", bytes.NewReader(b))
req.Header.Set("Content-Type", "application/json")
s.router.ServeHTTP(w, req)
// Now update role
body = map[string]interface{}{
"role": "assignee",
}
b, _ = json.Marshal(body)
w = httptest.NewRecorder()
req, _ = http.NewRequest("PATCH", "/api/v1/accounts/"+strconv.FormatUint(uint64(s.testAccount.ID), 10)+"/conversations/"+strconv.FormatUint(uint64(s.testConv.ID), 10)+"/participants/"+strconv.FormatUint(uint64(s.testUser.ID), 10), bytes.NewReader(b))
req.Header.Set("Content-Type", "application/json")
s.router.ServeHTTP(w, req)
assert.Equal(s.T(), http.StatusOK, w.Code)
}
func (s *ConversationParticipantHandlerTestSuite) Test_AddParticipant_InvalidAccountID() {
body := map[string]interface{}{
"user_id": 1,
"role": "participant",
}
b, _ := json.Marshal(body)
w := httptest.NewRecorder()
req, _ := http.NewRequest("POST", "/api/v1/accounts/invalid/conversations/1/participants", bytes.NewReader(b))
req.Header.Set("Content-Type", "application/json")
s.router.ServeHTTP(w, req)
assert.Equal(s.T(), http.StatusBadRequest, w.Code)
}
// ========== BatchUpdate Handler Tests ==========
func (s *ConversationParticipantHandlerTestSuite) Test_BatchUpdate_AddAndRemove() {
// Create a second user for testing batch add/remove
user2 := &model.User{Name: "BatchUser2", Email: "batch2@test.com", Password: "hashed", Role: "agent", Active: true}
s.Require().NoError(s.db.Create(user2).Error)
// First add s.testUser as participant
addBody := map[string]interface{}{
"user_id": s.testUser.ID,
"role": "assignee",
}
addBytes, _ := json.Marshal(addBody)
w := httptest.NewRecorder()
req, _ := http.NewRequest("POST", "/api/v1/accounts/"+strconv.FormatUint(uint64(s.testAccount.ID), 10)+"/conversations/"+strconv.FormatUint(uint64(s.testConv.ID), 10)+"/participants", bytes.NewReader(addBytes))
req.Header.Set("Content-Type", "application/json")
s.router.ServeHTTP(w, req)
assert.Equal(s.T(), http.StatusOK, w.Code)
// Now batch update: add user2, remove s.testUser
batchBody := map[string]interface{}{
"user_ids": []uint{user2.ID},
"removed_user_ids": []uint{s.testUser.ID},
}
batchBytes, _ := json.Marshal(batchBody)
w = httptest.NewRecorder()
req, _ = http.NewRequest("PATCH", "/api/v1/accounts/"+strconv.FormatUint(uint64(s.testAccount.ID), 10)+"/conversations/"+strconv.FormatUint(uint64(s.testConv.ID), 10)+"/participants", bytes.NewReader(batchBytes))
req.Header.Set("Content-Type", "application/json")
s.router.ServeHTTP(w, req)
assert.Equal(s.T(), http.StatusOK, w.Code)
}
func (s *ConversationParticipantHandlerTestSuite) Test_BatchUpdate_InvalidAccountID() {
batchBody := map[string]interface{}{
"user_ids": []uint{1},
}
batchBytes, _ := json.Marshal(batchBody)
w := httptest.NewRecorder()
req, _ := http.NewRequest("PATCH", "/api/v1/accounts/invalid/conversations/1/participants", bytes.NewReader(batchBytes))
req.Header.Set("Content-Type", "application/json")
s.router.ServeHTTP(w, req)
assert.Equal(s.T(), http.StatusBadRequest, w.Code)
}
func TestConversationParticipantHandlerTestSuite(t *testing.T) {
suite.Run(t, new(ConversationParticipantHandlerTestSuite))
}