* H-300: wire Captain Skills into Web runtime * H-300: enforce effective model and conservative skill budget * H-300: fix CI gosec step * ci: extend golangci-lint timeout * fix lint findings across backend * fix(push): resolve delivery protocol blockers * test(repository): close SQLite test databases * test(repository): reuse SQLite schema per package * H-307: restore backend Go cache in CI * H-307: prefetch modules before cold lint * H-307: resolve govulncheck security gate * H-307: build lint with patched Go toolchain * H-307: clear remaining security scan findings --------- Co-authored-by: Rogee <rogee@ipao.vip>
752 lines
24 KiB
Go
752 lines
24 KiB
Go
package automation
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/gochat/gochat/internal/channel"
|
|
"github.com/gochat/gochat/internal/model"
|
|
)
|
|
|
|
// --- Action Delivery tests ---
|
|
|
|
func TestNewHTTPAutomationWebhookDeliverer_Cov4(t *testing.T) {
|
|
d := NewHTTPAutomationWebhookDeliverer(nil, 0, 0)
|
|
assert.NotNil(t, d)
|
|
assert.Equal(t, defaultActionDeliveryAttempts, d.maxAttempts)
|
|
}
|
|
|
|
func TestNewHTTPAutomationWebhookDeliverer_WithClient_Cov4(t *testing.T) {
|
|
client := &http.Client{Timeout: 5 * time.Second}
|
|
d := NewHTTPAutomationWebhookDeliverer(client, 5, 2*time.Second)
|
|
assert.NotNil(t, d)
|
|
assert.Equal(t, 5, d.maxAttempts)
|
|
assert.Equal(t, 2*time.Second, d.retryDelay)
|
|
}
|
|
|
|
func TestNewHTTPAutomationWebhookDeliverer_ZeroTimeout_Cov4(t *testing.T) {
|
|
client := &http.Client{}
|
|
d := NewHTTPAutomationWebhookDeliverer(client, 1, 0)
|
|
assert.NotNil(t, d)
|
|
assert.Equal(t, defaultActionDeliveryTimeout, client.Timeout)
|
|
}
|
|
|
|
func TestHTTPAutomationWebhookDeliverer_DeliverWebhook_EmptyURL_Cov4(t *testing.T) {
|
|
d := NewHTTPAutomationWebhookDeliverer(nil, 1, 0)
|
|
_, err := d.DeliverWebhook(context.Background(), AutomationWebhookRequest{})
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "URL is required")
|
|
}
|
|
|
|
func TestHTTPAutomationWebhookDeliverer_DeliverWebhook_Success_Cov4(t *testing.T) {
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
assert.Equal(t, "application/json", r.Header.Get("Content-Type"))
|
|
assert.NotEmpty(t, r.Header.Get("X-Webhook-Event"))
|
|
w.WriteHeader(http.StatusOK)
|
|
_, err := w.Write([]byte("ok"))
|
|
require.NoError(t, err)
|
|
}))
|
|
defer srv.Close()
|
|
|
|
d := NewHTTPAutomationWebhookDeliverer(srv.Client(), 1, 0)
|
|
result, err := d.DeliverWebhook(context.Background(), AutomationWebhookRequest{
|
|
URL: srv.URL,
|
|
EventName: "test_event",
|
|
Payload: map[string]interface{}{"key": "value"},
|
|
})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 200, result.ResponseCode)
|
|
assert.False(t, result.Retryable)
|
|
assert.Equal(t, 1, result.Attempts)
|
|
}
|
|
|
|
func TestHTTPAutomationWebhookDeliverer_DeliverWebhook_ServerError_Cov4(t *testing.T) {
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
}))
|
|
defer srv.Close()
|
|
|
|
d := NewHTTPAutomationWebhookDeliverer(srv.Client(), 2, 1*time.Millisecond)
|
|
result, err := d.DeliverWebhook(context.Background(), AutomationWebhookRequest{
|
|
URL: srv.URL,
|
|
EventName: "test_event",
|
|
Payload: map[string]interface{}{"key": "value"},
|
|
})
|
|
assert.Error(t, err)
|
|
assert.True(t, result.Retryable)
|
|
assert.Equal(t, 500, result.ResponseCode)
|
|
}
|
|
|
|
func TestHTTPAutomationWebhookDeliverer_DeliverWebhook_ClientError_Cov4(t *testing.T) {
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
}))
|
|
defer srv.Close()
|
|
|
|
d := NewHTTPAutomationWebhookDeliverer(srv.Client(), 2, 0)
|
|
result, err := d.DeliverWebhook(context.Background(), AutomationWebhookRequest{
|
|
URL: srv.URL,
|
|
EventName: "test_event",
|
|
Payload: map[string]interface{}{"key": "value"},
|
|
})
|
|
assert.Error(t, err)
|
|
assert.False(t, result.Retryable)
|
|
assert.Equal(t, 400, result.ResponseCode)
|
|
}
|
|
|
|
func TestHTTPAutomationWebhookDeliverer_DeliverWebhook_TooManyRequests_Cov4(t *testing.T) {
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusTooManyRequests)
|
|
}))
|
|
defer srv.Close()
|
|
|
|
d := NewHTTPAutomationWebhookDeliverer(srv.Client(), 2, 1*time.Millisecond)
|
|
result, err := d.DeliverWebhook(context.Background(), AutomationWebhookRequest{
|
|
URL: srv.URL,
|
|
EventName: "test_event",
|
|
Payload: map[string]interface{}{"key": "value"},
|
|
})
|
|
assert.Error(t, err)
|
|
assert.True(t, result.Retryable)
|
|
assert.Equal(t, 429, result.ResponseCode)
|
|
}
|
|
|
|
func TestHTTPAutomationWebhookDeliverer_DeliverWebhook_BadURL_Cov4(t *testing.T) {
|
|
d := NewHTTPAutomationWebhookDeliverer(nil, 1, 0)
|
|
_, err := d.DeliverWebhook(context.Background(), AutomationWebhookRequest{
|
|
URL: "http://[::1]:invalid",
|
|
EventName: "test_event",
|
|
Payload: map[string]interface{}{"key": "value"},
|
|
})
|
|
// May get URL parse error or connection error
|
|
_ = err
|
|
}
|
|
|
|
func TestHTTPAutomationWebhookDeliverer_DeliverWebhook_WebhookEventName_Cov4(t *testing.T) {
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
assert.Equal(t, "custom_event", r.Header.Get("X-Webhook-Event"))
|
|
w.WriteHeader(http.StatusOK)
|
|
}))
|
|
defer srv.Close()
|
|
|
|
d := NewHTTPAutomationWebhookDeliverer(srv.Client(), 1, 0)
|
|
_, err := d.DeliverWebhook(context.Background(), AutomationWebhookRequest{
|
|
URL: srv.URL,
|
|
WebhookEvent: "custom_event",
|
|
Payload: map[string]interface{}{"key": "value"},
|
|
})
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestHTTPAutomationWebhookDeliverer_DeliverWebhook_ContextCancelled_Cov4(t *testing.T) {
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
time.Sleep(100 * time.Millisecond)
|
|
w.WriteHeader(http.StatusOK)
|
|
}))
|
|
defer srv.Close()
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
cancel()
|
|
|
|
d := NewHTTPAutomationWebhookDeliverer(srv.Client(), 3, 10*time.Millisecond)
|
|
_, err := d.DeliverWebhook(ctx, AutomationWebhookRequest{
|
|
URL: srv.URL,
|
|
EventName: "test_event",
|
|
Payload: map[string]interface{}{"key": "value"},
|
|
})
|
|
_ = err
|
|
}
|
|
|
|
// --- SMTP Transcript tests ---
|
|
|
|
func TestNewEnvAutomationTranscriptDeliverer_Cov4(t *testing.T) {
|
|
d := NewEnvAutomationTranscriptDeliverer()
|
|
assert.NotNil(t, d)
|
|
}
|
|
|
|
func TestSMTPAutomationTranscriptDeliverer_DeliverTranscript_EmptyRecipient_Cov4(t *testing.T) {
|
|
d := &SMTPAutomationTranscriptDeliverer{}
|
|
_, err := d.DeliverTranscript(context.Background(), AutomationTranscriptRequest{})
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "recipient")
|
|
}
|
|
|
|
func TestSMTPAutomationTranscriptDeliverer_DeliverTranscript_NoSMTPConfig_Cov4(t *testing.T) {
|
|
d := &SMTPAutomationTranscriptDeliverer{}
|
|
result, err := d.DeliverTranscript(context.Background(), AutomationTranscriptRequest{Recipient: "test@example.com"})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "smtp_not_configured", result.ResponseBody)
|
|
}
|
|
|
|
func TestSMTPAutomationTranscriptDeliverer_DeliverTranscript_WithSMTP_Cov4(t *testing.T) {
|
|
d := &SMTPAutomationTranscriptDeliverer{
|
|
Address: "localhost",
|
|
Port: 25,
|
|
Username: "user",
|
|
Password: "pass",
|
|
From: "from@example.com",
|
|
maxAttempts: 1,
|
|
timeout: 1 * time.Second,
|
|
}
|
|
result, _ := d.DeliverTranscript(context.Background(), AutomationTranscriptRequest{
|
|
Recipient: "test@example.com",
|
|
Subject: "Test",
|
|
Body: "Test body",
|
|
})
|
|
assert.Equal(t, "email_transcript", result.DeliveryType)
|
|
assert.True(t, result.Attempts >= 1)
|
|
}
|
|
|
|
func TestSMTPAutomationTranscriptDeliverer_DeliverTranscript_DefaultFrom_Cov4(t *testing.T) {
|
|
d := &SMTPAutomationTranscriptDeliverer{
|
|
Address: "localhost",
|
|
Port: 25,
|
|
maxAttempts: 1,
|
|
timeout: 1 * time.Second,
|
|
}
|
|
result, _ := d.DeliverTranscript(context.Background(), AutomationTranscriptRequest{
|
|
Recipient: "test@example.com",
|
|
Subject: "Test",
|
|
Body: "Test body",
|
|
})
|
|
assert.Equal(t, "email_transcript", result.DeliveryType)
|
|
}
|
|
|
|
func TestSMTPAutomationTranscriptDeliverer_send_Cov4(t *testing.T) {
|
|
d := &SMTPAutomationTranscriptDeliverer{
|
|
Address: "localhost",
|
|
Port: 25,
|
|
From: "Chatwoot <accounts@chatwoot.com>",
|
|
}
|
|
err := d.send(context.Background(), AutomationTranscriptRequest{
|
|
Recipient: "test@example.com",
|
|
Subject: "Test",
|
|
Body: "Body",
|
|
})
|
|
assert.Error(t, err) // Will fail to connect
|
|
}
|
|
|
|
func TestSMTPAutomationTranscriptDeliverer_send_ContextCancelled_Cov4(t *testing.T) {
|
|
d := &SMTPAutomationTranscriptDeliverer{
|
|
Address: "localhost",
|
|
Port: 25,
|
|
}
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
cancel()
|
|
err := d.send(ctx, AutomationTranscriptRequest{
|
|
Recipient: "test@example.com",
|
|
Subject: "Test",
|
|
Body: "Body",
|
|
})
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
func TestSmtpTranscriptMessage_Cov4(t *testing.T) {
|
|
msg := smtpTranscriptMessage("from@test.com", "to@test.com", "Subject", "Body")
|
|
assert.Contains(t, msg, "From: from@test.com")
|
|
assert.Contains(t, msg, "To: to@test.com")
|
|
assert.Contains(t, msg, "Subject: Subject")
|
|
assert.Contains(t, msg, "Body")
|
|
assert.Contains(t, msg, "MIME-Version: 1.0")
|
|
}
|
|
|
|
func TestWebhookEventName_Cov4(t *testing.T) {
|
|
// With WebhookEvent set
|
|
req := AutomationWebhookRequest{WebhookEvent: "custom_event"}
|
|
assert.Equal(t, "custom_event", webhookEventName(req))
|
|
|
|
// With EventName set, no WebhookEvent
|
|
req = AutomationWebhookRequest{EventName: "test"}
|
|
assert.Equal(t, "automation_event.test", webhookEventName(req))
|
|
|
|
// With empty WebhookEvent (whitespace)
|
|
req = AutomationWebhookRequest{WebhookEvent: " ", EventName: "test"}
|
|
assert.Equal(t, "automation_event.test", webhookEventName(req))
|
|
}
|
|
|
|
func TestFirstActionEnv_Cov4(t *testing.T) {
|
|
// Test with no env vars set
|
|
result := firstActionEnv("NONEXISTENT_KEY_1", "NONEXISTENT_KEY_2")
|
|
assert.Empty(t, result)
|
|
}
|
|
|
|
func TestActionEnvInt_Cov4(t *testing.T) {
|
|
// Test with non-existent key
|
|
result := actionEnvInt("NONEXISTENT_KEY_INT", 587)
|
|
assert.Equal(t, 587, result)
|
|
}
|
|
|
|
// --- Listener tests ---
|
|
|
|
func TestAutomationRuleListener_Name_Cov4(t *testing.T) {
|
|
l := NewAutomationRuleListener(nil)
|
|
assert.Equal(t, "automation_rule_listener", l.Name())
|
|
}
|
|
|
|
func TestAutomationRuleListener_OnEvent_UnknownEvent_Cov4(t *testing.T) {
|
|
l := NewAutomationRuleListener(nil)
|
|
err := l.OnEvent(context.Background(), &channel.ChannelEvent{
|
|
Type: channel.EventType("unknown_event"),
|
|
Data: map[string]interface{}{},
|
|
})
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestAutomationRuleListener_OnEvent_PerformedByAutomation_Cov4(t *testing.T) {
|
|
l := NewAutomationRuleListener(nil)
|
|
err := l.OnEvent(context.Background(), &channel.ChannelEvent{
|
|
Type: channel.EventMessageCreated,
|
|
Data: map[string]interface{}{
|
|
"performed_by": "automation_rule",
|
|
},
|
|
})
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestAutomationRuleListener_OnEvent_PerformedByAutomationRuleStruct_Cov4(t *testing.T) {
|
|
l := NewAutomationRuleListener(nil)
|
|
err := l.OnEvent(context.Background(), &channel.ChannelEvent{
|
|
Type: channel.EventMessageCreated,
|
|
Data: map[string]interface{}{
|
|
"performed_by": AutomationRule{},
|
|
},
|
|
})
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestAutomationRuleListener_OnEvent_PerformedByAutomationRulePointer_Cov4(t *testing.T) {
|
|
l := NewAutomationRuleListener(nil)
|
|
err := l.OnEvent(context.Background(), &channel.ChannelEvent{
|
|
Type: channel.EventMessageCreated,
|
|
Data: map[string]interface{}{
|
|
"performed_by": &AutomationRule{},
|
|
},
|
|
})
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestAutomationRuleListener_OnEvent_NoConversationID_Cov4(t *testing.T) {
|
|
l := NewAutomationRuleListener(nil)
|
|
err := l.OnEvent(context.Background(), &channel.ChannelEvent{
|
|
Type: channel.EventMessageCreated,
|
|
Data: map[string]interface{}{},
|
|
})
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestAutomationRuleListener_OnEvent_NoAccountID_Cov4(t *testing.T) {
|
|
l := NewAutomationRuleListener(nil)
|
|
err := l.OnEvent(context.Background(), &channel.ChannelEvent{
|
|
Type: channel.EventMessageCreated,
|
|
AccountID: 0,
|
|
Data: map[string]interface{}{
|
|
"conversation_id": uint(1),
|
|
},
|
|
})
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestAutomationRuleListener_OnEvent_MessageCreated_Activity_Cov4(t *testing.T) {
|
|
l := NewAutomationRuleListener(nil)
|
|
msg := &model.Message{MessageType: "activity"}
|
|
err := l.OnEvent(context.Background(), &channel.ChannelEvent{
|
|
Type: channel.EventMessageCreated,
|
|
AccountID: 1,
|
|
Data: map[string]interface{}{
|
|
"conversation_id": uint(1),
|
|
"message": msg,
|
|
},
|
|
})
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestAutomationRuleListener_OnEvent_MessageCreated_AutoReply_Cov4(t *testing.T) {
|
|
l := NewAutomationRuleListener(nil)
|
|
msg := &model.Message{
|
|
ContentAttributes: []byte(`{"email":{"auto_reply":true}}`),
|
|
}
|
|
err := l.OnEvent(context.Background(), &channel.ChannelEvent{
|
|
Type: channel.EventMessageCreated,
|
|
AccountID: 1,
|
|
Data: map[string]interface{}{
|
|
"conversation_id": uint(1),
|
|
"message": msg,
|
|
},
|
|
})
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestAutomationRuleListener_OnEvent_ConversationCreated_AutoReply_Cov4(t *testing.T) {
|
|
l := NewAutomationRuleListener(nil)
|
|
conv := &model.Conversation{
|
|
AdditionalAttributes: []byte(`{"auto_reply":true}`),
|
|
}
|
|
err := l.OnEvent(context.Background(), &channel.ChannelEvent{
|
|
Type: channel.EventConversationCreated,
|
|
AccountID: 1,
|
|
Data: map[string]interface{}{
|
|
"conversation": conv,
|
|
},
|
|
})
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestMapEventToAutomationEventName_Cov4(t *testing.T) {
|
|
assert.Equal(t, "conversation_created", mapEventToAutomationEventName(channel.EventConversationCreated))
|
|
assert.Equal(t, "conversation_updated", mapEventToAutomationEventName(channel.EventConversationUpdated))
|
|
assert.Equal(t, "conversation_opened", mapEventToAutomationEventName(channel.EventConversationOpened))
|
|
assert.Equal(t, "conversation_resolved", mapEventToAutomationEventName(channel.EventConversationResolved))
|
|
assert.Equal(t, "message_created", mapEventToAutomationEventName(channel.EventMessageCreated))
|
|
assert.Equal(t, "", mapEventToAutomationEventName(channel.EventType("unknown")))
|
|
}
|
|
|
|
func TestIsValidAutomationEventName_Cov4(t *testing.T) {
|
|
assert.True(t, isValidAutomationEventName("conversation_created"))
|
|
assert.True(t, isValidAutomationEventName("conversation_updated"))
|
|
assert.True(t, isValidAutomationEventName("conversation_opened"))
|
|
assert.True(t, isValidAutomationEventName("conversation_resolved"))
|
|
assert.True(t, isValidAutomationEventName("message_created"))
|
|
assert.False(t, isValidAutomationEventName("unknown_event"))
|
|
}
|
|
|
|
func TestNormalizeEventName_Cov4(t *testing.T) {
|
|
assert.Equal(t, "conversation_created", normalizeEventName("conversation.created"))
|
|
assert.Equal(t, "conversation_created", normalizeEventName("Conversation.Created"))
|
|
assert.Equal(t, "message_created", normalizeEventName("message.created"))
|
|
}
|
|
|
|
func TestExtractUintFromData_Cov4(t *testing.T) {
|
|
data := map[string]interface{}{
|
|
"uint_val": uint(42),
|
|
"int_val": int(42),
|
|
"float_val": float64(42),
|
|
"string_val": "42",
|
|
"bad_string": "abc",
|
|
"other_val": []string{"not a number"},
|
|
}
|
|
|
|
val, ok := extractUintFromData(data, "uint_val")
|
|
assert.True(t, ok)
|
|
assert.Equal(t, uint(42), val)
|
|
|
|
val, ok = extractUintFromData(data, "int_val")
|
|
assert.True(t, ok)
|
|
assert.Equal(t, uint(42), val)
|
|
|
|
val, ok = extractUintFromData(data, "float_val")
|
|
assert.True(t, ok)
|
|
assert.Equal(t, uint(42), val)
|
|
|
|
val, ok = extractUintFromData(data, "string_val")
|
|
assert.True(t, ok)
|
|
assert.Equal(t, uint(42), val)
|
|
|
|
_, ok = extractUintFromData(data, "bad_string")
|
|
assert.False(t, ok)
|
|
|
|
_, ok = extractUintFromData(data, "other_val")
|
|
assert.False(t, ok)
|
|
|
|
_, ok = extractUintFromData(data, "nonexistent")
|
|
assert.False(t, ok)
|
|
}
|
|
|
|
func TestTruthy_Cov4(t *testing.T) {
|
|
assert.True(t, truthy(true))
|
|
assert.True(t, truthy("true"))
|
|
assert.True(t, truthy("1"))
|
|
assert.True(t, truthy(float64(1)))
|
|
assert.True(t, truthy(int(1)))
|
|
assert.False(t, truthy(false))
|
|
assert.False(t, truthy("false"))
|
|
assert.False(t, truthy(""))
|
|
assert.False(t, truthy(float64(0)))
|
|
assert.False(t, truthy(int(0)))
|
|
assert.False(t, truthy(nil))
|
|
assert.False(t, truthy([]string{}))
|
|
}
|
|
|
|
func TestListenerJSONMap_Cov4(t *testing.T) {
|
|
// Empty bytes
|
|
result := listenerJSONMap(nil)
|
|
assert.NotNil(t, result)
|
|
assert.Empty(t, result)
|
|
|
|
// Valid JSON
|
|
result = listenerJSONMap([]byte(`{"key":"value"}`))
|
|
assert.Equal(t, "value", result["key"])
|
|
|
|
// Invalid JSON
|
|
result = listenerJSONMap([]byte(`invalid`))
|
|
assert.NotNil(t, result)
|
|
assert.Empty(t, result)
|
|
}
|
|
|
|
func TestExtractConversationFromData_Cov4(t *testing.T) {
|
|
// nil data
|
|
_, ok := extractConversationFromData(nil)
|
|
assert.False(t, ok)
|
|
|
|
// no conversation key
|
|
_, ok = extractConversationFromData(map[string]interface{}{})
|
|
assert.False(t, ok)
|
|
|
|
// nil conversation
|
|
_, ok = extractConversationFromData(map[string]interface{}{"conversation": nil})
|
|
assert.False(t, ok)
|
|
|
|
// pointer to conversation
|
|
conv := &model.Conversation{Base: model.Base{ID: 1}}
|
|
result, ok := extractConversationFromData(map[string]interface{}{"conversation": conv})
|
|
assert.True(t, ok)
|
|
assert.Equal(t, uint(1), result.ID)
|
|
|
|
// value conversation
|
|
convVal := model.Conversation{Base: model.Base{ID: 2}}
|
|
result, ok = extractConversationFromData(map[string]interface{}{"conversation": convVal})
|
|
assert.True(t, ok)
|
|
assert.Equal(t, uint(2), result.ID)
|
|
|
|
// wrong type
|
|
_, ok = extractConversationFromData(map[string]interface{}{"conversation": "not a conversation"})
|
|
assert.False(t, ok)
|
|
}
|
|
|
|
func TestExtractMessageFromData_Cov4(t *testing.T) {
|
|
// nil data
|
|
_, ok := extractMessageFromData(nil)
|
|
assert.False(t, ok)
|
|
|
|
// no message key
|
|
_, ok = extractMessageFromData(map[string]interface{}{})
|
|
assert.False(t, ok)
|
|
|
|
// nil message
|
|
_, ok = extractMessageFromData(map[string]interface{}{"message": nil})
|
|
assert.False(t, ok)
|
|
|
|
// pointer to message
|
|
msg := &model.Message{Base: model.Base{ID: 1}}
|
|
result, ok := extractMessageFromData(map[string]interface{}{"message": msg})
|
|
assert.True(t, ok)
|
|
assert.Equal(t, uint(1), result.ID)
|
|
|
|
// value message
|
|
msgVal := model.Message{Base: model.Base{ID: 2}}
|
|
result, ok = extractMessageFromData(map[string]interface{}{"message": msgVal})
|
|
assert.True(t, ok)
|
|
assert.Equal(t, uint(2), result.ID)
|
|
|
|
// wrong type
|
|
_, ok = extractMessageFromData(map[string]interface{}{"message": "not a message"})
|
|
assert.False(t, ok)
|
|
}
|
|
|
|
func TestExtractConversationIDFromEvent_Cov4(t *testing.T) {
|
|
// nil event
|
|
_, ok := extractConversationIDFromEvent(nil)
|
|
assert.False(t, ok)
|
|
|
|
// direct ConversationID
|
|
id, ok := extractConversationIDFromEvent(&channel.ChannelEvent{ConversationID: 5})
|
|
assert.True(t, ok)
|
|
assert.Equal(t, uint(5), id)
|
|
|
|
// from data map
|
|
id, ok = extractConversationIDFromEvent(&channel.ChannelEvent{
|
|
Data: map[string]interface{}{"conversation_id": uint(10)},
|
|
})
|
|
assert.True(t, ok)
|
|
assert.Equal(t, uint(10), id)
|
|
|
|
// from conversation in data
|
|
id, ok = extractConversationIDFromEvent(&channel.ChannelEvent{
|
|
Data: map[string]interface{}{"conversation": &model.Conversation{Base: model.Base{ID: 15}}},
|
|
})
|
|
assert.True(t, ok)
|
|
assert.Equal(t, uint(15), id)
|
|
|
|
// from message in data
|
|
id, ok = extractConversationIDFromEvent(&channel.ChannelEvent{
|
|
Data: map[string]interface{}{"message": &model.Message{ConversationID: 20}},
|
|
})
|
|
assert.True(t, ok)
|
|
assert.Equal(t, uint(20), id)
|
|
|
|
// not found
|
|
_, ok = extractConversationIDFromEvent(&channel.ChannelEvent{
|
|
Data: map[string]interface{}{},
|
|
})
|
|
assert.False(t, ok)
|
|
}
|
|
|
|
func TestExtractAccountIDFromEvent_Cov4(t *testing.T) {
|
|
// nil event
|
|
assert.Equal(t, uint(0), extractAccountIDFromEvent(nil))
|
|
|
|
// from data map
|
|
id := extractAccountIDFromEvent(&channel.ChannelEvent{
|
|
Data: map[string]interface{}{"account_id": uint(10)},
|
|
})
|
|
assert.Equal(t, uint(10), id)
|
|
|
|
// from conversation in data
|
|
id = extractAccountIDFromEvent(&channel.ChannelEvent{
|
|
Data: map[string]interface{}{"conversation": &model.Conversation{AccountID: 15}},
|
|
})
|
|
assert.Equal(t, uint(15), id)
|
|
|
|
// from message in data
|
|
id = extractAccountIDFromEvent(&channel.ChannelEvent{
|
|
Data: map[string]interface{}{"message": &model.Message{AccountID: 20}},
|
|
})
|
|
assert.Equal(t, uint(20), id)
|
|
|
|
// not found
|
|
id = extractAccountIDFromEvent(&channel.ChannelEvent{
|
|
Data: map[string]interface{}{},
|
|
})
|
|
assert.Equal(t, uint(0), id)
|
|
}
|
|
|
|
func TestPerformedByAutomation_Cov4(t *testing.T) {
|
|
// nil event
|
|
assert.False(t, performedByAutomation(nil))
|
|
|
|
// nil data
|
|
assert.False(t, performedByAutomation(&channel.ChannelEvent{}))
|
|
|
|
// no performed_by key
|
|
assert.False(t, performedByAutomation(&channel.ChannelEvent{Data: map[string]interface{}{}}))
|
|
|
|
// nil performed_by
|
|
assert.False(t, performedByAutomation(&channel.ChannelEvent{Data: map[string]interface{}{"performed_by": nil}}))
|
|
|
|
// string "automation_rule"
|
|
assert.True(t, performedByAutomation(&channel.ChannelEvent{Data: map[string]interface{}{"performed_by": "automation_rule"}}))
|
|
|
|
// string "AutomationRule"
|
|
assert.True(t, performedByAutomation(&channel.ChannelEvent{Data: map[string]interface{}{"performed_by": "AutomationRule"}}))
|
|
|
|
// AutomationRule struct
|
|
assert.True(t, performedByAutomation(&channel.ChannelEvent{Data: map[string]interface{}{"performed_by": AutomationRule{}}}))
|
|
|
|
// *AutomationRule pointer
|
|
assert.True(t, performedByAutomation(&channel.ChannelEvent{Data: map[string]interface{}{"performed_by": &AutomationRule{}}}))
|
|
|
|
// other string
|
|
assert.False(t, performedByAutomation(&channel.ChannelEvent{Data: map[string]interface{}{"performed_by": "something_else"}}))
|
|
|
|
// other type
|
|
assert.False(t, performedByAutomation(&channel.ChannelEvent{Data: map[string]interface{}{"performed_by": 123}}))
|
|
}
|
|
|
|
func TestIgnoreMessageCreatedEvent_Cov4(t *testing.T) {
|
|
// nil data
|
|
assert.False(t, ignoreMessageCreatedEvent(&channel.ChannelEvent{}))
|
|
|
|
// no message
|
|
assert.False(t, ignoreMessageCreatedEvent(&channel.ChannelEvent{Data: map[string]interface{}{}}))
|
|
|
|
// activity message
|
|
msg := &model.Message{MessageType: "activity"}
|
|
assert.True(t, ignoreMessageCreatedEvent(&channel.ChannelEvent{Data: map[string]interface{}{"message": msg}}))
|
|
|
|
// regular message
|
|
msg = &model.Message{MessageType: "incoming"}
|
|
assert.False(t, ignoreMessageCreatedEvent(&channel.ChannelEvent{Data: map[string]interface{}{"message": msg}}))
|
|
}
|
|
|
|
func TestIgnoreAutoReplyConversationEvent_Cov4(t *testing.T) {
|
|
// nil data
|
|
assert.False(t, ignoreAutoReplyConversationEvent(&channel.ChannelEvent{}))
|
|
|
|
// no conversation
|
|
assert.False(t, ignoreAutoReplyConversationEvent(&channel.ChannelEvent{Data: map[string]interface{}{}}))
|
|
|
|
// regular conversation
|
|
conv := &model.Conversation{}
|
|
assert.False(t, ignoreAutoReplyConversationEvent(&channel.ChannelEvent{Data: map[string]interface{}{"conversation": conv}}))
|
|
|
|
// auto_reply conversation
|
|
conv = &model.Conversation{AdditionalAttributes: []byte(`{"auto_reply":true}`)}
|
|
assert.True(t, ignoreAutoReplyConversationEvent(&channel.ChannelEvent{Data: map[string]interface{}{"conversation": conv}}))
|
|
}
|
|
|
|
func TestShouldSkipAutomationEvent_Cov4(t *testing.T) {
|
|
// performed_by automation
|
|
assert.True(t, shouldSkipAutomationEvent(&channel.ChannelEvent{
|
|
Type: channel.EventMessageCreated,
|
|
Data: map[string]interface{}{"performed_by": "automation_rule"},
|
|
}, "message_created"))
|
|
|
|
// non-skippable event
|
|
assert.False(t, shouldSkipAutomationEvent(&channel.ChannelEvent{
|
|
Type: channel.EventConversationUpdated,
|
|
Data: map[string]interface{}{},
|
|
}, "conversation_updated"))
|
|
}
|
|
|
|
func TestAutomationWebhookRequest_Struct_Cov4(t *testing.T) {
|
|
req := AutomationWebhookRequest{
|
|
AccountID: 1,
|
|
ConversationID: 2,
|
|
EventName: "test",
|
|
WebhookEvent: "custom",
|
|
URL: "http://example.com",
|
|
Payload: map[string]interface{}{"key": "value"},
|
|
}
|
|
assert.Equal(t, uint(1), req.AccountID)
|
|
assert.Equal(t, "http://example.com", req.URL)
|
|
}
|
|
|
|
func TestAutomationTranscriptRequest_Struct_Cov4(t *testing.T) {
|
|
req := AutomationTranscriptRequest{
|
|
AccountID: 1,
|
|
ConversationID: 2,
|
|
Recipient: "test@example.com",
|
|
Subject: "Test",
|
|
Body: "Body",
|
|
}
|
|
assert.Equal(t, "test@example.com", req.Recipient)
|
|
}
|
|
|
|
func TestActionDeliveryResult_Struct_Cov4(t *testing.T) {
|
|
result := ActionDeliveryResult{
|
|
DeliveryType: "webhook",
|
|
Target: "http://example.com",
|
|
Attempts: 1,
|
|
ResponseCode: 200,
|
|
ResponseBody: "ok",
|
|
Retryable: false,
|
|
Queued: false,
|
|
}
|
|
assert.Equal(t, "webhook", result.DeliveryType)
|
|
}
|
|
|
|
func TestRegisterAutomationRuleListener_Cov4(t *testing.T) {
|
|
d := channel.NewDispatcher()
|
|
RegisterAutomationRuleListener(d, nil)
|
|
// Verify it was registered by checking the dispatcher
|
|
// (no direct way to check, but no panic = success)
|
|
}
|
|
|
|
// Test for error type assertion
|
|
func TestErrorsIs_Cov4(t *testing.T) {
|
|
err := errors.New("test error")
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
// Test strings usage
|
|
func TestStringsUsage_Cov4(t *testing.T) {
|
|
assert.True(t, strings.Contains("hello world", "world"))
|
|
}
|