Align GoChat with Chatwoot frontend contracts

This commit is contained in:
2026-06-13 22:13:32 +08:00
parent 71abf58636
commit d884fdda0a
162 changed files with 9825 additions and 528 deletions
@@ -6,6 +6,7 @@ import (
"fmt"
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/gin-gonic/gin"
@@ -27,6 +28,20 @@ type AgentCapacityHandlerTestSuite struct {
account *model.Account
}
func assertErrorEnvelope(t *testing.T, body []byte, code string, messageContains string) {
t.Helper()
var payload map[string]any
assert.NoError(t, json.Unmarshal(body, &payload))
assert.Equal(t, false, payload["success"])
errorPayload, ok := payload["error"].(map[string]any)
if !assert.True(t, ok, "expected error envelope in %s", string(body)) {
return
}
assert.Equal(t, code, errorPayload["code"])
message, _ := errorPayload["message"].(string)
assert.True(t, strings.Contains(message, messageContains), "expected message %q to contain %q", message, messageContains)
}
func (s *AgentCapacityHandlerTestSuite) SetupSuite() {
gin.SetMode(gin.TestMode)
db, err := gorm.Open(sqlite.Open("file::memory:"), &gorm.Config{
@@ -190,6 +205,14 @@ func (s *AgentCapacityHandlerTestSuite) TestChatwootPolicyInboxLimitAndUserFlow(
s.Require().Equal(float64(11), limit["conversation_limit"])
s.Require().Equal("Priority", limit["inbox_name"])
w = httptest.NewRecorder()
req, _ = http.NewRequest(http.MethodPatch, fmt.Sprintf("/api/v1/accounts/%d/agent_capacity_policies/%d/inbox_limits/%d", s.account.ID, policyID, limitID), bytes.NewBufferString(`{"conversation_limit":13}`))
req.Header.Set("Content-Type", "application/json")
r.ServeHTTP(w, req)
s.Require().Equal(http.StatusOK, w.Code)
s.Require().NoError(json.Unmarshal(w.Body.Bytes(), &limit))
s.Require().Equal(float64(13), limit["conversation_limit"])
w = httptest.NewRecorder()
body = fmt.Sprintf(`{"user_id":%d}`, user.ID)
req, _ = http.NewRequest(http.MethodPost, fmt.Sprintf("/api/v1/accounts/%d/agent_capacity_policies/%d/users", s.account.ID, policyID), bytes.NewBufferString(body))
@@ -200,6 +223,15 @@ func (s *AgentCapacityHandlerTestSuite) TestChatwootPolicyInboxLimitAndUserFlow(
s.Require().NoError(json.Unmarshal(w.Body.Bytes(), &assigned))
s.Require().Equal(float64(user.ID), assigned["id"])
w = httptest.NewRecorder()
req, _ = http.NewRequest(http.MethodGet, fmt.Sprintf("/api/v1/accounts/%d/agent_capacity_policies/%d/users", s.account.ID, policyID), nil)
r.ServeHTTP(w, req)
s.Require().Equal(http.StatusOK, w.Code)
var assignedUsers []map[string]any
s.Require().NoError(json.Unmarshal(w.Body.Bytes(), &assignedUsers))
s.Require().Len(assignedUsers, 1)
s.Require().Equal(float64(user.ID), assignedUsers[0]["id"])
w = httptest.NewRecorder()
req, _ = http.NewRequest(http.MethodGet, fmt.Sprintf("/api/v1/accounts/%d/agent_capacity_policies/%d", s.account.ID, policyID), nil)
r.ServeHTTP(w, req)
@@ -212,6 +244,65 @@ func (s *AgentCapacityHandlerTestSuite) TestChatwootPolicyInboxLimitAndUserFlow(
req, _ = http.NewRequest(http.MethodDelete, fmt.Sprintf("/api/v1/accounts/%d/agent_capacity_policies/%d/inbox_limits/%d", s.account.ID, policyID, limitID), nil)
r.ServeHTTP(w, req)
s.Require().Equal(http.StatusNoContent, w.Code)
w = httptest.NewRecorder()
req, _ = http.NewRequest(http.MethodDelete, fmt.Sprintf("/api/v1/accounts/%d/agent_capacity_policies/%d/users/%d", s.account.ID, policyID, user.ID), nil)
r.ServeHTTP(w, req)
s.Require().Equal(http.StatusOK, w.Code)
w = httptest.NewRecorder()
req, _ = http.NewRequest(http.MethodGet, fmt.Sprintf("/api/v1/accounts/%d/agent_capacity_policies/%d/users", s.account.ID, policyID), nil)
r.ServeHTTP(w, req)
s.Require().Equal(http.StatusOK, w.Code)
s.Require().NoError(json.Unmarshal(w.Body.Bytes(), &assignedUsers))
s.Require().Empty(assignedUsers)
}
func (s *AgentCapacityHandlerTestSuite) TestChatwootPolicyInboxLimitAndUserValidationErrors() {
r := gin.New()
api := r.Group("/api/v1/accounts/:account_id")
RegisterAgentCapacityRoutes(api, s.handler)
inbox := &model.Inbox{AccountID: s.account.ID, Name: "Validation", ChannelType: "web_widget"}
s.Require().NoError(s.db.Create(inbox).Error)
w := httptest.NewRecorder()
body := `{"agent_capacity_policy":{"name":"Validation policy"}}`
req, _ := http.NewRequest(http.MethodPost, fmt.Sprintf("/api/v1/accounts/%d/agent_capacity_policies", s.account.ID), bytes.NewBufferString(body))
req.Header.Set("Content-Type", "application/json")
r.ServeHTTP(w, req)
s.Require().Equal(http.StatusOK, w.Code, w.Body.String())
var policy map[string]any
s.Require().NoError(json.Unmarshal(w.Body.Bytes(), &policy))
policyID := uint(policy["id"].(float64))
w = httptest.NewRecorder()
body = fmt.Sprintf(`{"inbox_id":%d,"conversation_limit":-1}`, inbox.ID)
req, _ = http.NewRequest(http.MethodPost, fmt.Sprintf("/api/v1/accounts/%d/agent_capacity_policies/%d/inbox_limits", s.account.ID, policyID), bytes.NewBufferString(body))
req.Header.Set("Content-Type", "application/json")
r.ServeHTTP(w, req)
s.Require().Equal(http.StatusUnprocessableEntity, w.Code, w.Body.String())
assertErrorEnvelope(s.T(), w.Body.Bytes(), "VALIDATION_ERROR", "conversation_limit must be greater than or equal")
w = httptest.NewRecorder()
req, _ = http.NewRequest(http.MethodPatch, fmt.Sprintf("/api/v1/accounts/%d/agent_capacity_policies/%d/inbox_limits/999999", s.account.ID, policyID), bytes.NewBufferString(`{"conversation_limit":5}`))
req.Header.Set("Content-Type", "application/json")
r.ServeHTTP(w, req)
s.Require().Equal(http.StatusNotFound, w.Code, w.Body.String())
assertErrorEnvelope(s.T(), w.Body.Bytes(), "NOT_FOUND", "inbox capacity limit not found")
w = httptest.NewRecorder()
req, _ = http.NewRequest(http.MethodPost, fmt.Sprintf("/api/v1/accounts/%d/agent_capacity_policies/%d/users", s.account.ID, policyID), bytes.NewBufferString(`{"user_id":0}`))
req.Header.Set("Content-Type", "application/json")
r.ServeHTTP(w, req)
s.Require().Equal(http.StatusBadRequest, w.Code, w.Body.String())
assertErrorEnvelope(s.T(), w.Body.Bytes(), "VALIDATION_ERROR", "user_id is required")
w = httptest.NewRecorder()
req, _ = http.NewRequest(http.MethodDelete, fmt.Sprintf("/api/v1/accounts/%d/agent_capacity_policies/%d/users/999999", s.account.ID, policyID), nil)
r.ServeHTTP(w, req)
s.Require().Equal(http.StatusNotFound, w.Code, w.Body.String())
assertErrorEnvelope(s.T(), w.Body.Bytes(), "NOT_FOUND", "account user not found")
}
func (s *AgentCapacityHandlerTestSuite) TestMutations_WriteAuditEntries() {