946 lines
37 KiB
Go
946 lines
37 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"gorm.io/driver/sqlite"
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm/logger"
|
|
|
|
whatsappchannel "github.com/gochat/gochat/internal/channel/whatsapp"
|
|
"github.com/gochat/gochat/internal/model"
|
|
channelmodel "github.com/gochat/gochat/internal/model/channel"
|
|
"github.com/gochat/gochat/internal/repository"
|
|
"github.com/gochat/gochat/internal/worker"
|
|
)
|
|
|
|
// ========== Test Setup ==========
|
|
|
|
// setupInboxServiceTest creates a test DB and InboxService using SQLite in-memory.
|
|
// WhatsApp-specific repos (whatsappService, whatsappRepo) are nil since we
|
|
// test the non-WhatsApp paths exclusively (web_widget, api channel types).
|
|
func setupInboxServiceTest(t *testing.T) (*InboxService, *gorm.DB) {
|
|
t.Helper()
|
|
|
|
db, err := gorm.Open(sqlite.Open("file::memory:"), &gorm.Config{
|
|
Logger: logger.Default.LogMode(logger.Silent),
|
|
})
|
|
require.NoError(t, err, "failed to open SQLite test db")
|
|
|
|
require.NoError(t, db.AutoMigrate(
|
|
&model.Account{},
|
|
&model.AgentBot{},
|
|
&model.Inbox{},
|
|
&model.CustomAttributeDefinition{},
|
|
&model.AgentBotInbox{},
|
|
&model.WebhookSubscription{},
|
|
&model.BackgroundJob{},
|
|
&model.ChannelShangwutongConfig{},
|
|
&channelmodel.ChannelAPI{},
|
|
&channelmodel.ChannelWhatsApp{},
|
|
), "failed to auto-migrate")
|
|
|
|
t.Cleanup(func() {
|
|
sqlDB, _ := db.DB()
|
|
sqlDB.Close()
|
|
})
|
|
|
|
repo := repository.NewInboxRepo(db)
|
|
agentBotInboxRepo := repository.NewAgentBotInboxRepo(db)
|
|
agentBotRepo := repository.NewAgentBotRepo(db)
|
|
webhookSubRepo := repository.NewWebhookSubscriptionRepo(db)
|
|
|
|
waRepo := whatsappchannel.NewRepository(db)
|
|
svc := NewInboxService(repo, agentBotInboxRepo, agentBotRepo, nil, webhookSubRepo, nil, waRepo)
|
|
return svc, db
|
|
}
|
|
|
|
type fakeInboxWhatsAppService struct {
|
|
healthPayload map[string]interface{}
|
|
healthErr error
|
|
templates []interface{}
|
|
templateErr error
|
|
fetchCalls int
|
|
webhookURL string
|
|
webhookFields []string
|
|
webhookErr error
|
|
callingStatus string
|
|
callingErr error
|
|
}
|
|
|
|
func (f *fakeInboxWhatsAppService) FetchMessageTemplates(context.Context, *channelmodel.ChannelWhatsApp) ([]interface{}, error) {
|
|
f.fetchCalls++
|
|
if f.templateErr != nil {
|
|
return nil, f.templateErr
|
|
}
|
|
return f.templates, nil
|
|
}
|
|
|
|
func (f *fakeInboxWhatsAppService) FetchHealthStatus(_ context.Context, _ *channelmodel.ChannelWhatsApp) (map[string]interface{}, error) {
|
|
return f.healthPayload, f.healthErr
|
|
}
|
|
|
|
func (f *fakeInboxWhatsAppService) SetupWebhook(_ context.Context, _ *channelmodel.ChannelWhatsApp, webhookURL string) error {
|
|
f.webhookURL = webhookURL
|
|
return f.webhookErr
|
|
}
|
|
|
|
func (f *fakeInboxWhatsAppService) SetupWebhookFields(_ context.Context, _ *channelmodel.ChannelWhatsApp, webhookURL string, fields []string) error {
|
|
f.webhookURL = webhookURL
|
|
f.webhookFields = fields
|
|
return f.webhookErr
|
|
}
|
|
|
|
func (f *fakeInboxWhatsAppService) UpdateCallingStatus(_ context.Context, _ *channelmodel.ChannelWhatsApp, status string) error {
|
|
f.callingStatus = status
|
|
return f.callingErr
|
|
}
|
|
|
|
// createInboxTestPrereqs creates prerequisite Account and Inbox for service tests.
|
|
func createInboxTestPrereqs(t *testing.T, db *gorm.DB, channelType string) (*model.Account, *model.Inbox) {
|
|
t.Helper()
|
|
|
|
account := &model.Account{Name: "InboxSvcTestOrg", Locale: "en", Active: true}
|
|
require.NoError(t, db.Create(account).Error)
|
|
|
|
inbox := &model.Inbox{
|
|
AccountID: account.ID,
|
|
Name: fmt.Sprintf("SvcTest%s", channelType),
|
|
ChannelType: channelType,
|
|
ChannelID: 1,
|
|
}
|
|
require.NoError(t, db.Create(inbox).Error)
|
|
|
|
return account, inbox
|
|
}
|
|
|
|
func ptrUint(v uint) *uint { return &v }
|
|
|
|
func TestInboxService_ListByAccountAndUser_OnlyAssignedInboxes(t *testing.T) {
|
|
svc, db := setupInboxServiceTest(t)
|
|
require.NoError(t, db.AutoMigrate(&model.User{}, &model.InboxMember{}))
|
|
account, assigned := createInboxTestPrereqs(t, db, "web_widget")
|
|
hidden := &model.Inbox{AccountID: account.ID, Name: "Hidden", ChannelType: "api", ChannelID: 2}
|
|
require.NoError(t, db.Create(hidden).Error)
|
|
user := &model.User{Name: "Scoped Agent", Email: "service-scoped-agent@example.com"}
|
|
require.NoError(t, db.Create(user).Error)
|
|
require.NoError(t, db.Create(&model.InboxMember{InboxID: assigned.ID, UserID: user.ID}).Error)
|
|
|
|
inboxes, total, err := svc.ListByAccountAndUser(context.Background(), account.ID, user.ID, 0, 25)
|
|
|
|
require.NoError(t, err)
|
|
assert.Equal(t, int64(1), total)
|
|
require.Len(t, inboxes, 1)
|
|
assert.Equal(t, assigned.ID, inboxes[0].ID)
|
|
}
|
|
|
|
func createWhatsAppInboxTestPrereqs(t *testing.T, db *gorm.DB, provider string) (*model.Account, *model.Inbox, *channelmodel.ChannelWhatsApp) {
|
|
t.Helper()
|
|
account, inbox := createInboxTestPrereqs(t, db, "whatsapp")
|
|
channel := &channelmodel.ChannelWhatsApp{
|
|
AccountID: account.ID,
|
|
InboxID: inbox.ID,
|
|
PhoneNumber: "+1555010000",
|
|
PhoneNumberID: "phone-123",
|
|
BusinessAccountID: "waba-456",
|
|
AccessToken: "token-789",
|
|
Provider: provider,
|
|
}
|
|
require.NoError(t, db.Create(channel).Error)
|
|
return account, inbox, channel
|
|
}
|
|
|
|
// createTestAgentBot creates an AgentBot for the given account.
|
|
func createTestAgentBot(t *testing.T, db *gorm.DB, accountID uint, suffix string) *model.AgentBot {
|
|
t.Helper()
|
|
|
|
bot := &model.AgentBot{
|
|
AccountID: &accountID,
|
|
Name: fmt.Sprintf("SvcBot-%s", suffix),
|
|
BotType: "default",
|
|
Secret: fmt.Sprintf("secret-svc-%s", suffix),
|
|
AccessToken: fmt.Sprintf("token-svc-%s", suffix),
|
|
}
|
|
require.NoError(t, db.Create(bot).Error)
|
|
return bot
|
|
}
|
|
|
|
func TestInboxService_Create_AllowsUnlimitedInboxLimit(t *testing.T) {
|
|
svc, db := setupInboxServiceTest(t)
|
|
account := &model.Account{Name: "Unlimited Inboxes", Locale: "en", Active: true, InboxLimit: 0}
|
|
require.NoError(t, db.Create(account).Error)
|
|
require.NoError(t, db.Create(&model.Inbox{AccountID: account.ID, Name: "Existing", ChannelType: "api"}).Error)
|
|
|
|
inbox, err := svc.Create(context.Background(), account.ID, CreateInboxRequest{
|
|
Name: "New API",
|
|
ChannelType: "api",
|
|
})
|
|
require.NoError(t, err)
|
|
require.NotZero(t, inbox.ID)
|
|
}
|
|
|
|
func TestInboxService_CreateRejectsAtAccountInboxLimit(t *testing.T) {
|
|
svc, db := setupInboxServiceTest(t)
|
|
account := &model.Account{Name: "Limited Inboxes", Locale: "en", Active: true, InboxLimit: 1}
|
|
require.NoError(t, db.Create(account).Error)
|
|
require.NoError(t, db.Create(&model.Inbox{AccountID: account.ID, Name: "Existing", ChannelType: "api"}).Error)
|
|
|
|
inbox, err := svc.Create(context.Background(), account.ID, CreateInboxRequest{
|
|
Name: "Blocked API",
|
|
ChannelType: "api",
|
|
})
|
|
|
|
require.ErrorIs(t, err, ErrInboxLimitExceeded)
|
|
require.Nil(t, inbox)
|
|
var count int64
|
|
require.NoError(t, db.Model(&model.Inbox{}).Where("account_id = ?", account.ID).Count(&count).Error)
|
|
assert.Equal(t, int64(1), count)
|
|
}
|
|
|
|
func TestInboxService_CreateAllowsBelowAccountInboxLimit(t *testing.T) {
|
|
svc, db := setupInboxServiceTest(t)
|
|
account := &model.Account{Name: "Below Limit", Locale: "en", Active: true, InboxLimit: 2}
|
|
require.NoError(t, db.Create(account).Error)
|
|
require.NoError(t, db.Create(&model.Inbox{AccountID: account.ID, Name: "Existing", ChannelType: "api"}).Error)
|
|
|
|
inbox, err := svc.Create(context.Background(), account.ID, CreateInboxRequest{
|
|
Name: "Allowed API",
|
|
ChannelType: "api",
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
require.NotZero(t, inbox.ID)
|
|
var count int64
|
|
require.NoError(t, db.Model(&model.Inbox{}).Where("account_id = ?", account.ID).Count(&count).Error)
|
|
assert.Equal(t, int64(2), count)
|
|
}
|
|
|
|
func TestInboxService_CreateAPIInboxPersistsChannelAPI(t *testing.T) {
|
|
svc, db := setupInboxServiceTest(t)
|
|
account := &model.Account{Name: "API Channel Account", Locale: "en", Active: true, InboxLimit: 0}
|
|
require.NoError(t, db.Create(account).Error)
|
|
|
|
inbox, err := svc.Create(context.Background(), account.ID, CreateInboxRequest{
|
|
Name: "API Persist",
|
|
ChannelType: "api",
|
|
Channel: map[string]any{
|
|
"identifier": "api-persist-inbox",
|
|
"hmac_token": "api-persist-secret",
|
|
"hmac_mandatory": true,
|
|
"webhook_url": "https://example.test/hook",
|
|
"additional_attributes": map[string]any{
|
|
"agent_reply_time_window": 30,
|
|
},
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
var channelAPI channelmodel.ChannelAPI
|
|
require.NoError(t, db.Where("inbox_id = ?", inbox.ID).First(&channelAPI).Error)
|
|
assert.Equal(t, inbox.Secret, channelAPI.Secret)
|
|
assert.Equal(t, "api-persist-inbox", channelAPI.Identifier)
|
|
assert.Equal(t, "api-persist-secret", channelAPI.HMACToken)
|
|
assert.True(t, channelAPI.HMACMandatory)
|
|
assert.Equal(t, "https://example.test/hook", channelAPI.WebhookURL)
|
|
assert.Equal(t, channelAPI.ID, inbox.ChannelID)
|
|
var attrs map[string]any
|
|
require.NoError(t, json.Unmarshal(channelAPI.AdditionalAttributes, &attrs))
|
|
assert.Equal(t, float64(30), attrs["agent_reply_time_window"])
|
|
}
|
|
|
|
func TestInboxService_UpdateAPIInboxPersistsChannelAPI(t *testing.T) {
|
|
svc, db := setupInboxServiceTest(t)
|
|
account := &model.Account{Name: "API Channel Update", Locale: "en", Active: true, InboxLimit: 0}
|
|
require.NoError(t, db.Create(account).Error)
|
|
inbox, err := svc.Create(context.Background(), account.ID, CreateInboxRequest{
|
|
Name: "API Update",
|
|
ChannelType: "api",
|
|
Channel: map[string]any{
|
|
"identifier": "api-update-inbox",
|
|
"hmac_token": "api-update-secret",
|
|
"hmac_mandatory": false,
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
updated, err := svc.Update(context.Background(), account.ID, inbox.ID, UpdateInboxRequest{Channel: map[string]any{
|
|
"hmac_mandatory": true,
|
|
"webhook_url": "https://example.test/updated-hook",
|
|
"additional_attributes": map[string]any{
|
|
"agent_reply_time_window": 45,
|
|
},
|
|
}})
|
|
require.NoError(t, err)
|
|
|
|
var channelAPI channelmodel.ChannelAPI
|
|
require.NoError(t, db.Where("inbox_id = ?", updated.ID).First(&channelAPI).Error)
|
|
assert.Equal(t, updated.Secret, channelAPI.Secret)
|
|
assert.Equal(t, "api-update-inbox", channelAPI.Identifier)
|
|
assert.Equal(t, "api-update-secret", channelAPI.HMACToken)
|
|
assert.True(t, channelAPI.HMACMandatory)
|
|
assert.Equal(t, "https://example.test/updated-hook", channelAPI.WebhookURL)
|
|
var attrs map[string]any
|
|
require.NoError(t, json.Unmarshal(channelAPI.AdditionalAttributes, &attrs))
|
|
assert.Equal(t, float64(45), attrs["agent_reply_time_window"])
|
|
}
|
|
|
|
func TestInboxService_APIInboxRejectsInvalidAgentReplyTimeWindow(t *testing.T) {
|
|
svc, db := setupInboxServiceTest(t)
|
|
account := &model.Account{Name: "API Window Validation", Locale: "en", Active: true, InboxLimit: 0}
|
|
require.NoError(t, db.Create(account).Error)
|
|
|
|
created, err := svc.Create(context.Background(), account.ID, CreateInboxRequest{
|
|
Name: "Invalid Window",
|
|
ChannelType: "api",
|
|
Channel: map[string]any{
|
|
"additional_attributes": map[string]any{"agent_reply_time_window": 0},
|
|
},
|
|
})
|
|
require.Error(t, err)
|
|
assert.Nil(t, created)
|
|
assert.Contains(t, err.Error(), "agent_reply_time_window must be greater than 0")
|
|
|
|
inbox, err := svc.Create(context.Background(), account.ID, CreateInboxRequest{
|
|
Name: "Valid Window",
|
|
ChannelType: "api",
|
|
Channel: map[string]any{
|
|
"additional_attributes": map[string]any{"agent_reply_time_window": 12},
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
updated, err := svc.Update(context.Background(), account.ID, inbox.ID, UpdateInboxRequest{Channel: map[string]any{
|
|
"additional_attributes": map[string]any{"agent_reply_time_window": "0"},
|
|
}})
|
|
require.Error(t, err)
|
|
assert.Nil(t, updated)
|
|
assert.Contains(t, err.Error(), "agent_reply_time_window must be greater than 0")
|
|
}
|
|
|
|
func TestInboxService_CreateShangwutongPersistsSecretsOutsideInboxConfig(t *testing.T) {
|
|
svc, db := setupInboxServiceTest(t)
|
|
account := &model.Account{Name: "SWT Account", Locale: "en", Active: true}
|
|
require.NoError(t, db.Create(account).Error)
|
|
inbox, err := svc.Create(context.Background(), account.ID, CreateInboxRequest{
|
|
Name: "商务通站点", ChannelType: "shangwutong",
|
|
Channel: map[string]any{
|
|
"session_id": "BYT99917999", "username": "agent", "password": "plain-secret",
|
|
"desired_presence": "online", "webhook_url": "http://connector:9100/webhooks/gochat/v1",
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "{}", inbox.ChannelConfig)
|
|
assert.Equal(t, "shangwutong", inbox.ChannelType)
|
|
var channelAPI channelmodel.ChannelAPI
|
|
require.NoError(t, db.Where("inbox_id = ?", inbox.ID).First(&channelAPI).Error)
|
|
assert.True(t, channelAPI.HMACMandatory)
|
|
assert.NotEmpty(t, channelAPI.HMACToken)
|
|
assert.NotEmpty(t, channelAPI.Secret)
|
|
var config model.ChannelShangwutongConfig
|
|
require.NoError(t, db.Where("inbox_id = ?", inbox.ID).First(&config).Error)
|
|
assert.Equal(t, "plain-secret", config.Password)
|
|
assert.Equal(t, int64(1), config.ConfigVersion)
|
|
}
|
|
|
|
func TestInboxService_CreateShangwutongEnsuresContactAttributeDefinitions(t *testing.T) {
|
|
svc, db := setupInboxServiceTest(t)
|
|
account := &model.Account{Name: "SWT attributes", Locale: "en", Active: true}
|
|
require.NoError(t, db.Create(account).Error)
|
|
require.NoError(t, db.Create(&model.CustomAttributeDefinition{
|
|
AccountID: account.ID, AttributeName: "swt_ip", AttributeDisplayName: "保留现有名称",
|
|
AttributeType: "number", AttributeModel: "contact_attribute", Description: "保留现有说明",
|
|
}).Error)
|
|
|
|
create := func(name, sessionID, username string) {
|
|
t.Helper()
|
|
_, err := svc.Create(context.Background(), account.ID, CreateInboxRequest{
|
|
Name: name, ChannelType: "shangwutong", Channel: map[string]any{
|
|
"session_id": sessionID, "username": username, "password": "secret",
|
|
"desired_presence": "online", "webhook_url": "http://connector:9100/webhooks/gochat/v1",
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
}
|
|
create("商务通一", "BYT99917999", "agent1")
|
|
create("商务通二", "BYT99917998", "agent2")
|
|
|
|
var definitions []model.CustomAttributeDefinition
|
|
require.NoError(t, db.Where("account_id = ? AND attribute_model = ? AND deleted_at IS NULL", account.ID, "contact_attribute").Order("attribute_name").Find(&definitions).Error)
|
|
require.Len(t, definitions, 16)
|
|
|
|
want := []string{
|
|
"swt_browser", "swt_browser_version", "swt_device", "swt_environment_version",
|
|
"swt_ip", "swt_ip_location", "swt_language", "swt_os", "swt_profile_channel",
|
|
"swt_query_title", "swt_query_word", "swt_resolution", "swt_site_id",
|
|
"swt_timezone", "swt_traffic_source", "swt_user_agent",
|
|
}
|
|
for index, definition := range definitions {
|
|
require.Equal(t, want[index], definition.AttributeName)
|
|
}
|
|
require.Equal(t, "保留现有名称", definitions[4].AttributeDisplayName)
|
|
require.Equal(t, "number", definitions[4].AttributeType)
|
|
require.Equal(t, "保留现有说明", definitions[4].Description)
|
|
|
|
var legacySourceDefinitions int64
|
|
require.NoError(t, db.Model(&model.CustomAttributeDefinition{}).
|
|
Where("account_id = ? AND attribute_name LIKE ?", account.ID, "swt_source_%").
|
|
Count(&legacySourceDefinitions).Error)
|
|
require.Zero(t, legacySourceDefinitions)
|
|
}
|
|
|
|
func TestInboxService_CreateShangwutongRollsBackWhenAttributeInitializationFails(t *testing.T) {
|
|
svc, db := setupInboxServiceTest(t)
|
|
account := &model.Account{Name: "SWT attribute rollback", Locale: "en", Active: true}
|
|
require.NoError(t, db.Create(account).Error)
|
|
require.NoError(t, db.Exec(`
|
|
CREATE TRIGGER fail_swt_attribute_definition
|
|
BEFORE INSERT ON custom_attribute_definitions
|
|
WHEN NEW.attribute_name = 'swt_ip_location'
|
|
BEGIN
|
|
SELECT RAISE(FAIL, 'forced attribute initialization failure');
|
|
END;
|
|
`).Error)
|
|
|
|
inbox, err := svc.Create(context.Background(), account.ID, CreateInboxRequest{
|
|
Name: "商务通回滚", ChannelType: "shangwutong", Channel: map[string]any{
|
|
"session_id": "BYT99917999", "username": "agent", "password": "secret",
|
|
"desired_presence": "online", "webhook_url": "http://connector:9100/webhooks/gochat/v1",
|
|
},
|
|
})
|
|
require.ErrorContains(t, err, "forced attribute initialization failure")
|
|
require.Nil(t, inbox)
|
|
|
|
for _, target := range []any{
|
|
&model.Inbox{}, &channelmodel.ChannelAPI{}, &model.ChannelShangwutongConfig{}, &model.CustomAttributeDefinition{},
|
|
} {
|
|
var count int64
|
|
require.NoError(t, db.Model(target).Count(&count).Error)
|
|
require.Zero(t, count)
|
|
}
|
|
}
|
|
|
|
func TestInboxService_ShangwutongLifecycleJobsAreTransactionalAndKeepRotationDeleteSnapshots(t *testing.T) {
|
|
svc, db := setupInboxServiceTest(t)
|
|
wp := worker.NewWorkerPool(db)
|
|
svc.SetWorkerPool(wp)
|
|
account := &model.Account{Name: "Account", Active: true}
|
|
require.NoError(t, db.Create(account).Error)
|
|
inbox, err := svc.Create(context.Background(), account.ID, CreateInboxRequest{
|
|
Name: "商务通", ChannelType: "shangwutong", Channel: map[string]any{
|
|
"session_id": "LZA69557093", "username": "operator", "password": "secret-password",
|
|
"desired_presence": "online", "webhook_url": "http://connector:9100/webhooks/gochat/v1",
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
var channelAPI channelmodel.ChannelAPI
|
|
require.NoError(t, db.Where("inbox_id = ?", inbox.ID).First(&channelAPI).Error)
|
|
oldSecret := channelAPI.Secret
|
|
_, err = svc.ResetSecret(context.Background(), account.ID, inbox.ID)
|
|
require.NoError(t, err)
|
|
require.NoError(t, svc.DeleteByAccount(context.Background(), account.ID, inbox.ID))
|
|
var jobs []model.BackgroundJob
|
|
require.NoError(t, db.Where("job_type = ?", TaskTypeShangwutongWebhookDelivery).Order("id ASC").Find(&jobs).Error)
|
|
require.Len(t, jobs, 3)
|
|
var created, rotated, deleted shangwutongWebhookDeliveryJob
|
|
require.NoError(t, json.Unmarshal(jobs[0].Payload, &created))
|
|
require.NoError(t, json.Unmarshal(jobs[1].Payload, &rotated))
|
|
require.NoError(t, json.Unmarshal(jobs[2].Payload, &deleted))
|
|
require.Equal(t, "inbox_created", created.Event)
|
|
require.Empty(t, created.SigningSecret)
|
|
require.Equal(t, "inbox_updated", rotated.Event)
|
|
require.Equal(t, oldSecret, rotated.SigningSecret)
|
|
require.Equal(t, "inbox_deleted", deleted.Event)
|
|
require.True(t, deleted.Tombstone)
|
|
require.Equal(t, "http://connector:9100/webhooks/gochat/v1", deleted.WebhookURL)
|
|
require.NotEmpty(t, deleted.SigningSecret)
|
|
var activeConfigCount int64
|
|
require.NoError(t, db.Model(&model.ChannelShangwutongConfig{}).Where("inbox_id = ?", inbox.ID).Count(&activeConfigCount).Error)
|
|
require.Zero(t, activeConfigCount)
|
|
replacement, err := svc.Create(context.Background(), account.ID, CreateInboxRequest{
|
|
Name: "商务通替换", ChannelType: "shangwutong", Channel: map[string]any{
|
|
"session_id": "LZA69557093", "username": "operator", "password": "replacement-password",
|
|
"desired_presence": "online", "webhook_url": "http://connector:9100/webhooks/gochat/v1",
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
require.NotEqual(t, inbox.ID, replacement.ID)
|
|
for _, job := range jobs {
|
|
require.NotContains(t, string(job.Payload), "secret-password")
|
|
require.NotContains(t, string(job.Payload), "operator")
|
|
require.NotContains(t, string(job.Payload), "LZA69557093")
|
|
}
|
|
}
|
|
|
|
func TestInboxService_UpdateShangwutongVersionsConnectorFieldsAndKeepsOmittedPassword(t *testing.T) {
|
|
svc, db := setupInboxServiceTest(t)
|
|
account := &model.Account{Name: "SWT Update", Locale: "en", Active: true}
|
|
require.NoError(t, db.Create(account).Error)
|
|
inbox, err := svc.Create(context.Background(), account.ID, CreateInboxRequest{
|
|
Name: "商务通站点", ChannelType: "shangwutong",
|
|
Channel: map[string]any{
|
|
"session_id": "BYT99917999", "username": "agent", "password": "plain-secret",
|
|
"desired_presence": "online", "webhook_url": "http://connector:9100/webhooks/gochat/v1",
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
updated, err := svc.Update(context.Background(), account.ID, inbox.ID, UpdateInboxRequest{
|
|
Name: "只改名称", Channel: map[string]any{"desired_presence": "busy"},
|
|
})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "只改名称", updated.Name)
|
|
var config model.ChannelShangwutongConfig
|
|
require.NoError(t, db.Where("inbox_id = ?", inbox.ID).First(&config).Error)
|
|
assert.Equal(t, "plain-secret", config.Password)
|
|
assert.Equal(t, "busy", config.DesiredPresence)
|
|
assert.Equal(t, int64(2), config.ConfigVersion)
|
|
_, err = svc.Update(context.Background(), account.ID, inbox.ID, UpdateInboxRequest{
|
|
Channel: map[string]any{"username": "other"},
|
|
})
|
|
require.ErrorContains(t, err, "cannot be changed in place")
|
|
}
|
|
|
|
func TestInboxService_SharedShangwutongConnectorURLIsEnforced(t *testing.T) {
|
|
svc, db := setupInboxServiceTest(t)
|
|
account := &model.Account{Name: "SWT URLs", Locale: "en", Active: true}
|
|
require.NoError(t, db.Create(account).Error)
|
|
base := CreateInboxRequest{Name: "商务通一", ChannelType: "shangwutong", Channel: map[string]any{
|
|
"session_id": "BYT99917999", "username": "agent1", "password": "secret",
|
|
"desired_presence": "online", "webhook_url": "http://connector:9100/webhooks/gochat/v1",
|
|
}}
|
|
_, err := svc.Create(context.Background(), account.ID, base)
|
|
require.NoError(t, err)
|
|
base.Name = "商务通二"
|
|
base.Channel["session_id"] = "BYT99917998"
|
|
base.Channel["username"] = "agent2"
|
|
base.Channel["webhook_url"] = "http://other:9100/webhooks/gochat/v1"
|
|
_, err = svc.Create(context.Background(), account.ID, base)
|
|
require.ErrorContains(t, err, "same webhook_url")
|
|
}
|
|
|
|
func TestInboxService_DeletedShangwutongIdentityCanBeRecreated(t *testing.T) {
|
|
svc, db := setupInboxServiceTest(t)
|
|
account := &model.Account{Name: "SWT identity reuse", Locale: "en", Active: true}
|
|
require.NoError(t, db.Create(account).Error)
|
|
request := CreateInboxRequest{Name: "商务通旧收件箱", Channel: map[string]any{
|
|
"type": "shangwutong", "session_id": "BYT99917999", "username": "agent", "password": "secret",
|
|
"desired_presence": "offline", "webhook_url": "http://connector:9100/webhooks/gochat/v1",
|
|
}}
|
|
first, err := svc.Create(context.Background(), account.ID, request)
|
|
require.NoError(t, err)
|
|
require.NoError(t, svc.DeleteByAccount(context.Background(), account.ID, first.ID))
|
|
request.Name = "商务通新收件箱"
|
|
second, err := svc.Create(context.Background(), account.ID, request)
|
|
require.NoError(t, err)
|
|
assert.NotEqual(t, first.ID, second.ID)
|
|
}
|
|
|
|
func TestInboxService_ResetSecretRegeneratesAPIWebhookSecret(t *testing.T) {
|
|
svc, db := setupInboxServiceTest(t)
|
|
account := &model.Account{Name: "API Secret Reset", Locale: "en", Active: true, InboxLimit: 0}
|
|
require.NoError(t, db.Create(account).Error)
|
|
|
|
inbox, err := svc.Create(context.Background(), account.ID, CreateInboxRequest{
|
|
Name: "API Secret Reset",
|
|
ChannelType: "api",
|
|
Channel: map[string]any{
|
|
"hmac_token": "public-hmac-token",
|
|
"webhook_url": "https://example.test/reset-hook",
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
var before channelmodel.ChannelAPI
|
|
require.NoError(t, db.Where("inbox_id = ?", inbox.ID).First(&before).Error)
|
|
require.NotEmpty(t, before.Secret)
|
|
|
|
reset, err := svc.ResetSecret(context.Background(), account.ID, inbox.ID)
|
|
require.NoError(t, err)
|
|
|
|
var after channelmodel.ChannelAPI
|
|
require.NoError(t, db.Where("inbox_id = ?", inbox.ID).First(&after).Error)
|
|
assert.NotEqual(t, before.Secret, after.Secret)
|
|
assert.Equal(t, after.Secret, reset.Secret)
|
|
assert.Equal(t, before.HMACToken, after.HMACToken)
|
|
assert.Equal(t, "public-hmac-token", after.HMACToken)
|
|
}
|
|
|
|
// ========================================
|
|
// SetAgentBot service tests
|
|
// ========================================
|
|
|
|
func TestInboxService_SetAgentBot_AssignBot(t *testing.T) {
|
|
svc, db := setupInboxServiceTest(t)
|
|
account, inbox := createInboxTestPrereqs(t, db, "web_widget")
|
|
bot := createTestAgentBot(t, db, account.ID, "assign")
|
|
|
|
binding, err := svc.SetAgentBot(context.Background(), account.ID, inbox.ID, SetAgentBotRequest{
|
|
AgentBotID: ptrUint(bot.ID),
|
|
})
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, binding)
|
|
assert.Equal(t, bot.ID, binding.AgentBotID)
|
|
assert.Equal(t, inbox.ID, binding.InboxID)
|
|
assert.Equal(t, model.AgentBotInboxActive, binding.Status)
|
|
}
|
|
|
|
func TestInboxService_SetAgentBot_RemoveBot(t *testing.T) {
|
|
svc, db := setupInboxServiceTest(t)
|
|
account, inbox := createInboxTestPrereqs(t, db, "web_widget")
|
|
bot := createTestAgentBot(t, db, account.ID, "remove")
|
|
|
|
// First assign a bot
|
|
binding, err := svc.SetAgentBot(context.Background(), account.ID, inbox.ID, SetAgentBotRequest{
|
|
AgentBotID: ptrUint(bot.ID),
|
|
})
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, binding)
|
|
|
|
// Now remove it (missing/null agent_bot)
|
|
result, err := svc.SetAgentBot(context.Background(), account.ID, inbox.ID, SetAgentBotRequest{})
|
|
require.NoError(t, err)
|
|
assert.Nil(t, result, "removing bot should return nil binding")
|
|
|
|
bindings, err := repository.NewAgentBotInboxRepo(db).FindByInboxID(context.Background(), inbox.ID)
|
|
require.NoError(t, err)
|
|
assert.Empty(t, bindings, "Chatwoot destroys the agent_bot_inbox row on disconnect")
|
|
}
|
|
|
|
func TestInboxService_SetAgentBot_InboxNotFound(t *testing.T) {
|
|
svc, _ := setupInboxServiceTest(t)
|
|
|
|
binding, err := svc.SetAgentBot(context.Background(), 9999, 9999, SetAgentBotRequest{
|
|
AgentBotID: ptrUint(1),
|
|
})
|
|
assert.Error(t, err)
|
|
assert.Nil(t, binding)
|
|
assert.Contains(t, err.Error(), "inbox not found")
|
|
}
|
|
|
|
func TestInboxService_SetAgentBot_BotNotFound(t *testing.T) {
|
|
svc, db := setupInboxServiceTest(t)
|
|
account, inbox := createInboxTestPrereqs(t, db, "web_widget")
|
|
|
|
binding, err := svc.SetAgentBot(context.Background(), account.ID, inbox.ID, SetAgentBotRequest{
|
|
AgentBotID: ptrUint(9999),
|
|
})
|
|
assert.Error(t, err)
|
|
assert.Nil(t, binding)
|
|
assert.Contains(t, err.Error(), "agent bot not found")
|
|
}
|
|
|
|
func TestInboxService_SetAgentBot_ReassignBot(t *testing.T) {
|
|
svc, db := setupInboxServiceTest(t)
|
|
account, inbox := createInboxTestPrereqs(t, db, "web_widget")
|
|
bot1 := createTestAgentBot(t, db, account.ID, "first")
|
|
bot2 := createTestAgentBot(t, db, account.ID, "second")
|
|
|
|
// Assign first bot
|
|
binding1, err := svc.SetAgentBot(context.Background(), account.ID, inbox.ID, SetAgentBotRequest{
|
|
AgentBotID: ptrUint(bot1.ID),
|
|
})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, bot1.ID, binding1.AgentBotID)
|
|
|
|
// Reassign to second bot — Chatwoot updates the inbox's single has_one binding.
|
|
binding2, err := svc.SetAgentBot(context.Background(), account.ID, inbox.ID, SetAgentBotRequest{
|
|
AgentBotID: ptrUint(bot2.ID),
|
|
})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, bot2.ID, binding2.AgentBotID)
|
|
assert.Equal(t, model.AgentBotInboxActive, binding2.Status)
|
|
assert.Equal(t, binding1.ID, binding2.ID)
|
|
}
|
|
|
|
func TestInboxService_SetAgentBot_RecreatesBindingAfterDisconnect(t *testing.T) {
|
|
svc, db := setupInboxServiceTest(t)
|
|
account, inbox := createInboxTestPrereqs(t, db, "web_widget")
|
|
bot := createTestAgentBot(t, db, account.ID, "recreate")
|
|
|
|
// Assign bot
|
|
_, err := svc.SetAgentBot(context.Background(), account.ID, inbox.ID, SetAgentBotRequest{
|
|
AgentBotID: ptrUint(bot.ID),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Remove bot (destroy binding)
|
|
_, err = svc.SetAgentBot(context.Background(), account.ID, inbox.ID, SetAgentBotRequest{})
|
|
require.NoError(t, err)
|
|
|
|
// Re-assign same bot — should create a fresh binding.
|
|
binding2, err := svc.SetAgentBot(context.Background(), account.ID, inbox.ID, SetAgentBotRequest{
|
|
AgentBotID: ptrUint(bot.ID),
|
|
})
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, binding2)
|
|
assert.Equal(t, bot.ID, binding2.AgentBotID)
|
|
assert.Equal(t, model.AgentBotInboxActive, binding2.Status)
|
|
}
|
|
|
|
// ========================================
|
|
// Health service tests
|
|
// ========================================
|
|
|
|
func TestInboxService_Health_NonWhatsAppInboxRejected(t *testing.T) {
|
|
svc, db := setupInboxServiceTest(t)
|
|
account, inbox := createInboxTestPrereqs(t, db, "api")
|
|
|
|
result, err := svc.Health(context.Background(), account.ID, inbox.ID)
|
|
require.ErrorIs(t, err, ErrInboxHealthWhatsAppCloudOnly)
|
|
assert.Nil(t, result)
|
|
}
|
|
|
|
func TestInboxService_Health_ShangwutongReturnsOnlyRuntimeState(t *testing.T) {
|
|
svc, db := setupInboxServiceTest(t)
|
|
account, inbox := createInboxTestPrereqs(t, db, "shangwutong")
|
|
rejected := "auth_failed"
|
|
require.NoError(t, db.Create(&model.ChannelShangwutongConfig{
|
|
InboxID: inbox.ID, SessionID: "LZA69557093", Username: "operator", Password: "plain-secret",
|
|
DesiredPresence: "busy", ConfigVersion: 7, ActualPresence: "online",
|
|
ConnectionStatus: "connected", CredentialStatus: "rejected", LastErrorCode: &rejected,
|
|
}).Error)
|
|
|
|
result, err := svc.Health(context.Background(), account.ID, inbox.ID)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, int64(7), result["config_version"])
|
|
assert.Equal(t, "busy", result["desired_presence"])
|
|
assert.Equal(t, "connected", result["connection_status"])
|
|
assert.Equal(t, "rejected", result["credential_status"])
|
|
assert.NotContains(t, result, "password")
|
|
assert.NotContains(t, result, "session_id")
|
|
assert.NotContains(t, result, "username")
|
|
}
|
|
|
|
func TestInboxService_Health_WhatsAppCloudReturnsPayload(t *testing.T) {
|
|
svc, db := setupInboxServiceTest(t)
|
|
account, inbox, _ := createWhatsAppInboxTestPrereqs(t, db, "whatsapp_cloud")
|
|
svc.whatsappService = &fakeInboxWhatsAppService{healthPayload: map[string]interface{}{
|
|
"id": "phone-123",
|
|
"quality_rating": "GREEN",
|
|
"expected_webhook_url": "https://app.test/webhooks/whatsapp/+1555010000",
|
|
"business_id": "waba-456",
|
|
}}
|
|
|
|
result, err := svc.Health(context.Background(), account.ID, inbox.ID)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "phone-123", result["id"])
|
|
assert.Equal(t, "GREEN", result["quality_rating"])
|
|
assert.NotContains(t, result, "healthy")
|
|
assert.NotContains(t, result, "status")
|
|
}
|
|
|
|
func TestInboxService_Health_NonCloudWhatsAppRejected(t *testing.T) {
|
|
svc, db := setupInboxServiceTest(t)
|
|
account, inbox, _ := createWhatsAppInboxTestPrereqs(t, db, "360dialog")
|
|
svc.whatsappService = &fakeInboxWhatsAppService{}
|
|
|
|
result, err := svc.Health(context.Background(), account.ID, inbox.ID)
|
|
require.ErrorIs(t, err, ErrInboxHealthWhatsAppCloudOnly)
|
|
assert.Nil(t, result)
|
|
}
|
|
|
|
func TestInboxService_Health_InboxNotFound(t *testing.T) {
|
|
svc, _ := setupInboxServiceTest(t)
|
|
|
|
result, err := svc.Health(context.Background(), 9999, 9999)
|
|
assert.Error(t, err)
|
|
assert.Nil(t, result)
|
|
assert.Contains(t, err.Error(), "inbox not found")
|
|
}
|
|
|
|
// ========================================
|
|
// SyncTemplates service tests
|
|
// ========================================
|
|
|
|
func TestInboxService_SyncTemplates_NonWhatsAppInbox(t *testing.T) {
|
|
svc, db := setupInboxServiceTest(t)
|
|
account, inbox := createInboxTestPrereqs(t, db, "api")
|
|
|
|
err := svc.SyncTemplates(context.Background(), account.ID, inbox.ID)
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "Template sync is only available for WhatsApp channels")
|
|
}
|
|
|
|
func TestInboxService_SyncTemplates_InboxNotFound(t *testing.T) {
|
|
svc, _ := setupInboxServiceTest(t)
|
|
|
|
err := svc.SyncTemplates(context.Background(), 9999, 9999)
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "inbox not found")
|
|
}
|
|
|
|
func TestInboxService_SyncTemplates_WhatsAppQueuesWorkerJob(t *testing.T) {
|
|
svc, db := setupInboxServiceTest(t)
|
|
account, inbox, channel := createWhatsAppInboxTestPrereqs(t, db, "whatsapp_cloud")
|
|
wp := worker.NewWorkerPool(db)
|
|
svc.SetWorkerPool(wp)
|
|
svc.whatsappService = &fakeInboxWhatsAppService{}
|
|
|
|
err := svc.SyncTemplates(context.Background(), account.ID, inbox.ID)
|
|
require.NoError(t, err)
|
|
|
|
var job model.BackgroundJob
|
|
require.NoError(t, db.Where("job_type = ? AND queue = ?", TaskTypeInboxSyncTemplates, "low").First(&job).Error)
|
|
var payload inboxTemplateSyncJob
|
|
require.NoError(t, json.Unmarshal(job.Payload, &payload))
|
|
assert.Equal(t, account.ID, payload.AccountID)
|
|
assert.Equal(t, inbox.ID, payload.InboxID)
|
|
assert.Equal(t, channel.ID, payload.ChannelID)
|
|
}
|
|
|
|
func TestInboxService_SyncTemplatesWorkerFetchesTemplates(t *testing.T) {
|
|
svc, db := setupInboxServiceTest(t)
|
|
account, inbox, _ := createWhatsAppInboxTestPrereqs(t, db, "whatsapp_cloud")
|
|
fake := &fakeInboxWhatsAppService{templates: []interface{}{map[string]interface{}{"name": "hello_world"}}}
|
|
svc.whatsappService = fake
|
|
wp := worker.NewWorkerPool(db)
|
|
svc.SetWorkerPool(wp)
|
|
RegisterInboxTemplateSyncJobs(wp, svc)
|
|
|
|
require.NoError(t, svc.SyncTemplates(context.Background(), account.ID, inbox.ID))
|
|
processed, err := wp.ProcessOne(context.Background())
|
|
require.NoError(t, err)
|
|
assert.True(t, processed)
|
|
assert.Equal(t, 1, fake.fetchCalls)
|
|
}
|
|
|
|
// ========================================
|
|
// RegisterWebhook service tests
|
|
// ========================================
|
|
|
|
func TestInboxService_RegisterWebhook_NonWhatsAppInboxRejected(t *testing.T) {
|
|
svc, db := setupInboxServiceTest(t)
|
|
account, inbox := createInboxTestPrereqs(t, db, "web_widget")
|
|
|
|
err := svc.RegisterWebhook(context.Background(), account.ID, inbox.ID, RegisterWebhookRequest{
|
|
URL: "https://example.com/webhook",
|
|
})
|
|
require.ErrorIs(t, err, ErrInboxHealthWhatsAppCloudOnly)
|
|
}
|
|
|
|
func TestInboxService_RegisterWebhook_InboxNotFound(t *testing.T) {
|
|
svc, _ := setupInboxServiceTest(t)
|
|
|
|
err := svc.RegisterWebhook(context.Background(), 9999, 9999, RegisterWebhookRequest{
|
|
URL: "https://example.com/webhook",
|
|
})
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "inbox not found")
|
|
}
|
|
|
|
func TestInboxService_RegisterWebhook_WhatsAppCloudUsesExpectedCallbackURL(t *testing.T) {
|
|
svc, db := setupInboxServiceTest(t)
|
|
account, inbox, _ := createWhatsAppInboxTestPrereqs(t, db, "whatsapp_cloud")
|
|
fake := &fakeInboxWhatsAppService{}
|
|
svc.whatsappService = fake
|
|
t.Setenv("FRONTEND_URL", "https://app.example.com/")
|
|
|
|
err := svc.RegisterWebhook(context.Background(), account.ID, inbox.ID, RegisterWebhookRequest{})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "https://app.example.com/webhooks/whatsapp/+1555010000", fake.webhookURL)
|
|
}
|
|
|
|
func TestInboxService_RegisterWebhook_NonCloudWhatsAppRejected(t *testing.T) {
|
|
svc, db := setupInboxServiceTest(t)
|
|
account, inbox, _ := createWhatsAppInboxTestPrereqs(t, db, "360dialog")
|
|
svc.whatsappService = &fakeInboxWhatsAppService{}
|
|
|
|
err := svc.RegisterWebhook(context.Background(), account.ID, inbox.ID, RegisterWebhookRequest{})
|
|
require.ErrorIs(t, err, ErrInboxHealthWhatsAppCloudOnly)
|
|
}
|
|
|
|
// ========================================
|
|
// WhatsApp calling service tests
|
|
// ========================================
|
|
|
|
func TestInboxService_EnableWhatsAppCalling_SetsProviderConfigAndWebhook(t *testing.T) {
|
|
svc, db := setupInboxServiceTest(t)
|
|
account, inbox, channel := createWhatsAppInboxTestPrereqs(t, db, "whatsapp_cloud")
|
|
account.FeatureFlags = `{"channel_voice":true}`
|
|
require.NoError(t, db.Save(account).Error)
|
|
fake := &fakeInboxWhatsAppService{}
|
|
svc.whatsappService = fake
|
|
t.Setenv("FRONTEND_URL", "https://app.example.test")
|
|
|
|
err := svc.EnableWhatsAppCalling(context.Background(), account.ID, inbox.ID)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "ENABLED", fake.callingStatus)
|
|
assert.Equal(t, "https://app.example.test/webhooks/whatsapp/+1555010000", fake.webhookURL)
|
|
assert.Empty(t, fake.webhookFields)
|
|
|
|
var updated channelmodel.ChannelWhatsApp
|
|
require.NoError(t, db.First(&updated, channel.ID).Error)
|
|
providerConfig := parseJSONMap(updated.ProviderConfig)
|
|
assert.Equal(t, true, providerConfig["calling_enabled"])
|
|
|
|
var updatedInbox model.Inbox
|
|
require.NoError(t, db.First(&updatedInbox, inbox.ID).Error)
|
|
channelConfig := parseJSONMap(updatedInbox.ChannelConfig)
|
|
assert.Equal(t, true, channelConfig["voice_enabled"])
|
|
}
|
|
|
|
func TestInboxService_EnableWhatsAppCalling_RequiresCloudAndFeature(t *testing.T) {
|
|
svc, db := setupInboxServiceTest(t)
|
|
account, inbox, _ := createWhatsAppInboxTestPrereqs(t, db, "360dialog")
|
|
account.FeatureFlags = `{"channel_voice":true}`
|
|
require.NoError(t, db.Save(account).Error)
|
|
svc.whatsappService = &fakeInboxWhatsAppService{}
|
|
|
|
err := svc.EnableWhatsAppCalling(context.Background(), account.ID, inbox.ID)
|
|
require.ErrorIs(t, err, ErrInboxWhatsAppCallingUnsupported)
|
|
|
|
account, inbox, _ = createWhatsAppInboxTestPrereqs(t, db, "whatsapp_cloud")
|
|
err = svc.EnableWhatsAppCalling(context.Background(), account.ID, inbox.ID)
|
|
require.ErrorIs(t, err, ErrInboxWhatsAppCallingFeatureRequired)
|
|
}
|
|
|
|
func TestInboxService_DisableWhatsAppCalling_PersistsFalseAndIgnoresWebhookFailure(t *testing.T) {
|
|
svc, db := setupInboxServiceTest(t)
|
|
account, inbox, channel := createWhatsAppInboxTestPrereqs(t, db, "whatsapp_cloud")
|
|
channel.ProviderConfig = `{"calling_enabled":true,"phone_number_id":"phone-1","business_account_id":"waba-1"}`
|
|
require.NoError(t, db.Save(channel).Error)
|
|
fake := &fakeInboxWhatsAppService{webhookErr: fmt.Errorf("meta unavailable")}
|
|
svc.whatsappService = fake
|
|
t.Setenv("FRONTEND_URL", "https://app.example.test")
|
|
|
|
err := svc.DisableWhatsAppCalling(context.Background(), account.ID, inbox.ID)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, []string{"messages", "smb_message_echoes"}, fake.webhookFields)
|
|
|
|
var updated channelmodel.ChannelWhatsApp
|
|
require.NoError(t, db.First(&updated, channel.ID).Error)
|
|
providerConfig := parseJSONMap(updated.ProviderConfig)
|
|
assert.Equal(t, false, providerConfig["calling_enabled"])
|
|
}
|
|
|
|
func TestInboxService_SetInboundCalls_PersistsVoiceInboxSetting(t *testing.T) {
|
|
svc, db := setupInboxServiceTest(t)
|
|
account, inbox, channel := createWhatsAppInboxTestPrereqs(t, db, "whatsapp_cloud")
|
|
inbox.ChannelConfig = `{"voice_enabled":true,"inbound_calls_enabled":true}`
|
|
require.NoError(t, db.Save(inbox).Error)
|
|
require.NoError(t, svc.SetInboundCalls(context.Background(), account.ID, inbox.ID, false))
|
|
var updated model.Inbox
|
|
require.NoError(t, db.First(&updated, inbox.ID).Error)
|
|
assert.Equal(t, false, parseJSONMap(updated.ChannelConfig)["inbound_calls_enabled"])
|
|
var updatedChannel channelmodel.ChannelWhatsApp
|
|
require.NoError(t, db.First(&updatedChannel, channel.ID).Error)
|
|
assert.Equal(t, false, parseJSONMap(updatedChannel.ProviderConfig)["inbound_calls_enabled"])
|
|
require.NoError(t, svc.SetInboundCalls(context.Background(), account.ID, inbox.ID, true))
|
|
require.NoError(t, db.First(&updated, inbox.ID).Error)
|
|
assert.Equal(t, true, parseJSONMap(updated.ChannelConfig)["inbound_calls_enabled"])
|
|
}
|
|
|
|
func TestInboxService_SetInboundCalls_RejectsUnsupportedInbox(t *testing.T) {
|
|
svc, db := setupInboxServiceTest(t)
|
|
account, inbox := createInboxTestPrereqs(t, db, "web_widget")
|
|
require.ErrorIs(t, svc.SetInboundCalls(context.Background(), account.ID, inbox.ID, true), ErrInboxInboundCallsUnsupported)
|
|
inbox.ChannelType = "whatsapp"
|
|
inbox.ChannelConfig = `{}`
|
|
require.NoError(t, db.Save(inbox).Error)
|
|
require.ErrorIs(t, svc.SetInboundCalls(context.Background(), account.ID, inbox.ID, true), ErrInboxInboundCallsUnsupported)
|
|
}
|