HH-502: cover non-widget message sender events (#113)

Co-authored-by: Rogee <rogee@ipao.vip>
This commit is contained in:
Rogee
2026-08-23 07:40:38 +08:00
committed by GitHub
co-authored by rogee
parent eaf6edef66
commit dac8c5e068
3 changed files with 113 additions and 21 deletions
@@ -114,6 +114,7 @@ func TestWebSocketMultiInstanceFanout(t *testing.T) {
t.Cleanup(instanceB.stop)
authHeaders := signIn(t, baseURLA, seedData.AdminEmail, seedData.AdminPassword)
apiSeed := createAPIFanoutSeed(t, baseURLA, authHeaders, seedData.AccountID, stamp)
connA, identifier := connectAccountCable(t, baseURLA, issueWSTicket(t, baseURLA, authHeaders), seedData.AccountID)
defer connA.Close()
connB, identifierB := connectAccountCable(t, baseURLB, issueWSTicket(t, baseURLB, authHeaders), seedData.AccountID)
@@ -127,31 +128,36 @@ func TestWebSocketMultiInstanceFanout(t *testing.T) {
senderObjectType string
senderID uint
widget bool
apiInbox bool
attachment bool
}{
{name: "agent_bot", senderType: "AgentBot", senderObjectType: "agent_bot", senderID: agentBotID},
{name: "contact", senderType: "Contact", senderObjectType: "contact", widget: true},
{name: "captain_assistant", senderType: "Captain::Assistant", senderObjectType: "captain_assistant", senderID: seedData.CaptainAssistantID},
{name: "user", senderType: "User", senderObjectType: "user", attachment: true},
{name: "user", senderType: "User", senderObjectType: "user", apiInbox: true, attachment: true},
}
frames := make(map[string]any, len(senderCases))
for _, senderCase := range senderCases {
content := "multi-instance " + senderCase.name + " " + stamp
targetSeed := seedData
if senderCase.apiInbox {
targetSeed = apiSeed
}
var created map[string]any
if senderCase.widget {
createWidgetMessage(t, baseURLA, seedData, content)
createWidgetMessage(t, baseURLA, targetSeed, content)
} else {
created = createDashboardMessage(t, baseURLA, authHeaders, seedData, content, senderCase.senderType, senderCase.senderID, senderCase.attachment)
created = createDashboardMessage(t, baseURLA, authHeaders, targetSeed, content, senderCase.senderType, senderCase.senderID, senderCase.attachment)
}
frameA := readMessageCreated(t, connA, content)
frameB := readMessageCreated(t, connB, content)
if created == nil {
created = fetchHTTPMessage(t, baseURLA, authHeaders, seedData, content)
created = fetchHTTPMessage(t, baseURLA, authHeaders, targetSeed, content)
}
require.Equal(t, frameA, frameB, "%s sender must fan out identically", senderCase.name)
assertFanoutContract(t, frameA, identifier, created, seedData, content, senderCase.senderType, senderCase.senderObjectType, !senderCase.widget, senderCase.attachment)
assertFanoutContract(t, frameB, identifier, created, seedData, content, senderCase.senderType, senderCase.senderObjectType, !senderCase.widget, senderCase.attachment)
assertFanoutContract(t, frameA, identifier, created, targetSeed, content, senderCase.senderType, senderCase.senderObjectType, !senderCase.widget, senderCase.attachment)
assertFanoutContract(t, frameB, identifier, created, targetSeed, content, senderCase.senderType, senderCase.senderObjectType, !senderCase.widget, senderCase.attachment)
frames[senderCase.name+"_a"] = frameA
frames[senderCase.name+"_b"] = frameB
}
@@ -298,6 +304,45 @@ func signIn(t *testing.T, baseURL, email, password string) http.Header {
return response.Header.Clone()
}
func createAPIFanoutSeed(t *testing.T, baseURL string, authHeaders http.Header, accountID uint, stamp string) fanoutSeed {
t.Helper()
post := func(endpoint string, payload any) map[string]any {
body, err := json.Marshal(payload)
require.NoError(t, err)
request, err := http.NewRequest(http.MethodPost, endpoint, bytes.NewReader(body))
require.NoError(t, err)
request.Header.Set("Content-Type", "application/json")
setAuthHeaders(request, authHeaders)
response, err := http.DefaultClient.Do(request)
require.NoError(t, err)
defer response.Body.Close()
responseBody, err := io.ReadAll(response.Body)
require.NoError(t, err)
require.Equal(t, http.StatusOK, response.StatusCode, "create API fanout fixture response: %s", responseBody)
var result map[string]any
require.NoError(t, json.Unmarshal(responseBody, &result))
return result
}
inbox := post(fmt.Sprintf("%s/api/v1/accounts/%d/inboxes", baseURL, accountID), map[string]any{
"name": "WebSocket API Inbox " + stamp,
"channel": map[string]any{"type": "api"},
})
inboxID := uint(inbox["id"].(float64))
contactResponse := post(fmt.Sprintf("%s/api/v1/accounts/%d/contacts", baseURL, accountID), map[string]any{
"name": "WebSocket API Contact " + stamp,
"identifier": "ws-api-" + stamp,
"inbox_id": inboxID,
"source_id": "ws-api-source-" + stamp,
})
contactID := uint(contactResponse["payload"].(map[string]any)["contact"].(map[string]any)["id"].(float64))
conversation := post(fmt.Sprintf("%s/api/v1/accounts/%d/conversations", baseURL, accountID), map[string]any{
"inbox_id": inboxID,
"contact_id": contactID,
})
return fanoutSeed{AccountID: accountID, ConversationDisplayID: uint(conversation["id"].(float64))}
}
func issueWSTicket(t *testing.T, baseURL string, authHeaders http.Header) string {
t.Helper()
request, err := http.NewRequest(http.MethodPost, baseURL+"/api/v1/auth/ws_ticket", nil)