446 lines
23 KiB
Go
446 lines
23 KiB
Go
package v1
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/gochat/gochat/internal/model"
|
|
channelmodel "github.com/gochat/gochat/internal/model/channel"
|
|
"github.com/stretchr/testify/require"
|
|
"gorm.io/datatypes"
|
|
"gorm.io/driver/sqlite"
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm/logger"
|
|
)
|
|
|
|
type fakeContactPresence struct {
|
|
statuses map[string]string
|
|
}
|
|
|
|
func (f fakeContactPresence) GetContactStatus(_ context.Context, contactID, accountID uint) (string, error) {
|
|
status := f.statuses[fmt.Sprintf("%d:%d", contactID, accountID)]
|
|
if status == "" {
|
|
status = "offline"
|
|
}
|
|
return status, nil
|
|
}
|
|
|
|
func TestConversationCanReplyRespectsMessagingWindows(t *testing.T) {
|
|
db := newConversationSerializerTestDB(t)
|
|
ctx := context.Background()
|
|
account := &model.Account{Name: "Reply Window Account", Active: true}
|
|
require.NoError(t, db.Create(account).Error)
|
|
contact := &model.Contact{AccountID: account.ID, Name: "Reply Window Contact"}
|
|
require.NoError(t, db.Create(contact).Error)
|
|
|
|
t.Run("api channel uses configured agent reply window", func(t *testing.T) {
|
|
inbox := createSerializerInbox(t, db, account.ID, "Channel::Api", "API")
|
|
require.NoError(t, db.Table("channel_api").Create(map[string]any{
|
|
"inbox_id": inbox.ID,
|
|
"additional_attributes": datatypes.JSON([]byte(`{"agent_reply_time_window":"12"}`)),
|
|
}).Error)
|
|
conversation := createSerializerConversation(t, db, account.ID, inbox.ID, contact.ID)
|
|
createSerializerMessage(t, db, account.ID, inbox.ID, conversation.ID, "incoming", time.Now().Add(-13*time.Hour))
|
|
require.False(t, conversationCanReply(ctx, db, conversation))
|
|
})
|
|
|
|
t.Run("facebook defaults to 24 hours", func(t *testing.T) {
|
|
inbox := createSerializerInbox(t, db, account.ID, "Channel::FacebookPage", "Facebook")
|
|
conversation := createSerializerConversation(t, db, account.ID, inbox.ID, contact.ID)
|
|
createSerializerMessage(t, db, account.ID, inbox.ID, conversation.ID, "incoming", time.Now().Add(-25*time.Hour))
|
|
require.False(t, conversationCanReply(ctx, db, conversation))
|
|
})
|
|
|
|
t.Run("facebook human agent env extends to seven days", func(t *testing.T) {
|
|
t.Setenv("ENABLE_MESSENGER_CHANNEL_HUMAN_AGENT", "true")
|
|
inbox := createSerializerInbox(t, db, account.ID, "Channel::FacebookPage", "Facebook Human Agent")
|
|
conversation := createSerializerConversation(t, db, account.ID, inbox.ID, contact.ID)
|
|
createSerializerMessage(t, db, account.ID, inbox.ID, conversation.ID, "incoming", time.Now().Add(-6*24*time.Hour))
|
|
require.True(t, conversationCanReply(ctx, db, conversation))
|
|
})
|
|
|
|
t.Run("twilio sms has no reply window unless medium is whatsapp", func(t *testing.T) {
|
|
smsInbox := createSerializerInbox(t, db, account.ID, "Channel::TwilioSms", "Twilio SMS")
|
|
smsConversation := createSerializerConversation(t, db, account.ID, smsInbox.ID, contact.ID)
|
|
createSerializerMessage(t, db, account.ID, smsInbox.ID, smsConversation.ID, "incoming", time.Now().Add(-10*24*time.Hour))
|
|
require.True(t, conversationCanReply(ctx, db, smsConversation))
|
|
|
|
whatsappInbox := createSerializerInbox(t, db, account.ID, "twilio_sms", "Twilio WhatsApp")
|
|
whatsappInbox.ChannelConfig = `{"medium":"whatsapp"}`
|
|
require.NoError(t, db.Save(whatsappInbox).Error)
|
|
whatsappConversation := createSerializerConversation(t, db, account.ID, whatsappInbox.ID, contact.ID)
|
|
createSerializerMessage(t, db, account.ID, whatsappInbox.ID, whatsappConversation.ID, "incoming", time.Now().Add(-25*time.Hour))
|
|
require.False(t, conversationCanReply(ctx, db, whatsappConversation))
|
|
})
|
|
}
|
|
|
|
func TestSerializeMessageIncludesAgentBotSender(t *testing.T) {
|
|
db := newConversationSerializerTestDB(t)
|
|
ctx := context.Background()
|
|
account := &model.Account{Name: "Agent Bot Sender Account", Active: true}
|
|
require.NoError(t, db.Create(account).Error)
|
|
contact := &model.Contact{AccountID: account.ID, Name: "Agent Bot Sender Contact"}
|
|
require.NoError(t, db.Create(contact).Error)
|
|
inbox := createSerializerInbox(t, db, account.ID, "web_widget", "Web Widget")
|
|
conversation := createSerializerConversation(t, db, account.ID, inbox.ID, contact.ID)
|
|
agentBot := &model.AgentBot{AccountID: &account.ID, Name: "Reply Bot", AvatarURL: "https://example.com/bot.png"}
|
|
require.NoError(t, db.Create(agentBot).Error)
|
|
message := &model.Message{
|
|
AccountID: account.ID,
|
|
InboxID: inbox.ID,
|
|
ConversationID: conversation.ID,
|
|
MessageType: "outgoing",
|
|
ContentType: "text",
|
|
Content: "bot reply",
|
|
SenderID: &agentBot.ID,
|
|
SenderType: string(model.SenderTypeAgentBot),
|
|
ContentAttributes: datatypes.JSON([]byte(`{}`)),
|
|
}
|
|
require.NoError(t, db.Create(message).Error)
|
|
|
|
payload := serializeMessage(ctx, db, message, conversation)
|
|
require.Equal(t, account.ID, payload.AccountID)
|
|
sender := payload.Sender
|
|
require.Equal(t, agentBot.ID, sender["id"])
|
|
require.Equal(t, "Reply Bot", sender["name"])
|
|
require.Equal(t, "agent_bot", sender["type"])
|
|
}
|
|
|
|
func TestSerializeVoiceCallMessageIncludesCallPayload(t *testing.T) {
|
|
db := newConversationSerializerTestDB(t)
|
|
ctx := context.Background()
|
|
account := &model.Account{Name: "Voice Call Account", Active: true}
|
|
require.NoError(t, db.Create(account).Error)
|
|
contact := &model.Contact{AccountID: account.ID, Name: "Call Contact", PhoneNumber: "+1555010001"}
|
|
require.NoError(t, db.Create(contact).Error)
|
|
inbox := createSerializerInbox(t, db, account.ID, "Channel::TwilioSms", "Voice Inbox")
|
|
require.NoError(t, db.Create(&channelmodel.ChannelTwilioSMS{AccountID: account.ID, InboxID: inbox.ID, AccountSID: "AC123", PhoneNumber: "+1555010000"}).Error)
|
|
agent := &model.User{AccountID: account.ID, Name: "Voice Agent", DisplayName: "Agent Voice", Email: "voice@example.com", Password: "pass"}
|
|
require.NoError(t, db.Create(agent).Error)
|
|
conversation := createSerializerConversation(t, db, account.ID, inbox.ID, contact.ID)
|
|
startedAt := time.Date(2026, 6, 10, 12, 0, 0, 0, time.UTC)
|
|
message := &model.Message{
|
|
AccountID: account.ID,
|
|
InboxID: inbox.ID,
|
|
ConversationID: conversation.ID,
|
|
SenderID: &agent.ID,
|
|
SenderType: "user",
|
|
MessageType: "outgoing",
|
|
ContentType: "voice_call",
|
|
Content: "Twilio voice call",
|
|
Status: "sent",
|
|
}
|
|
require.NoError(t, db.Create(message).Error)
|
|
call := &model.Call{
|
|
AccountID: account.ID,
|
|
InboxID: inbox.ID,
|
|
ConversationID: conversation.ID,
|
|
ContactID: contact.ID,
|
|
MessageID: &message.ID,
|
|
AcceptedByAgentID: &agent.ID,
|
|
Provider: "twilio",
|
|
Direction: "outgoing",
|
|
ProviderCallID: "CA123",
|
|
ConferenceSID: "CF123",
|
|
StartedAt: &startedAt,
|
|
CallerType: "User",
|
|
CallerID: agent.ID,
|
|
Status: "in_progress",
|
|
Duration: 42,
|
|
CallDirection: "outbound",
|
|
RecordingURL: "https://recordings.example/call.mp3",
|
|
AdditionalAttributes: json.RawMessage([]byte(`{"ended_at":1760000000,"transcript":"hello"}`)),
|
|
}
|
|
require.NoError(t, db.Create(call).Error)
|
|
|
|
payload := serializeMessage(ctx, db, message, conversation)
|
|
require.NotNil(t, payload.Call)
|
|
require.Equal(t, call.ID, payload.Call["id"])
|
|
require.Equal(t, "CA123", payload.Call["provider_call_id"])
|
|
require.Equal(t, "twilio", payload.Call["provider"])
|
|
require.Equal(t, "outgoing", payload.Call["direction"])
|
|
require.Equal(t, "in-progress", payload.Call["status"])
|
|
require.Equal(t, 42, payload.Call["duration_seconds"])
|
|
require.Equal(t, "CF123", payload.Call["conference_sid"])
|
|
require.Equal(t, agent.ID, payload.Call["accepted_by_agent_id"])
|
|
require.Equal(t, "Agent Voice", payload.Call["accepted_by_agent_name"])
|
|
require.Equal(t, startedAt.Unix(), payload.Call["started_at"])
|
|
require.Equal(t, float64(1760000000), payload.Call["ended_at"])
|
|
require.Equal(t, "+1555010000", payload.Call["from_number"])
|
|
require.Equal(t, "+1555010001", payload.Call["to_number"])
|
|
require.Equal(t, "https://recordings.example/call.mp3", payload.Call["recording_url"])
|
|
require.Equal(t, "hello", payload.Call["transcript"])
|
|
}
|
|
|
|
func TestSerializeConversationLastNonActivityMessageSkipsLatestActivity(t *testing.T) {
|
|
db := newConversationSerializerTestDB(t)
|
|
ctx := context.Background()
|
|
account := &model.Account{Name: "Last Non Activity Account", Active: true}
|
|
require.NoError(t, db.Create(account).Error)
|
|
contact := &model.Contact{AccountID: account.ID, Name: "Last Non Activity Contact"}
|
|
require.NoError(t, db.Create(contact).Error)
|
|
inbox := createSerializerInbox(t, db, account.ID, "web_widget", "Web Widget")
|
|
conversation := createSerializerConversation(t, db, account.ID, inbox.ID, contact.ID)
|
|
olderMessage := &model.Message{AccountID: account.ID, InboxID: inbox.ID, ConversationID: conversation.ID, MessageType: "incoming", ContentType: "text", Content: "older reply"}
|
|
olderMessage.CreatedAt = time.Now().Add(-3 * time.Hour)
|
|
olderMessage.UpdatedAt = olderMessage.CreatedAt
|
|
require.NoError(t, db.Create(olderMessage).Error)
|
|
recentMessage := &model.Message{AccountID: account.ID, InboxID: inbox.ID, ConversationID: conversation.ID, MessageType: "outgoing", ContentType: "text", Content: "recent reply"}
|
|
recentMessage.CreatedAt = time.Now().Add(-2 * time.Hour)
|
|
recentMessage.UpdatedAt = recentMessage.CreatedAt
|
|
require.NoError(t, db.Create(recentMessage).Error)
|
|
activityMessage := &model.Message{AccountID: account.ID, InboxID: inbox.ID, ConversationID: conversation.ID, MessageType: "activity", ContentType: "text", Content: "activity event"}
|
|
activityMessage.CreatedAt = time.Now().Add(-1 * time.Hour)
|
|
activityMessage.UpdatedAt = activityMessage.CreatedAt
|
|
require.NoError(t, db.Create(activityMessage).Error)
|
|
|
|
payload := serializeConversation(ctx, db, conversation)
|
|
require.Len(t, payload.Messages, 1)
|
|
require.Equal(t, activityMessage.ID, payload.Messages[0].ID)
|
|
require.NotNil(t, payload.LastNonActivityMessage)
|
|
require.Equal(t, recentMessage.ID, payload.LastNonActivityMessage.ID)
|
|
require.Equal(t, "recent reply", payload.LastNonActivityMessage.Content)
|
|
}
|
|
|
|
func TestSerializeAttachmentOmitAccountID(t *testing.T) {
|
|
db := newConversationSerializerTestDB(t)
|
|
ctx := context.Background()
|
|
account := &model.Account{Name: "Attachment Account", Active: true}
|
|
require.NoError(t, db.Create(account).Error)
|
|
contact := &model.Contact{AccountID: account.ID, Name: "Contact"}
|
|
require.NoError(t, db.Create(contact).Error)
|
|
inbox := createSerializerInbox(t, db, account.ID, "web_widget", "Web Widget")
|
|
conversation := createSerializerConversation(t, db, account.ID, inbox.ID, contact.ID)
|
|
message := &model.Message{AccountID: account.ID, InboxID: inbox.ID, ConversationID: conversation.ID, MessageType: "incoming", Content: "hi"}
|
|
require.NoError(t, db.Create(message).Error)
|
|
attachment := &model.Attachment{AccountID: account.ID, MessageID: message.ID, FileType: "file", FileName: "report.pdf", FileURL: "https://files.example/report.pdf", FileSize: 512}
|
|
require.NoError(t, db.Create(attachment).Error)
|
|
|
|
payload := serializeAttachment(ctx, db, attachment)
|
|
require.Equal(t, message.ID, payload["message_id"])
|
|
require.Equal(t, "file", payload["file_type"])
|
|
require.Equal(t, "https://files.example/report.pdf", payload["data_url"])
|
|
require.Equal(t, "pdf", payload["extension"])
|
|
require.NotContains(t, payload, "account_id")
|
|
}
|
|
|
|
func TestSerializeMessageAttachmentIncludesAccountID(t *testing.T) {
|
|
db := newConversationSerializerTestDB(t)
|
|
ctx := context.Background()
|
|
account := &model.Account{Name: "Message Attachment Account", Active: true}
|
|
require.NoError(t, db.Create(account).Error)
|
|
contact := &model.Contact{AccountID: account.ID, Name: "Contact"}
|
|
require.NoError(t, db.Create(contact).Error)
|
|
inbox := createSerializerInbox(t, db, account.ID, "web_widget", "Web Widget")
|
|
conversation := createSerializerConversation(t, db, account.ID, inbox.ID, contact.ID)
|
|
message := &model.Message{AccountID: account.ID, InboxID: inbox.ID, ConversationID: conversation.ID, MessageType: "incoming", ContentType: "text", Content: "hi"}
|
|
require.NoError(t, db.Create(message).Error)
|
|
attachment := &model.Attachment{AccountID: account.ID, MessageID: message.ID, FileType: "image", FileName: "photo.png", FileURL: "https://files.example/photo.png", FileSize: 128, Width: 80, Height: 60}
|
|
require.NoError(t, db.Create(attachment).Error)
|
|
|
|
payload := serializeMessage(ctx, db, message, conversation)
|
|
require.Len(t, payload.Attachments, 1)
|
|
attachmentPayload, ok := payload.Attachments[0].(map[string]any)
|
|
require.True(t, ok)
|
|
require.Equal(t, account.ID, attachmentPayload["account_id"])
|
|
require.Equal(t, attachment.ID, attachmentPayload["id"])
|
|
require.Equal(t, message.ID, attachmentPayload["message_id"])
|
|
require.Equal(t, "image", attachmentPayload["file_type"])
|
|
require.Equal(t, "https://files.example/photo.png", attachmentPayload["data_url"])
|
|
require.Equal(t, "png", attachmentPayload["extension"])
|
|
require.NotContains(t, attachmentPayload, "created_at")
|
|
require.NotContains(t, attachmentPayload, "sender")
|
|
}
|
|
|
|
func TestSerializeConversationUsesLatestMessageByCreatedAt(t *testing.T) {
|
|
db := newConversationSerializerTestDB(t)
|
|
ctx := context.Background()
|
|
account := &model.Account{Name: "Latest Message Account", Active: true}
|
|
require.NoError(t, db.Create(account).Error)
|
|
contact := &model.Contact{AccountID: account.ID, Name: "Latest Message Contact"}
|
|
require.NoError(t, db.Create(contact).Error)
|
|
inbox := createSerializerInbox(t, db, account.ID, "web_widget", "Web Widget")
|
|
conversation := createSerializerConversation(t, db, account.ID, inbox.ID, contact.ID)
|
|
newerCreatedMessage := &model.Message{AccountID: account.ID, InboxID: inbox.ID, ConversationID: conversation.ID, MessageType: "incoming", ContentType: "text", Content: "newer by timestamp"}
|
|
newerCreatedMessage.CreatedAt = time.Now().Add(-1 * time.Hour)
|
|
newerCreatedMessage.UpdatedAt = newerCreatedMessage.CreatedAt
|
|
require.NoError(t, db.Create(newerCreatedMessage).Error)
|
|
olderCreatedMessage := &model.Message{AccountID: account.ID, InboxID: inbox.ID, ConversationID: conversation.ID, MessageType: "incoming", ContentType: "text", Content: "older by timestamp"}
|
|
olderCreatedMessage.CreatedAt = time.Now().Add(-2 * time.Hour)
|
|
olderCreatedMessage.UpdatedAt = olderCreatedMessage.CreatedAt
|
|
require.NoError(t, db.Create(olderCreatedMessage).Error)
|
|
require.Greater(t, olderCreatedMessage.ID, newerCreatedMessage.ID)
|
|
|
|
payload := serializeConversation(ctx, db, conversation)
|
|
require.Len(t, payload.Messages, 1)
|
|
require.Equal(t, newerCreatedMessage.ID, payload.Messages[0].ID)
|
|
require.Equal(t, "newer by timestamp", payload.Messages[0].Content)
|
|
}
|
|
|
|
func TestSerializeConversationMetaIncludesAgentBotAssignee(t *testing.T) {
|
|
db := newConversationSerializerTestDB(t)
|
|
ctx := context.Background()
|
|
account := &model.Account{Name: "Bot Assignee Account", Active: true}
|
|
require.NoError(t, db.Create(account).Error)
|
|
contact := &model.Contact{AccountID: account.ID, Name: "Bot Assignee Contact"}
|
|
require.NoError(t, db.Create(contact).Error)
|
|
inbox := createSerializerInbox(t, db, account.ID, "web_widget", "Web Widget")
|
|
agentBot := &model.AgentBot{AccountID: &account.ID, Name: "Routing Bot", Description: "Routes conversations", AvatarURL: "https://example.com/routing-bot.png", OutgoingURL: "https://example.com/webhook", BotType: "webhook"}
|
|
require.NoError(t, db.Create(agentBot).Error)
|
|
conversation := createSerializerConversation(t, db, account.ID, inbox.ID, contact.ID)
|
|
conversation.AssigneeAgentBotID = &agentBot.ID
|
|
require.NoError(t, db.Save(conversation).Error)
|
|
|
|
payload := serializeConversation(ctx, db, conversation)
|
|
require.Equal(t, "AgentBot", payload.Meta.AssigneeType)
|
|
require.Equal(t, agentBot.ID, payload.Meta.Assignee["id"])
|
|
require.Equal(t, "Routing Bot", payload.Meta.Assignee["name"])
|
|
require.Equal(t, "Routes conversations", payload.Meta.Assignee["description"])
|
|
require.Equal(t, "https://example.com/routing-bot.png", payload.Meta.Assignee["thumbnail"])
|
|
require.Equal(t, "https://example.com/webhook", payload.Meta.Assignee["outgoing_url"])
|
|
require.Equal(t, "webhook", payload.Meta.Assignee["bot_type"])
|
|
}
|
|
|
|
func TestSerializeConversationMetaIncludesAgentCustomFields(t *testing.T) {
|
|
db := newConversationSerializerTestDB(t)
|
|
ctx := context.Background()
|
|
account := &model.Account{Name: "Agent Custom Fields Account", Active: true}
|
|
require.NoError(t, db.Create(account).Error)
|
|
contact := &model.Contact{AccountID: account.ID, Name: "Agent Custom Fields Contact"}
|
|
require.NoError(t, db.Create(contact).Error)
|
|
inbox := createSerializerInbox(t, db, account.ID, "web_widget", "Web Widget")
|
|
customRoleID := uint(77)
|
|
agent := &model.User{AccountID: account.ID, Name: "Custom Agent", Email: "custom-agent@example.com", Password: "pass", Role: "agent", CustomRoleID: &customRoleID, CustomAttributes: datatypes.JSON([]byte(`{"region":"emea"}`))}
|
|
require.NoError(t, db.Create(agent).Error)
|
|
conversation := createSerializerConversation(t, db, account.ID, inbox.ID, contact.ID)
|
|
conversation.AssigneeID = &agent.ID
|
|
require.NoError(t, db.Save(conversation).Error)
|
|
|
|
payload := serializeConversation(ctx, db, conversation)
|
|
require.Equal(t, "User", payload.Meta.AssigneeType)
|
|
require.Equal(t, agent.ID, payload.Meta.Assignee["id"])
|
|
require.Equal(t, "Custom Agent", payload.Meta.Assignee["name"])
|
|
require.Equal(t, map[string]any{"region": "emea"}, payload.Meta.Assignee["custom_attributes"])
|
|
require.Equal(t, customRoleID, payload.Meta.Assignee["custom_role_id"])
|
|
}
|
|
|
|
func TestSerializeConversationMetaTeamUsesChatwootFieldNames(t *testing.T) {
|
|
db := newConversationSerializerTestDB(t)
|
|
ctx := context.Background()
|
|
account := &model.Account{Name: "Team Meta Account", Active: true}
|
|
require.NoError(t, db.Create(account).Error)
|
|
contact := &model.Contact{AccountID: account.ID, Name: "Team Meta Contact"}
|
|
require.NoError(t, db.Create(contact).Error)
|
|
inbox := createSerializerInbox(t, db, account.ID, "web_widget", "Web Widget")
|
|
team := &model.Team{AccountID: account.ID, Name: "Tier 1", Description: "Support team", AllowAutoAssignment: true}
|
|
require.NoError(t, db.Create(team).Error)
|
|
conversation := createSerializerConversation(t, db, account.ID, inbox.ID, contact.ID)
|
|
conversation.TeamID = &team.ID
|
|
require.NoError(t, db.Save(conversation).Error)
|
|
|
|
payload := serializeConversation(ctx, db, conversation)
|
|
require.NotNil(t, payload.Meta.Team)
|
|
require.Equal(t, team.ID, payload.Meta.Team["id"])
|
|
require.Equal(t, "Tier 1", payload.Meta.Team["name"])
|
|
require.Equal(t, true, payload.Meta.Team["allow_auto_assign"])
|
|
require.Equal(t, false, payload.Meta.Team["is_member"])
|
|
require.NotContains(t, payload.Meta.Team, "allow_auto_assignment")
|
|
require.NotContains(t, payload.Meta.Team, "created_at")
|
|
require.NotContains(t, payload.Meta.Team, "updated_at")
|
|
}
|
|
|
|
func TestSerializeConversationIncludesSlaEvents(t *testing.T) {
|
|
db := newConversationSerializerTestDB(t)
|
|
ctx := context.Background()
|
|
account := &model.Account{Name: "SLA Event Account", Active: true}
|
|
require.NoError(t, db.Create(account).Error)
|
|
contact := &model.Contact{AccountID: account.ID, Name: "SLA Event Contact"}
|
|
require.NoError(t, db.Create(contact).Error)
|
|
inbox := createSerializerInbox(t, db, account.ID, "web_widget", "Web Widget")
|
|
conversation := createSerializerConversation(t, db, account.ID, inbox.ID, contact.ID)
|
|
policy := &model.SlaPolicy{AccountID: account.ID, Name: "Gold SLA", FirstResponseTimeThreshold: 10}
|
|
require.NoError(t, db.Create(policy).Error)
|
|
applied := &model.AppliedSLA{AccountID: account.ID, ConversationID: conversation.ID, SlaPolicyID: policy.ID, SLAStatus: model.SLAStatusActive}
|
|
require.NoError(t, db.Create(applied).Error)
|
|
eventCreatedAt := time.Now().Add(-30 * time.Minute).UTC().Truncate(time.Second)
|
|
event := &model.SlaEvent{AppliedSlaID: applied.ID, AccountID: account.ID, ConversationID: conversation.ID, InboxID: inbox.ID, SlaPolicyID: policy.ID, EventType: model.SLAEventNRT, Meta: datatypes.JSON([]byte(`{"message_id":123}`))}
|
|
event.CreatedAt = eventCreatedAt
|
|
event.UpdatedAt = eventCreatedAt.Add(5 * time.Minute)
|
|
require.NoError(t, db.Create(event).Error)
|
|
|
|
payload := serializeConversation(ctx, db, conversation)
|
|
require.NotNil(t, payload.AppliedSLA)
|
|
require.Equal(t, policy.ID, payload.AppliedSLA["sla_id"])
|
|
require.Len(t, payload.SlaEvents, 1)
|
|
require.Equal(t, event.ID, payload.SlaEvents[0]["id"])
|
|
require.Equal(t, "nrt", payload.SlaEvents[0]["event_type"])
|
|
require.Equal(t, map[string]any{"message_id": float64(123)}, payload.SlaEvents[0]["meta"])
|
|
require.Equal(t, event.CreatedAt.Unix(), payload.SlaEvents[0]["created_at"])
|
|
require.Equal(t, event.UpdatedAt.Unix(), payload.SlaEvents[0]["updated_at"])
|
|
}
|
|
|
|
func TestSerializeContactUsesPresenceStatus(t *testing.T) {
|
|
contact := &model.Contact{AccountID: 7, Name: "Online Contact"}
|
|
contact.ID = 42
|
|
ctx := withContactPresence(context.Background(), fakeContactPresence{statuses: map[string]string{"42:7": "online"}})
|
|
|
|
payload := serializeContactWithContext(ctx, contact)
|
|
|
|
require.Equal(t, "online", payload["availability_status"])
|
|
require.Equal(t, "contact", payload["type"])
|
|
require.Equal(t, "offline", serializeContact(contact)["availability_status"])
|
|
}
|
|
|
|
func TestSerializeUserIncludesSenderType(t *testing.T) {
|
|
user := &model.User{AccountID: 7, Name: "Support Agent"}
|
|
user.ID = 43
|
|
|
|
payload := serializeUser(user, user.AccountID)
|
|
|
|
require.Equal(t, "user", payload["type"])
|
|
}
|
|
|
|
func TestSerializeCRMContactUsesPresenceStatus(t *testing.T) {
|
|
contact := &model.Contact{AccountID: 9, Name: "CRM Online Contact"}
|
|
contact.ID = 99
|
|
ctx := withContactPresence(context.Background(), fakeContactPresence{statuses: map[string]string{"99:9": "online"}})
|
|
|
|
payload := serializeCRMContact(ctx, nil, contact, false)
|
|
|
|
require.Equal(t, "online", payload["availability_status"])
|
|
}
|
|
|
|
func newConversationSerializerTestDB(t *testing.T) *gorm.DB {
|
|
t.Helper()
|
|
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{Logger: logger.Default.LogMode(logger.Silent)})
|
|
require.NoError(t, err)
|
|
require.NoError(t, db.AutoMigrate(&model.Account{}, &model.User{}, &model.Inbox{}, &model.Contact{}, &model.Conversation{}, &model.Message{}, &model.Attachment{}, &model.AgentBot{}, &model.Team{}, &model.SlaPolicy{}, &model.AppliedSLA{}, &model.SlaEvent{}, &model.Call{}, &channelmodel.ChannelTwilioSMS{}, &channelmodel.ChannelWhatsApp{}))
|
|
require.NoError(t, db.Exec(`CREATE TABLE channel_api (id integer primary key autoincrement, inbox_id integer, additional_attributes json)`).Error)
|
|
return db
|
|
}
|
|
|
|
func createSerializerInbox(t *testing.T, db *gorm.DB, accountID uint, channelType, name string) *model.Inbox {
|
|
t.Helper()
|
|
inbox := &model.Inbox{AccountID: accountID, Name: name, ChannelType: channelType, ChannelID: 1, Enabled: true}
|
|
require.NoError(t, db.Create(inbox).Error)
|
|
return inbox
|
|
}
|
|
|
|
func createSerializerConversation(t *testing.T, db *gorm.DB, accountID, inboxID, contactID uint) *model.Conversation {
|
|
t.Helper()
|
|
conversation := &model.Conversation{AccountID: accountID, InboxID: inboxID, ContactID: contactID, Status: "open"}
|
|
require.NoError(t, db.Create(conversation).Error)
|
|
return conversation
|
|
}
|
|
|
|
func createSerializerMessage(t *testing.T, db *gorm.DB, accountID, inboxID, conversationID uint, messageType string, createdAt time.Time) {
|
|
t.Helper()
|
|
message := &model.Message{AccountID: accountID, InboxID: inboxID, ConversationID: conversationID, MessageType: messageType, Content: "hello"}
|
|
message.CreatedAt = createdAt
|
|
message.UpdatedAt = createdAt
|
|
require.NoError(t, db.Create(message).Error)
|
|
}
|