732 lines
20 KiB
Go
732 lines
20 KiB
Go
package campaign
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"gorm.io/driver/sqlite"
|
|
"gorm.io/gorm"
|
|
|
|
"github.com/gochat/gochat/internal/channel"
|
|
"github.com/gochat/gochat/internal/model"
|
|
)
|
|
|
|
func newTestDB(t *testing.T) *gorm.DB {
|
|
t.Helper()
|
|
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
|
|
require.NoError(t, err)
|
|
err = db.AutoMigrate(
|
|
&Campaign{},
|
|
&model.Conversation{},
|
|
&model.Message{},
|
|
&model.Inbox{},
|
|
&model.Contact{},
|
|
)
|
|
require.NoError(t, err)
|
|
return db
|
|
}
|
|
|
|
// --- Model Tests ---
|
|
|
|
func TestCampaign_TableName(t *testing.T) {
|
|
assert.Equal(t, "campaigns", Campaign{}.TableName())
|
|
}
|
|
|
|
func TestCampaignStatus_Constants(t *testing.T) {
|
|
assert.Equal(t, CampaignStatus("active"), CampaignStatusActive)
|
|
assert.Equal(t, CampaignStatus("completed"), CampaignStatusCompleted)
|
|
assert.Equal(t, CampaignStatus("processing"), CampaignStatusProcessing)
|
|
}
|
|
|
|
func TestCampaignType_Constants(t *testing.T) {
|
|
assert.Equal(t, CampaignType("ongoing"), CampaignTypeOngoing)
|
|
assert.Equal(t, CampaignType("one_off"), CampaignTypeOneOff)
|
|
}
|
|
|
|
// --- Service Tests ---
|
|
|
|
func TestNewCampaignService(t *testing.T) {
|
|
db := newTestDB(t)
|
|
svc := NewCampaignService(db)
|
|
require.NotNil(t, svc)
|
|
assert.Equal(t, db, svc.db)
|
|
assert.Nil(t, svc.dispatcher)
|
|
}
|
|
|
|
func TestNewCampaignService_WithDispatcher(t *testing.T) {
|
|
db := newTestDB(t)
|
|
d := &channel.Dispatcher{}
|
|
svc := NewCampaignService(db, d)
|
|
require.NotNil(t, svc)
|
|
assert.NotNil(t, svc.dispatcher)
|
|
}
|
|
|
|
func TestCampaignService_Create(t *testing.T) {
|
|
db := newTestDB(t)
|
|
svc := NewCampaignService(db)
|
|
ctx := context.Background()
|
|
|
|
c := &Campaign{
|
|
AccountID: 1,
|
|
InboxID: 2,
|
|
Title: "Test Campaign",
|
|
Message: "Hello!",
|
|
CampaignStatus: CampaignStatusActive,
|
|
CampaignType: CampaignTypeOneOff,
|
|
Enabled: true,
|
|
}
|
|
err := svc.Create(ctx, c)
|
|
require.NoError(t, err)
|
|
assert.NotZero(t, c.ID)
|
|
assert.NotZero(t, c.DisplayID) // auto-assigned
|
|
}
|
|
|
|
func TestCampaignService_Create_WithExistingDisplayID(t *testing.T) {
|
|
db := newTestDB(t)
|
|
svc := NewCampaignService(db)
|
|
ctx := context.Background()
|
|
|
|
c := &Campaign{
|
|
AccountID: 1,
|
|
InboxID: 2,
|
|
DisplayID: 42,
|
|
Title: "Test Campaign",
|
|
Message: "Hello!",
|
|
CampaignStatus: CampaignStatusActive,
|
|
CampaignType: CampaignTypeOneOff,
|
|
Enabled: true,
|
|
}
|
|
err := svc.Create(ctx, c)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, uint(42), c.DisplayID) // should not be overwritten
|
|
}
|
|
|
|
func TestCampaignService_Create_AutoIncrementDisplayID(t *testing.T) {
|
|
db := newTestDB(t)
|
|
svc := NewCampaignService(db)
|
|
ctx := context.Background()
|
|
|
|
c1 := &Campaign{AccountID: 1, InboxID: 2, Title: "C1", Message: "M1", CampaignType: CampaignTypeOneOff}
|
|
err := svc.Create(ctx, c1)
|
|
require.NoError(t, err)
|
|
|
|
c2 := &Campaign{AccountID: 1, InboxID: 2, Title: "C2", Message: "M2", CampaignType: CampaignTypeOneOff}
|
|
err = svc.Create(ctx, c2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, uint(1), c1.DisplayID)
|
|
assert.Equal(t, uint(2), c2.DisplayID)
|
|
}
|
|
|
|
func TestCampaignService_GetByID(t *testing.T) {
|
|
db := newTestDB(t)
|
|
svc := NewCampaignService(db)
|
|
ctx := context.Background()
|
|
|
|
c := &Campaign{AccountID: 1, InboxID: 2, Title: "Test", Message: "Msg", CampaignType: CampaignTypeOneOff}
|
|
err := svc.Create(ctx, c)
|
|
require.NoError(t, err)
|
|
|
|
retrieved, err := svc.GetByID(ctx, c.ID)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, c.Title, retrieved.Title)
|
|
}
|
|
|
|
func TestCampaignService_GetByID_NotFound(t *testing.T) {
|
|
db := newTestDB(t)
|
|
svc := NewCampaignService(db)
|
|
ctx := context.Background()
|
|
|
|
_, err := svc.GetByID(ctx, 99999)
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
func TestCampaignService_ListByAccount(t *testing.T) {
|
|
db := newTestDB(t)
|
|
svc := NewCampaignService(db)
|
|
ctx := context.Background()
|
|
|
|
for i := 0; i < 3; i++ {
|
|
err := svc.Create(ctx, &Campaign{
|
|
AccountID: 1, InboxID: 2, Title: "C", Message: "M", CampaignType: CampaignTypeOneOff,
|
|
})
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
campaigns, count, err := svc.ListByAccount(ctx, 1, 0, 25)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, int64(3), count)
|
|
assert.Len(t, campaigns, 3)
|
|
}
|
|
|
|
func TestCampaignService_ListByAccount_Pagination(t *testing.T) {
|
|
db := newTestDB(t)
|
|
svc := NewCampaignService(db)
|
|
ctx := context.Background()
|
|
|
|
for i := 0; i < 5; i++ {
|
|
svc.Create(ctx, &Campaign{AccountID: 1, InboxID: 2, Title: "C", Message: "M", CampaignType: CampaignTypeOneOff})
|
|
}
|
|
|
|
campaigns, count, err := svc.ListByAccount(ctx, 1, 0, 2)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, int64(5), count)
|
|
assert.Len(t, campaigns, 2)
|
|
|
|
campaigns2, _, err := svc.ListByAccount(ctx, 1, 2, 2)
|
|
require.NoError(t, err)
|
|
assert.Len(t, campaigns2, 2)
|
|
}
|
|
|
|
func TestCampaignService_ListByInbox(t *testing.T) {
|
|
db := newTestDB(t)
|
|
svc := NewCampaignService(db)
|
|
ctx := context.Background()
|
|
|
|
svc.Create(ctx, &Campaign{AccountID: 1, InboxID: 10, Title: "C1", Message: "M", CampaignType: CampaignTypeOneOff})
|
|
svc.Create(ctx, &Campaign{AccountID: 1, InboxID: 10, Title: "C2", Message: "M", CampaignType: CampaignTypeOneOff})
|
|
svc.Create(ctx, &Campaign{AccountID: 1, InboxID: 20, Title: "C3", Message: "M", CampaignType: CampaignTypeOneOff})
|
|
|
|
campaigns, err := svc.ListByInbox(ctx, 1, 10)
|
|
require.NoError(t, err)
|
|
assert.Len(t, campaigns, 2)
|
|
}
|
|
|
|
func TestCampaignService_Update(t *testing.T) {
|
|
db := newTestDB(t)
|
|
svc := NewCampaignService(db)
|
|
ctx := context.Background()
|
|
|
|
c := &Campaign{AccountID: 1, InboxID: 2, Title: "Test", Message: "Msg", CampaignType: CampaignTypeOneOff}
|
|
err := svc.Create(ctx, c)
|
|
require.NoError(t, err)
|
|
|
|
err = svc.Update(ctx, c.ID, map[string]interface{}{"title": "Updated Title"})
|
|
require.NoError(t, err)
|
|
|
|
retrieved, err := svc.GetByID(ctx, c.ID)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "Updated Title", retrieved.Title)
|
|
}
|
|
|
|
func TestCampaignService_Delete(t *testing.T) {
|
|
db := newTestDB(t)
|
|
svc := NewCampaignService(db)
|
|
ctx := context.Background()
|
|
|
|
c := &Campaign{AccountID: 1, InboxID: 2, Title: "Test", Message: "Msg", CampaignType: CampaignTypeOneOff}
|
|
err := svc.Create(ctx, c)
|
|
require.NoError(t, err)
|
|
|
|
err = svc.Delete(ctx, c.ID)
|
|
require.NoError(t, err)
|
|
|
|
_, err = svc.GetByID(ctx, c.ID)
|
|
assert.Error(t, err) // soft deleted
|
|
}
|
|
|
|
func TestCampaignService_MarkCompleted(t *testing.T) {
|
|
db := newTestDB(t)
|
|
svc := NewCampaignService(db)
|
|
ctx := context.Background()
|
|
|
|
c := &Campaign{AccountID: 1, InboxID: 2, Title: "Test", Message: "Msg", CampaignType: CampaignTypeOneOff, CampaignStatus: CampaignStatusActive}
|
|
err := svc.Create(ctx, c)
|
|
require.NoError(t, err)
|
|
|
|
err = svc.MarkCompleted(ctx, c.ID)
|
|
require.NoError(t, err)
|
|
|
|
retrieved, err := svc.GetByID(ctx, c.ID)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, CampaignStatusCompleted, retrieved.CampaignStatus)
|
|
}
|
|
|
|
func TestCampaignService_TriggerCampaign_Disabled(t *testing.T) {
|
|
db := newTestDB(t)
|
|
svc := NewCampaignService(db)
|
|
ctx := context.Background()
|
|
|
|
c := &Campaign{
|
|
AccountID: 1,
|
|
InboxID: 2,
|
|
Title: "Test",
|
|
Message: "Msg",
|
|
CampaignType: CampaignTypeOneOff,
|
|
CampaignStatus: CampaignStatusActive,
|
|
Enabled: false,
|
|
}
|
|
err := svc.Create(ctx, c)
|
|
require.NoError(t, err)
|
|
|
|
err = svc.TriggerCampaign(ctx, c.ID)
|
|
require.NoError(t, err) // disabled campaigns are skipped
|
|
}
|
|
|
|
func TestCampaignService_TriggerCampaign_NotFound(t *testing.T) {
|
|
db := newTestDB(t)
|
|
svc := NewCampaignService(db)
|
|
ctx := context.Background()
|
|
|
|
err := svc.TriggerCampaign(ctx, 99999)
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
func TestCampaignService_TriggerCampaign_WithAudience(t *testing.T) {
|
|
db := newTestDB(t)
|
|
svc := NewCampaignService(db)
|
|
ctx := context.Background()
|
|
|
|
// Seed inbox and contacts
|
|
inbox := &model.Inbox{AccountID: 1, Name: "Test", ChannelType: "api"}
|
|
err := db.Create(inbox).Error
|
|
require.NoError(t, err)
|
|
|
|
contact1 := &model.Contact{AccountID: 1, Name: "C1"}
|
|
contact2 := &model.Contact{AccountID: 1, Name: "C2"}
|
|
err = db.Create(contact1).Error
|
|
require.NoError(t, err)
|
|
err = db.Create(contact2).Error
|
|
require.NoError(t, err)
|
|
|
|
audienceJSON, _ := json.Marshal(map[string]interface{}{
|
|
"contact_ids": []uint{contact1.ID, contact2.ID},
|
|
})
|
|
|
|
c := &Campaign{
|
|
AccountID: 1,
|
|
InboxID: inbox.ID,
|
|
Title: "Test",
|
|
Message: "Hello!",
|
|
CampaignType: CampaignTypeOneOff,
|
|
CampaignStatus: CampaignStatusActive,
|
|
Enabled: true,
|
|
Audience: string(audienceJSON),
|
|
}
|
|
err = svc.Create(ctx, c)
|
|
require.NoError(t, err)
|
|
|
|
err = svc.TriggerCampaign(ctx, c.ID)
|
|
require.NoError(t, err)
|
|
|
|
// Verify conversations were created
|
|
var convCount int64
|
|
db.Model(&model.Conversation{}).Where("campaign_id = ?", c.ID).Count(&convCount)
|
|
assert.True(t, convCount > 0, "should have created conversations")
|
|
}
|
|
|
|
func TestCampaignAudienceContactIDs_ContactIDs(t *testing.T) {
|
|
audience := `{"contact_ids": [1, 2, 3]}`
|
|
ids, err := campaignAudienceContactIDs(audience)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, []uint{1, 2, 3}, ids)
|
|
}
|
|
|
|
func TestCampaignAudienceContactIDs_Empty(t *testing.T) {
|
|
audience := `{}`
|
|
ids, err := campaignAudienceContactIDs(audience)
|
|
require.NoError(t, err)
|
|
assert.Empty(t, ids)
|
|
}
|
|
|
|
func TestCampaignAudienceContactIDs_FrontendRules(t *testing.T) {
|
|
audience := `[{"label":"VIP"}]`
|
|
ids, err := campaignAudienceContactIDs(audience)
|
|
require.NoError(t, err)
|
|
assert.Empty(t, ids)
|
|
}
|
|
|
|
func TestCampaignAudienceContactIDs_InvalidJSON(t *testing.T) {
|
|
audience := `invalid json`
|
|
ids, err := campaignAudienceContactIDs(audience)
|
|
assert.Error(t, err)
|
|
assert.Nil(t, ids)
|
|
}
|
|
|
|
// --- CampaignConversationBuilder Tests ---
|
|
|
|
func TestNewCampaignConversationBuilder(t *testing.T) {
|
|
db := newTestDB(t)
|
|
b := NewCampaignConversationBuilder(db)
|
|
require.NotNil(t, b)
|
|
assert.Nil(t, b.dispatcher)
|
|
}
|
|
|
|
func TestCampaignConversationBuilder_Build_WithContactIDs(t *testing.T) {
|
|
db := newTestDB(t)
|
|
b := NewCampaignConversationBuilder(db)
|
|
ctx := context.Background()
|
|
|
|
inbox := &model.Inbox{AccountID: 1, Name: "Test", ChannelType: "api"}
|
|
err := db.Create(inbox).Error
|
|
require.NoError(t, err)
|
|
|
|
contact := &model.Contact{AccountID: 1, Name: "C1"}
|
|
err = db.Create(contact).Error
|
|
require.NoError(t, err)
|
|
|
|
audienceJSON, _ := json.Marshal(map[string]interface{}{
|
|
"contact_ids": []uint{contact.ID},
|
|
})
|
|
|
|
c := &Campaign{
|
|
AccountID: 1,
|
|
InboxID: inbox.ID,
|
|
Title: "Test",
|
|
Message: "Hello!",
|
|
CampaignType: CampaignTypeOneOff,
|
|
CampaignStatus: CampaignStatusActive,
|
|
Enabled: true,
|
|
Audience: string(audienceJSON),
|
|
}
|
|
err = db.Create(c).Error
|
|
require.NoError(t, err)
|
|
|
|
err = b.Build(ctx, c)
|
|
require.NoError(t, err)
|
|
|
|
// Verify conversation and message were created
|
|
var conv model.Conversation
|
|
err = db.Where("campaign_id = ?", c.ID).First(&conv).Error
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "open", conv.Status)
|
|
assert.Equal(t, "campaign", conv.ChannelType)
|
|
|
|
var msg model.Message
|
|
err = db.Where("conversation_id = ?", conv.ID).First(&msg).Error
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "Hello!", msg.Content)
|
|
assert.Equal(t, "outgoing", msg.MessageType)
|
|
assert.Equal(t, "template", msg.ContentType)
|
|
}
|
|
|
|
func TestCampaignConversationBuilder_Build_InvalidAudience(t *testing.T) {
|
|
db := newTestDB(t)
|
|
b := NewCampaignConversationBuilder(db)
|
|
ctx := context.Background()
|
|
|
|
c := &Campaign{
|
|
AccountID: 1,
|
|
InboxID: 2,
|
|
Title: "Test",
|
|
Message: "Hello!",
|
|
Audience: "invalid json",
|
|
}
|
|
err := b.Build(ctx, c)
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "parse audience")
|
|
}
|
|
|
|
func TestCampaignConversationBuilder_Build_WithSender(t *testing.T) {
|
|
db := newTestDB(t)
|
|
b := NewCampaignConversationBuilder(db)
|
|
ctx := context.Background()
|
|
|
|
inbox := &model.Inbox{AccountID: 1, Name: "Test", ChannelType: "api"}
|
|
err := db.Create(inbox).Error
|
|
require.NoError(t, err)
|
|
|
|
contact := &model.Contact{AccountID: 1, Name: "C1"}
|
|
err = db.Create(contact).Error
|
|
require.NoError(t, err)
|
|
|
|
senderID := uint(100)
|
|
audienceJSON, _ := json.Marshal(map[string]interface{}{
|
|
"contact_ids": []uint{contact.ID},
|
|
})
|
|
|
|
c := &Campaign{
|
|
AccountID: 1,
|
|
InboxID: inbox.ID,
|
|
Title: "Test",
|
|
Message: "Hello!",
|
|
CampaignType: CampaignTypeOneOff,
|
|
CampaignStatus: CampaignStatusActive,
|
|
Enabled: true,
|
|
Audience: string(audienceJSON),
|
|
SenderID: &senderID,
|
|
}
|
|
err = db.Create(c).Error
|
|
require.NoError(t, err)
|
|
|
|
err = b.Build(ctx, c)
|
|
require.NoError(t, err)
|
|
|
|
var conv model.Conversation
|
|
err = db.Where("campaign_id = ?", c.ID).First(&conv).Error
|
|
require.NoError(t, err)
|
|
assert.Equal(t, &senderID, conv.AssigneeID)
|
|
}
|
|
|
|
// --- CampaignListener Tests ---
|
|
|
|
func TestNewCampaignListener(t *testing.T) {
|
|
db := newTestDB(t)
|
|
svc := NewCampaignService(db)
|
|
l := NewCampaignListener(db, svc)
|
|
require.NotNil(t, l)
|
|
assert.Equal(t, "campaign_listener", l.Name())
|
|
}
|
|
|
|
func TestCampaignListener_OnEvent_Unknown(t *testing.T) {
|
|
db := newTestDB(t)
|
|
svc := NewCampaignService(db)
|
|
l := NewCampaignListener(db, svc)
|
|
ctx := context.Background()
|
|
|
|
event := &channel.ChannelEvent{Type: "unknown.event", Data: map[string]interface{}{}}
|
|
err := l.OnEvent(ctx, event)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestCampaignListener_OnCampaignTriggered(t *testing.T) {
|
|
db := newTestDB(t)
|
|
svc := NewCampaignService(db)
|
|
l := NewCampaignListener(db, svc)
|
|
ctx := context.Background()
|
|
|
|
inbox := &model.Inbox{AccountID: 1, Name: "Test", ChannelType: "api"}
|
|
err := db.Create(inbox).Error
|
|
require.NoError(t, err)
|
|
|
|
c := &Campaign{
|
|
AccountID: 1, InboxID: inbox.ID, Title: "Test", Message: "Hi",
|
|
CampaignType: CampaignTypeOneOff, Enabled: true, Audience: `{"contact_ids":[]}`,
|
|
}
|
|
err = svc.Create(ctx, c)
|
|
require.NoError(t, err)
|
|
|
|
event := &channel.ChannelEvent{
|
|
Type: "campaign.triggered",
|
|
AccountID: 1,
|
|
Data: map[string]interface{}{"campaign_id": float64(c.ID)},
|
|
}
|
|
err = l.OnEvent(ctx, event)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestCampaignListener_OnCampaignTriggered_NoCampaignID(t *testing.T) {
|
|
db := newTestDB(t)
|
|
svc := NewCampaignService(db)
|
|
l := NewCampaignListener(db, svc)
|
|
ctx := context.Background()
|
|
|
|
event := &channel.ChannelEvent{
|
|
Type: "campaign.triggered",
|
|
Data: map[string]interface{}{},
|
|
}
|
|
err := l.OnEvent(ctx, event)
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "campaign_id not found")
|
|
}
|
|
|
|
func TestCampaignListener_OnCampaignTriggered_IntID(t *testing.T) {
|
|
db := newTestDB(t)
|
|
svc := NewCampaignService(db)
|
|
l := NewCampaignListener(db, svc)
|
|
ctx := context.Background()
|
|
|
|
inbox := &model.Inbox{AccountID: 1, Name: "Test", ChannelType: "api"}
|
|
err := db.Create(inbox).Error
|
|
require.NoError(t, err)
|
|
|
|
c := &Campaign{
|
|
AccountID: 1, InboxID: inbox.ID, Title: "Test", Message: "Hi",
|
|
CampaignType: CampaignTypeOneOff, Enabled: true, Audience: `{"contact_ids":[]}`,
|
|
}
|
|
err = svc.Create(ctx, c)
|
|
require.NoError(t, err)
|
|
|
|
event := &channel.ChannelEvent{
|
|
Type: "campaign.triggered",
|
|
Data: map[string]interface{}{"campaign_id": int(c.ID)},
|
|
}
|
|
err = l.OnEvent(ctx, event)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestCampaignListener_OnCampaignTriggered_UintID(t *testing.T) {
|
|
db := newTestDB(t)
|
|
svc := NewCampaignService(db)
|
|
l := NewCampaignListener(db, svc)
|
|
ctx := context.Background()
|
|
|
|
inbox := &model.Inbox{AccountID: 1, Name: "Test", ChannelType: "api"}
|
|
err := db.Create(inbox).Error
|
|
require.NoError(t, err)
|
|
|
|
c := &Campaign{
|
|
AccountID: 1, InboxID: inbox.ID, Title: "Test", Message: "Hi",
|
|
CampaignType: CampaignTypeOneOff, Enabled: true, Audience: `{"contact_ids":[]}`,
|
|
}
|
|
err = svc.Create(ctx, c)
|
|
require.NoError(t, err)
|
|
|
|
event := &channel.ChannelEvent{
|
|
Type: "campaign.triggered",
|
|
Data: map[string]interface{}{"campaign_id": uint(c.ID)},
|
|
}
|
|
err = l.OnEvent(ctx, event)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestCampaignListener_OnCampaignTriggered_InvalidType(t *testing.T) {
|
|
db := newTestDB(t)
|
|
svc := NewCampaignService(db)
|
|
l := NewCampaignListener(db, svc)
|
|
ctx := context.Background()
|
|
|
|
event := &channel.ChannelEvent{
|
|
Type: "campaign.triggered",
|
|
Data: map[string]interface{}{"campaign_id": "not-a-number"},
|
|
}
|
|
err := l.OnEvent(ctx, event)
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "unexpected campaign_id type")
|
|
}
|
|
|
|
func TestCampaignListener_OnConversationResolved_NonCampaignChannel(t *testing.T) {
|
|
db := newTestDB(t)
|
|
svc := NewCampaignService(db)
|
|
l := NewCampaignListener(db, svc)
|
|
ctx := context.Background()
|
|
|
|
conv := &model.Conversation{Base: model.Base{ID: 1}, AccountID: 10, InboxID: 20, ChannelType: "api"}
|
|
event := &channel.ChannelEvent{
|
|
Type: channel.EventConversationResolved,
|
|
AccountID: 10,
|
|
InboxID: 20,
|
|
Data: map[string]interface{}{"conversation": conv},
|
|
}
|
|
err := l.OnEvent(ctx, event)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestCampaignListener_OnConversationResolved_CampaignChannel_NoCampaignID(t *testing.T) {
|
|
db := newTestDB(t)
|
|
svc := NewCampaignService(db)
|
|
l := NewCampaignListener(db, svc)
|
|
ctx := context.Background()
|
|
|
|
conv := &model.Conversation{Base: model.Base{ID: 1}, AccountID: 10, InboxID: 20, ChannelType: "campaign"}
|
|
event := &channel.ChannelEvent{
|
|
Type: channel.EventConversationResolved,
|
|
AccountID: 10,
|
|
InboxID: 20,
|
|
Data: map[string]interface{}{"conversation": conv},
|
|
}
|
|
err := l.OnEvent(ctx, event)
|
|
assert.NoError(t, err) // no campaign_id — returns nil
|
|
}
|
|
|
|
func TestCampaignListener_OnConversationResolved_OneOffCampaign(t *testing.T) {
|
|
db := newTestDB(t)
|
|
svc := NewCampaignService(db)
|
|
l := NewCampaignListener(db, svc)
|
|
ctx := context.Background()
|
|
|
|
c := &Campaign{
|
|
AccountID: 1, InboxID: 2, Title: "Test", Message: "Hi",
|
|
CampaignType: CampaignTypeOneOff, CampaignStatus: CampaignStatusActive,
|
|
}
|
|
err := svc.Create(ctx, c)
|
|
require.NoError(t, err)
|
|
|
|
conv := &model.Conversation{Base: model.Base{ID: 1}, AccountID: 1, InboxID: 2, ChannelType: "campaign"}
|
|
event := &channel.ChannelEvent{
|
|
Type: channel.EventConversationResolved,
|
|
AccountID: 1,
|
|
InboxID: 2,
|
|
Data: map[string]interface{}{
|
|
"conversation": conv,
|
|
"campaign_id": float64(c.ID),
|
|
},
|
|
}
|
|
err = l.OnEvent(ctx, event)
|
|
require.NoError(t, err)
|
|
|
|
// Campaign should be marked completed
|
|
retrieved, err := svc.GetByID(ctx, c.ID)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, CampaignStatusCompleted, retrieved.CampaignStatus)
|
|
}
|
|
|
|
func TestCampaignListener_OnConversationResolved_OngoingCampaign(t *testing.T) {
|
|
db := newTestDB(t)
|
|
svc := NewCampaignService(db)
|
|
l := NewCampaignListener(db, svc)
|
|
ctx := context.Background()
|
|
|
|
c := &Campaign{
|
|
AccountID: 1, InboxID: 2, Title: "Test", Message: "Hi",
|
|
CampaignType: CampaignTypeOngoing, CampaignStatus: CampaignStatusActive,
|
|
}
|
|
err := svc.Create(ctx, c)
|
|
require.NoError(t, err)
|
|
|
|
conv := &model.Conversation{Base: model.Base{ID: 1}, AccountID: 1, InboxID: 2, ChannelType: "campaign"}
|
|
event := &channel.ChannelEvent{
|
|
Type: channel.EventConversationResolved,
|
|
AccountID: 1,
|
|
InboxID: 2,
|
|
Data: map[string]interface{}{
|
|
"conversation": conv,
|
|
"campaign_id": float64(c.ID),
|
|
},
|
|
}
|
|
err = l.OnEvent(ctx, event)
|
|
require.NoError(t, err)
|
|
|
|
// Ongoing campaigns should NOT be marked completed
|
|
retrieved, err := svc.GetByID(ctx, c.ID)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, CampaignStatusActive, retrieved.CampaignStatus)
|
|
}
|
|
|
|
func TestCampaignListener_OnConversationResolved_NoConversation(t *testing.T) {
|
|
db := newTestDB(t)
|
|
svc := NewCampaignService(db)
|
|
l := NewCampaignListener(db, svc)
|
|
ctx := context.Background()
|
|
|
|
event := &channel.ChannelEvent{
|
|
Type: channel.EventConversationResolved,
|
|
Data: map[string]interface{}{},
|
|
}
|
|
err := l.OnEvent(ctx, event)
|
|
assert.NoError(t, err) // no conversation — returns nil
|
|
}
|
|
|
|
func TestCampaignListener_OnConversationResolved_CampaignNotFound(t *testing.T) {
|
|
db := newTestDB(t)
|
|
svc := NewCampaignService(db)
|
|
l := NewCampaignListener(db, svc)
|
|
ctx := context.Background()
|
|
|
|
conv := &model.Conversation{Base: model.Base{ID: 1}, AccountID: 1, InboxID: 2, ChannelType: "campaign"}
|
|
event := &channel.ChannelEvent{
|
|
Type: channel.EventConversationResolved,
|
|
Data: map[string]interface{}{
|
|
"conversation": conv,
|
|
"campaign_id": float64(99999),
|
|
},
|
|
}
|
|
err := l.OnEvent(ctx, event)
|
|
assert.NoError(t, err) // campaign not found — returns nil
|
|
}
|
|
|
|
func TestCampaign_ScheduledAt(t *testing.T) {
|
|
now := time.Now()
|
|
c := Campaign{ScheduledAt: &now}
|
|
assert.Equal(t, &now, c.ScheduledAt)
|
|
}
|
|
|
|
func TestCampaign_TriggerOnlyDuringBusinessHours(t *testing.T) {
|
|
c := Campaign{TriggerOnlyDuringBusinessHours: true}
|
|
assert.True(t, c.TriggerOnlyDuringBusinessHours)
|
|
}
|