package wsevent import ( "context" "encoding/json" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/gochat/gochat/internal/channel" "github.com/gochat/gochat/internal/model" wspkg "github.com/gochat/gochat/internal/ws" ) func makeMessage_Cov3(id uint) *model.Message { return &model.Message{ Base: model.Base{ID: id, CreatedAt: time.Now(), UpdatedAt: time.Now()}, ConversationID: 10, AccountID: 1, InboxID: 5, Content: "hello", MessageType: "incoming", ContentType: "text", Status: "sent", SenderType: "contact", } } func makeConversation_Cov3(id uint) *model.Conversation { displayID := uint(200) return &model.Conversation{ Base: model.Base{ID: id, CreatedAt: time.Now(), UpdatedAt: time.Now()}, DisplayID: &displayID, AccountID: 1, InboxID: 5, ContactID: 3, Status: "open", ChannelType: "web_widget", } } func makeContact_Cov3(id uint) *model.Contact { return &model.Contact{ Base: model.Base{ID: id, CreatedAt: time.Now(), UpdatedAt: time.Now()}, AccountID: 1, Name: "Alice", Email: "alice@test.com", } } func makeInbox_Cov3(id uint) *model.Inbox { return &model.Inbox{ Base: model.Base{ID: id}, AccountID: 1, ChannelType: "web_widget", Name: "Test Inbox", } } // captureHub_Cov3 implements ws.MessageHandler for testing type captureHub_Cov3 struct { accountID uint data []byte } func (h *captureHub_Cov3) SendToAccount(accountID uint, data []byte) { h.accountID = accountID h.data = append([]byte(nil), data...) } func (h *captureHub_Cov3) SendToRoom(string, []byte) {} // === eventMessage tests === func TestEventMessage_Ptr_Cov3(t *testing.T) { msg := makeMessage_Cov3(1) data := map[string]interface{}{"message": msg} result, ok := eventMessage(data) assert.True(t, ok) assert.NotNil(t, result) assert.Equal(t, uint(1), result.ID) } func TestEventMessage_NilPtr_Cov3(t *testing.T) { data := map[string]interface{}{"message": (*model.Message)(nil)} _, ok := eventMessage(data) assert.False(t, ok) } func TestEventMessage_Value_Cov3(t *testing.T) { msg := *makeMessage_Cov3(2) data := map[string]interface{}{"message": msg} result, ok := eventMessage(data) assert.True(t, ok) assert.NotNil(t, result) assert.Equal(t, uint(2), result.ID) } func TestEventMessage_Missing_Cov3(t *testing.T) { data := map[string]interface{}{} _, ok := eventMessage(data) assert.False(t, ok) } func TestEventMessage_WrongType_Cov3(t *testing.T) { data := map[string]interface{}{"message": "not a message"} _, ok := eventMessage(data) assert.False(t, ok) } // === eventConversation tests === func TestEventConversation_Ptr_Cov3(t *testing.T) { conv := makeConversation_Cov3(1) data := map[string]interface{}{"conversation": conv} result, ok := eventConversation(data) assert.True(t, ok) assert.NotNil(t, result) assert.Equal(t, uint(1), result.ID) } func TestEventConversation_NilPtr_Cov3(t *testing.T) { data := map[string]interface{}{"conversation": (*model.Conversation)(nil)} _, ok := eventConversation(data) assert.False(t, ok) } func TestEventConversation_Value_Cov3(t *testing.T) { conv := *makeConversation_Cov3(2) data := map[string]interface{}{"conversation": conv} result, ok := eventConversation(data) assert.True(t, ok) assert.NotNil(t, result) assert.Equal(t, uint(2), result.ID) } func TestEventConversation_Missing_Cov3(t *testing.T) { data := map[string]interface{}{} _, ok := eventConversation(data) assert.False(t, ok) } func TestEventConversation_WrongType_Cov3(t *testing.T) { data := map[string]interface{}{"conversation": 123} _, ok := eventConversation(data) assert.False(t, ok) } // === eventContact tests === func TestEventContact_Ptr_Cov3(t *testing.T) { contact := makeContact_Cov3(1) data := map[string]interface{}{"contact": contact} result, ok := eventContact(data) assert.True(t, ok) assert.NotNil(t, result) assert.Equal(t, uint(1), result.ID) } func TestEventContact_NilPtr_Cov3(t *testing.T) { data := map[string]interface{}{"contact": (*model.Contact)(nil)} _, ok := eventContact(data) assert.False(t, ok) } func TestEventContact_Value_Cov3(t *testing.T) { contact := *makeContact_Cov3(2) data := map[string]interface{}{"contact": contact} result, ok := eventContact(data) assert.True(t, ok) assert.NotNil(t, result) assert.Equal(t, uint(2), result.ID) } func TestEventContact_Missing_Cov3(t *testing.T) { data := map[string]interface{}{} _, ok := eventContact(data) assert.False(t, ok) } func TestEventContact_WrongType_Cov3(t *testing.T) { data := map[string]interface{}{"contact": true} _, ok := eventContact(data) assert.False(t, ok) } // === eventInbox tests === func TestEventInbox_Ptr_Cov3(t *testing.T) { inbox := makeInbox_Cov3(1) data := map[string]interface{}{"inbox": inbox} result, ok := eventInbox(data) assert.True(t, ok) assert.NotNil(t, result) assert.Equal(t, uint(1), result.ID) } func TestEventInbox_NilPtr_Cov3(t *testing.T) { data := map[string]interface{}{"inbox": (*model.Inbox)(nil)} _, ok := eventInbox(data) assert.False(t, ok) } func TestEventInbox_Value_Cov3(t *testing.T) { inbox := *makeInbox_Cov3(2) data := map[string]interface{}{"inbox": inbox} result, ok := eventInbox(data) assert.True(t, ok) assert.NotNil(t, result) assert.Equal(t, uint(2), result.ID) } func TestEventInbox_Missing_Cov3(t *testing.T) { data := map[string]interface{}{} _, ok := eventInbox(data) assert.False(t, ok) } func TestEventInbox_WrongType_Cov3(t *testing.T) { data := map[string]interface{}{"inbox": 3.14} _, ok := eventInbox(data) assert.False(t, ok) } // === messageTypeValue & nonEmpty === func TestMessageTypeValue_Cov3(t *testing.T) { assert.Equal(t, 0, messageTypeValue("incoming")) assert.Equal(t, 0, messageTypeValue(" INCOMING ")) assert.Equal(t, 2, messageTypeValue("activity")) assert.Equal(t, 3, messageTypeValue("template")) assert.Equal(t, 1, messageTypeValue("outgoing")) assert.Equal(t, 1, messageTypeValue("")) assert.Equal(t, 1, messageTypeValue("unknown")) } func TestNonEmpty_Cov3(t *testing.T) { assert.Equal(t, "fallback", nonEmpty("", "fallback")) assert.Equal(t, "fallback", nonEmpty(" ", "fallback")) assert.Equal(t, "value", nonEmpty("value", "fallback")) } // === modelMap === func TestModelMap_Cov3(t *testing.T) { msg := makeMessage_Cov3(1) result := modelMap(msg) assert.NotEmpty(t, result) assert.Equal(t, "hello", result["content"]) } func TestModelMap_Nil_Cov3(t *testing.T) { result := modelMap(nil) assert.Empty(t, result) } // === wsEventPayload === func TestWsEventPayload_Nil_Cov3(t *testing.T) { result := wsEventPayload(nil) assert.Empty(t, result) } func TestWsEventPayload_UnknownType_Cov3(t *testing.T) { event := &channel.ChannelEvent{ Type: "unknown.event", Data: map[string]interface{}{"foo": "bar"}, } result := wsEventPayload(event) assert.Equal(t, "bar", result["foo"]) } func TestWsEventPayload_MessageCreated_Cov3(t *testing.T) { msg := makeMessage_Cov3(1) conv := makeConversation_Cov3(10) contact := makeContact_Cov3(3) event := &channel.ChannelEvent{ Type: channel.EventMessageCreated, Data: map[string]interface{}{ "message": msg, "conversation": conv, "contact": contact, }, } result := wsEventPayload(event) assert.NotEmpty(t, result) assert.NotNil(t, result["sender"]) assert.NotNil(t, result["conversation"]) } func TestWsEventPayload_MessageCreatedValue_Cov3(t *testing.T) { msg := *makeMessage_Cov3(1) event := &channel.ChannelEvent{ Type: channel.EventMessageUpdated, Data: map[string]interface{}{"message": msg}, } result := wsEventPayload(event) assert.NotEmpty(t, result) } func TestWsEventPayload_MessageDeleted_Cov3(t *testing.T) { msg := makeMessage_Cov3(1) event := &channel.ChannelEvent{ Type: channel.EventMessageDeleted, Data: map[string]interface{}{"message": msg}, } result := wsEventPayload(event) assert.NotEmpty(t, result) } func TestWsEventPayload_ConversationCreated_Cov3(t *testing.T) { conv := makeConversation_Cov3(1) contact := makeContact_Cov3(3) inbox := makeInbox_Cov3(5) event := &channel.ChannelEvent{ Type: channel.EventConversationCreated, Data: map[string]interface{}{ "conversation": conv, "contact": contact, "inbox": inbox, }, } result := wsEventPayload(event) assert.NotEmpty(t, result) assert.NotNil(t, result["meta"]) } func TestWsEventPayload_ConversationResolved_Cov3(t *testing.T) { conv := makeConversation_Cov3(1) event := &channel.ChannelEvent{ Type: channel.EventConversationResolved, Data: map[string]interface{}{"conversation": conv}, } result := wsEventPayload(event) assert.NotEmpty(t, result) } func TestWsEventPayload_ContactCreated_Cov3(t *testing.T) { contact := makeContact_Cov3(1) event := &channel.ChannelEvent{ Type: channel.EventContactCreated, Data: map[string]interface{}{"contact": contact}, } result := wsEventPayload(event) assert.NotEmpty(t, result) assert.Equal(t, "offline", result["availability_status"]) } func TestWsEventPayload_ContactUpdated_NilAttrs_Cov3(t *testing.T) { contact := makeContact_Cov3(1) contact.CustomAttributes = nil contact.AdditionalAttributes = nil event := &channel.ChannelEvent{ Type: channel.EventContactUpdated, Data: map[string]interface{}{"contact": contact}, } result := wsEventPayload(event) assert.NotEmpty(t, result) assert.NotNil(t, result["additional_attributes"]) assert.NotNil(t, result["custom_attributes"]) } func TestWsEventPayload_ContactDeleted_Cov3(t *testing.T) { contact := makeContact_Cov3(1) event := &channel.ChannelEvent{ Type: channel.EventContactDeleted, Data: map[string]interface{}{"contact": contact}, } result := wsEventPayload(event) assert.NotEmpty(t, result) } // === conversationPushPayload === func TestConversationPushPayload_Cov3(t *testing.T) { conv := makeConversation_Cov3(1) lastActivity := time.Now().Unix() conv.LastActivityAt = &lastActivity contact := makeContact_Cov3(3) inbox := makeInbox_Cov3(5) data := map[string]interface{}{ "contact": contact, "inbox": inbox, } result := conversationPushPayload(conv, data) assert.NotEmpty(t, result) assert.Equal(t, uint(200), result["id"]) assert.NotNil(t, result["meta"]) assert.NotNil(t, result["messages"]) assert.Equal(t, false, result["ai_takeover_active"]) botID := uint(9) conv.AssigneeAgentBotID = &botID conv.Status = string(model.ConversationStatusPending) assert.Equal(t, true, conversationPushPayload(conv, data)["ai_takeover_active"]) } // === messagePushPayload === func TestMessagePushPayload_Cov3(t *testing.T) { msg := makeMessage_Cov3(1) conv := makeConversation_Cov3(10) contact := makeContact_Cov3(3) data := map[string]interface{}{ "conversation": conv, "contact": contact, } result := messagePushPayload(msg, data) assert.NotEmpty(t, result) assert.NotNil(t, result["sender"]) assert.NotNil(t, result["conversation"]) } func TestMessagePushPayload_NilContentAttributes_Cov3(t *testing.T) { msg := makeMessage_Cov3(1) msg.ContentAttributes = nil data := map[string]interface{}{} result := messagePushPayload(msg, data) assert.NotEmpty(t, result) assert.NotNil(t, result["content_attributes"]) } // === contactPushPayload === func TestContactPushPayload_NilAttributes_Cov3(t *testing.T) { contact := makeContact_Cov3(1) contact.AdditionalAttributes = nil contact.CustomAttributes = nil result := contactPushPayload(contact) assert.NotEmpty(t, result) assert.NotNil(t, result["additional_attributes"]) assert.NotNil(t, result["custom_attributes"]) } // === isWSEventType === func TestIsWSEventType_AllTypes_Cov3(t *testing.T) { types := []string{ "message.created", "message.updated", "message.deleted", "conversation.created", "conversation.updated", "conversation.resolved", "conversation.opened", "conversation.assigned", "conversation.unassigned", "conversation.status_changed", "conversation.typing_on", "conversation.typing_off", "conversation.mentioned", "conversation.read", "contact.created", "contact.updated", "contact.deleted", "contact.merged", "inbox.created", "inbox.updated", "inbox.deleted", "presence.update", "agent.typing_on", "agent.typing_off", "agent.online", "agent.offline", "notification.created", "notification.updated", "notification.deleted", } for _, tp := range types { assert.True(t, isWSEventType(tp), "expected true for %s", tp) } } func TestIsWSEventType_Unknown_Cov3(t *testing.T) { assert.False(t, isWSEventType("unknown.event")) assert.False(t, isWSEventType("")) } // === OnEvent === func TestOnEvent_NonWSEventType_Cov3(t *testing.T) { hub := &captureHub_Cov3{} l := New(wspkg.NewEventPublisherLocal(hub, nil)) event := &channel.ChannelEvent{ Type: "unknown.event", AccountID: 1, Data: map[string]interface{}{}, } err := l.OnEvent(context.Background(), event) require.NoError(t, err) assert.Nil(t, hub.data) // nothing published } func TestOnEvent_MessageCreated_Cov3(t *testing.T) { hub := &captureHub_Cov3{} l := New(wspkg.NewEventPublisherLocal(hub, nil)) msg := makeMessage_Cov3(1) event := &channel.ChannelEvent{ Type: channel.EventMessageCreated, AccountID: 1, ConversationID: 10, InboxID: 5, Data: map[string]interface{}{"message": msg}, } err := l.OnEvent(context.Background(), event) require.NoError(t, err) assert.NotNil(t, hub.data) var envelope map[string]interface{} require.NoError(t, json.Unmarshal(hub.data, &envelope)) payload, ok := envelope["data"].(map[string]interface{}) require.True(t, ok) assert.Equal(t, float64(1), payload["account_id"]) } func TestOnEvent_ConversationCreated_Cov3(t *testing.T) { hub := &captureHub_Cov3{} l := New(wspkg.NewEventPublisherLocal(hub, nil)) conv := makeConversation_Cov3(1) event := &channel.ChannelEvent{ Type: channel.EventConversationCreated, AccountID: 1, ConversationID: 1, InboxID: 5, Data: map[string]interface{}{"conversation": conv}, } err := l.OnEvent(context.Background(), event) require.NoError(t, err) assert.NotNil(t, hub.data) } func TestOnEvent_ConversationUpdatedPublishesInactiveTakeover_Cov3(t *testing.T) { hub := &captureHub_Cov3{} l := New(wspkg.NewEventPublisherLocal(hub, nil)) conv := makeConversation_Cov3(1) event := &channel.ChannelEvent{ Type: channel.EventConversationUpdated, AccountID: 1, ConversationID: 1, Data: map[string]interface{}{"conversation": conv}, } require.NoError(t, l.OnEvent(context.Background(), event)) var envelope map[string]interface{} require.NoError(t, json.Unmarshal(hub.data, &envelope)) payload := envelope["data"].(map[string]interface{}) assert.Equal(t, false, payload["ai_takeover_active"]) } func TestOnEvent_WithInboxID_Cov3(t *testing.T) { hub := &captureHub_Cov3{} l := New(wspkg.NewEventPublisherLocal(hub, nil)) event := &channel.ChannelEvent{ Type: "conversation.updated", AccountID: 1, InboxID: 5, Data: map[string]interface{}{"foo": "bar"}, } err := l.OnEvent(context.Background(), event) require.NoError(t, err) assert.NotNil(t, hub.data) } func TestOnEvent_ConversationIDAlreadySet_Cov3(t *testing.T) { hub := &captureHub_Cov3{} l := New(wspkg.NewEventPublisherLocal(hub, nil)) conv := makeConversation_Cov3(42) event := &channel.ChannelEvent{ Type: channel.EventConversationResolved, AccountID: 1, ConversationID: 42, InboxID: 5, Data: map[string]interface{}{ "conversation": conv, "conversation_id": float64(999), // already set in data }, } err := l.OnEvent(context.Background(), event) require.NoError(t, err) assert.NotNil(t, hub.data) }