1:1
This commit is contained in:
@@ -34,10 +34,12 @@ func TestInboxHandler_ChatwootSerializerParity(t *testing.T) {
|
||||
_ = sqlDB.Close()
|
||||
}
|
||||
})
|
||||
require.NoError(t, db.AutoMigrate(&model.Account{}, &model.Inbox{}, &model.WorkingHour{}, &channelmodel.ChannelAPI{}))
|
||||
require.NoError(t, db.AutoMigrate(&model.Account{}, &model.Inbox{}, &model.WorkingHour{}, &model.Portal{}, &channelmodel.ChannelAPI{}))
|
||||
|
||||
account := &model.Account{Name: "Inbox Parity", Locale: "en", Active: true}
|
||||
require.NoError(t, db.Create(account).Error)
|
||||
portal := &model.Portal{AccountID: account.ID, Name: "Docs", Slug: "docs"}
|
||||
require.NoError(t, db.Create(portal).Error)
|
||||
widget := &model.Inbox{
|
||||
AccountID: account.ID,
|
||||
Name: "Website",
|
||||
@@ -53,6 +55,7 @@ func TestInboxHandler_ChatwootSerializerParity(t *testing.T) {
|
||||
SenderNameType: "friendly_name",
|
||||
BusinessName: "Example Co",
|
||||
Timezone: "UTC",
|
||||
PortalID: &portal.ID,
|
||||
ChannelConfig: `{"website_token":"web-token","hmac_token":"hmac-token","widget_color":"#1f93ff","website_url":"https://example.com","welcome_title":"Hi","welcome_tagline":"We reply fast","reply_time":"in_a_few_minutes","pre_chat_form_enabled":true,"pre_chat_form_options":{"fields":[{"name":"email"}]},"continuity_via_email":true}`,
|
||||
}
|
||||
require.NoError(t, db.Create(widget).Error)
|
||||
@@ -86,7 +89,11 @@ func TestInboxHandler_ChatwootSerializerParity(t *testing.T) {
|
||||
showData := inboxParityObject(t, show)
|
||||
require.NotContains(t, showData, "payload")
|
||||
require.Equal(t, "Channel::WebWidget", showData["channel_type"])
|
||||
helpCenter := showData["help_center"].(map[string]any)
|
||||
require.Equal(t, "Docs", helpCenter["name"])
|
||||
require.Equal(t, "docs", helpCenter["slug"])
|
||||
require.Equal(t, "web-token", showData["website_token"])
|
||||
require.Equal(t, "friendly", showData["sender_name_type"])
|
||||
require.Equal(t, "hmac-token", showData["hmac_token"])
|
||||
require.Equal(t, "#1f93ff", showData["widget_color"])
|
||||
require.Equal(t, "https://example.com", showData["website_url"])
|
||||
@@ -111,6 +118,140 @@ func TestInboxHandler_ChatwootSerializerParity(t *testing.T) {
|
||||
require.Equal(t, "Your inbox deletion request will be processed in some time.", inboxParityObject(t, destroy)["message"])
|
||||
}
|
||||
|
||||
func TestSerializeInboxIncludesTwitterTweetsEnabled(t *testing.T) {
|
||||
defaultInbox := &model.Inbox{AccountID: 1, Name: "Twitter", ChannelType: "Channel::TwitterProfile"}
|
||||
defaultPayload := serializeInbox(defaultInbox, nil, false)
|
||||
require.Equal(t, "Channel::TwitterProfile", defaultPayload["channel_type"])
|
||||
require.Equal(t, true, defaultPayload["tweets_enabled"])
|
||||
|
||||
disabledInbox := &model.Inbox{AccountID: 1, Name: "Twitter", ChannelType: "twitter", ChannelConfig: `{"tweets_enabled":false}`}
|
||||
disabledPayload := serializeInbox(disabledInbox, nil, false)
|
||||
require.Equal(t, "Channel::TwitterProfile", disabledPayload["channel_type"])
|
||||
require.Equal(t, false, disabledPayload["tweets_enabled"])
|
||||
}
|
||||
|
||||
func TestSerializeInboxEmailForwardingDependsOnInboundMailerDomain(t *testing.T) {
|
||||
inbox := &model.Inbox{AccountID: 1, Name: "Email", ChannelType: "email", ChannelConfig: `{"email":"support@example.com","forward_to_email":"support+forward@example.test"}`}
|
||||
|
||||
t.Setenv("MAILER_INBOUND_EMAIL_DOMAIN", "")
|
||||
disabled := serializeInbox(inbox, nil, false)
|
||||
require.Equal(t, "Channel::Email", disabled["channel_type"])
|
||||
require.Equal(t, false, disabled["forwarding_enabled"])
|
||||
require.NotContains(t, disabled, "forward_to_email")
|
||||
|
||||
t.Setenv("MAILER_INBOUND_EMAIL_DOMAIN", "mail.example.test")
|
||||
enabled := serializeInbox(inbox, nil, false)
|
||||
require.Equal(t, true, enabled["forwarding_enabled"])
|
||||
require.Equal(t, "support+forward@example.test", enabled["forward_to_email"])
|
||||
}
|
||||
|
||||
func TestSerializeInboxTwilioVoiceWebhookURLsRequireConfiguredTwimlApp(t *testing.T) {
|
||||
unconfigured := &model.Inbox{AccountID: 1, Name: "Twilio", ChannelType: "twilio_sms", ChannelConfig: `{"voice_enabled":true,"twiml_app_sid":"","api_key_secret":"","voice_call_webhook_url":"https://voice.example/call","voice_status_webhook_url":"https://voice.example/status"}`}
|
||||
unconfiguredPayload := serializeInbox(unconfigured, nil, true)
|
||||
require.Equal(t, "Channel::TwilioSms", unconfiguredPayload["channel_type"])
|
||||
require.Equal(t, false, unconfiguredPayload["voice_configured"])
|
||||
require.Equal(t, false, unconfiguredPayload["has_api_key_secret"])
|
||||
require.NotContains(t, unconfiguredPayload, "voice_call_webhook_url")
|
||||
require.NotContains(t, unconfiguredPayload, "voice_status_webhook_url")
|
||||
|
||||
configured := &model.Inbox{AccountID: 1, Name: "Twilio", ChannelType: "Channel::TwilioSms", ChannelConfig: `{"voice_enabled":true,"twiml_app_sid":"AP123","api_key_secret":"secret","voice_call_webhook_url":"https://voice.example/call","voice_status_webhook_url":"https://voice.example/status"}`}
|
||||
configuredPayload := serializeInbox(configured, nil, true)
|
||||
require.Equal(t, true, configuredPayload["voice_configured"])
|
||||
require.Equal(t, true, configuredPayload["has_api_key_secret"])
|
||||
require.Equal(t, "https://voice.example/call", configuredPayload["voice_call_webhook_url"])
|
||||
require.Equal(t, "https://voice.example/status", configuredPayload["voice_status_webhook_url"])
|
||||
}
|
||||
|
||||
func TestSerializeInboxUsesChatwootSenderNameTypeValues(t *testing.T) {
|
||||
defaultInbox := &model.Inbox{AccountID: 1, Name: "Default", ChannelType: "web_widget"}
|
||||
require.Equal(t, "friendly", serializeInbox(defaultInbox, nil, false)["sender_name_type"])
|
||||
|
||||
legacyFriendly := &model.Inbox{AccountID: 1, Name: "Legacy Friendly", ChannelType: "web_widget", SenderNameType: "friendly_name"}
|
||||
require.Equal(t, "friendly", serializeInbox(legacyFriendly, nil, false)["sender_name_type"])
|
||||
|
||||
legacyBusiness := &model.Inbox{AccountID: 1, Name: "Legacy Business", ChannelType: "web_widget", SenderNameType: "business_name"}
|
||||
require.Equal(t, "professional", serializeInbox(legacyBusiness, nil, false)["sender_name_type"])
|
||||
}
|
||||
|
||||
func TestSerializeInboxComputesCallbackWebhookURL(t *testing.T) {
|
||||
t.Setenv("FRONTEND_URL", "https://app.example.test/")
|
||||
|
||||
twilio := &model.Inbox{AccountID: 1, Name: "Twilio", ChannelType: "twilio_sms"}
|
||||
require.Equal(t, "https://app.example.test/twilio/callback", serializeInbox(twilio, nil, false)["callback_webhook_url"])
|
||||
|
||||
sms := &model.Inbox{AccountID: 1, Name: "SMS", ChannelType: "sms", ChannelConfig: `{"phone_number":"+1555010000"}`}
|
||||
require.Equal(t, "https://app.example.test/webhooks/sms/1555010000", serializeInbox(sms, nil, false)["callback_webhook_url"])
|
||||
|
||||
line := &model.Inbox{AccountID: 1, Name: "Line", ChannelType: "line", ChannelConfig: `{"line_channel_id":"line-id"}`}
|
||||
require.Equal(t, "https://app.example.test/webhooks/line/line-id", serializeInbox(line, nil, false)["callback_webhook_url"])
|
||||
|
||||
whatsapp := &model.Inbox{AccountID: 1, Name: "WhatsApp", ChannelType: "whatsapp", ChannelConfig: `{"phone_number":"+1555010001"}`}
|
||||
require.Equal(t, "https://app.example.test/webhooks/whatsapp/+1555010001", serializeInbox(whatsapp, nil, false)["callback_webhook_url"])
|
||||
|
||||
website := &model.Inbox{AccountID: 1, Name: "Website", ChannelType: "web_widget"}
|
||||
require.Nil(t, serializeInbox(website, nil, false)["callback_webhook_url"])
|
||||
}
|
||||
|
||||
func TestInboxHandler_SensitiveFieldsRequireAdministratorRole(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
db, err := gorm.Open(sqlite.Open("file:inbox_handler_sensitive_fields?mode=memory&cache=shared"), &gorm.Config{
|
||||
Logger: logger.Default.LogMode(logger.Silent),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
sqlDB, dbErr := db.DB()
|
||||
if dbErr == nil {
|
||||
_ = sqlDB.Close()
|
||||
}
|
||||
})
|
||||
require.NoError(t, db.AutoMigrate(&model.Account{}, &model.Inbox{}, &model.WorkingHour{}, &channelmodel.ChannelAPI{}))
|
||||
|
||||
account := &model.Account{Name: "Sensitive Inbox", Locale: "en", Active: true}
|
||||
require.NoError(t, db.Create(account).Error)
|
||||
apiInbox := &model.Inbox{AccountID: account.ID, Name: "API", ChannelType: "api", ChannelID: 1, Enabled: true, WebhookURL: "https://example.com/hook", Secret: "api-secret", ChannelConfig: `{"hmac_token":"api-hmac","identifier":"api-identifier"}`}
|
||||
require.NoError(t, db.Create(apiInbox).Error)
|
||||
emailInbox := &model.Inbox{AccountID: account.ID, Name: "Email", ChannelType: "email", ChannelID: 2, Enabled: true, ChannelConfig: `{"email":"support@example.com","provider":"google","provider_config":{},"imap_password":"imap-secret","smtp_password":"smtp-secret","smtp_address":"smtp.example.com"}`}
|
||||
require.NoError(t, db.Create(emailInbox).Error)
|
||||
whatsappInbox := &model.Inbox{AccountID: account.ID, Name: "WhatsApp", ChannelType: "whatsapp", ChannelID: 3, Enabled: true, ChannelConfig: `{"phone_number":"+1555010000","provider_config":{"api_key":"wa-secret"}}`}
|
||||
require.NoError(t, db.Create(whatsappInbox).Error)
|
||||
|
||||
router := setupInboxParityRouter(db)
|
||||
|
||||
agentShow := inboxParityRequestWithRole(t, router, http.MethodGet, fmt.Sprintf("/api/v1/accounts/%d/inboxes/%d", account.ID, apiInbox.ID), nil, "agent")
|
||||
require.Equal(t, http.StatusOK, agentShow.Code, agentShow.Body.String())
|
||||
agentAPIData := inboxParityObject(t, agentShow)
|
||||
require.Equal(t, "https://example.com/hook", agentAPIData["webhook_url"])
|
||||
require.NotContains(t, agentAPIData, "secret")
|
||||
require.NotContains(t, agentAPIData, "hmac_token")
|
||||
|
||||
adminShow := inboxParityRequestWithRole(t, router, http.MethodGet, fmt.Sprintf("/api/v1/accounts/%d/inboxes/%d", account.ID, apiInbox.ID), nil, "administrator")
|
||||
require.Equal(t, http.StatusOK, adminShow.Code, adminShow.Body.String())
|
||||
adminAPIData := inboxParityObject(t, adminShow)
|
||||
require.Equal(t, "api-secret", adminAPIData["secret"])
|
||||
require.Equal(t, "api-hmac", adminAPIData["hmac_token"])
|
||||
|
||||
agentEmailShow := inboxParityRequestWithRole(t, router, http.MethodGet, fmt.Sprintf("/api/v1/accounts/%d/inboxes/%d", account.ID, emailInbox.ID), nil, "agent")
|
||||
require.Equal(t, http.StatusOK, agentEmailShow.Code, agentEmailShow.Body.String())
|
||||
agentEmailData := inboxParityObject(t, agentEmailShow)
|
||||
require.Equal(t, "support@example.com", agentEmailData["email"])
|
||||
require.NotContains(t, agentEmailData, "imap_password")
|
||||
require.NotContains(t, agentEmailData, "smtp_password")
|
||||
require.NotContains(t, agentEmailData, "smtp_address")
|
||||
require.NotContains(t, agentEmailData, "reauthorization_required")
|
||||
|
||||
adminEmailShow := inboxParityRequestWithRole(t, router, http.MethodGet, fmt.Sprintf("/api/v1/accounts/%d/inboxes/%d", account.ID, emailInbox.ID), nil, "administrator")
|
||||
require.Equal(t, http.StatusOK, adminEmailShow.Code, adminEmailShow.Body.String())
|
||||
adminEmailData := inboxParityObject(t, adminEmailShow)
|
||||
require.Equal(t, true, adminEmailData["reauthorization_required"])
|
||||
|
||||
agentWhatsappShow := inboxParityRequestWithRole(t, router, http.MethodGet, fmt.Sprintf("/api/v1/accounts/%d/inboxes/%d", account.ID, whatsappInbox.ID), nil, "agent")
|
||||
require.Equal(t, http.StatusOK, agentWhatsappShow.Code, agentWhatsappShow.Body.String())
|
||||
agentWhatsappData := inboxParityObject(t, agentWhatsappShow)
|
||||
require.Equal(t, "+1555010000", agentWhatsappData["phone_number"])
|
||||
require.NotContains(t, agentWhatsappData, "provider_config")
|
||||
}
|
||||
|
||||
func TestInboxHandler_ChatwootCreateUpdateRequestBinding(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
@@ -136,7 +277,7 @@ func TestInboxHandler_ChatwootCreateUpdateRequestBinding(t *testing.T) {
|
||||
"greeting_message": {"Hello from Chatwoot"},
|
||||
"enable_email_collect": {"false"},
|
||||
"allow_messages_after_resolved": {"true"},
|
||||
"sender_name_type": {"business_name"},
|
||||
"sender_name_type": {"professional"},
|
||||
"business_name": {"Acme Support"},
|
||||
"channel[type]": {"web_widget"},
|
||||
"channel[website_url]": {"https://acme.example"},
|
||||
@@ -154,7 +295,7 @@ func TestInboxHandler_ChatwootCreateUpdateRequestBinding(t *testing.T) {
|
||||
require.NotEmpty(t, websiteData["website_token"])
|
||||
require.NotEmpty(t, websiteData["hmac_token"])
|
||||
require.Equal(t, false, websiteData["enable_email_collect"])
|
||||
require.Equal(t, "business_name", websiteData["sender_name_type"])
|
||||
require.Equal(t, "professional", websiteData["sender_name_type"])
|
||||
require.Equal(t, "Acme Support", websiteData["business_name"])
|
||||
require.ElementsMatch(t, []any{"attachments", "emoji_picker"}, websiteData["selected_feature_flags"].([]any))
|
||||
|
||||
@@ -280,6 +421,7 @@ func TestInboxHandler_ChatwootCreateRejectsAccountInboxLimit(t *testing.T) {
|
||||
|
||||
func TestInboxHandler_ChatwootChannelSpecificConfigDepth(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
t.Setenv("MAILER_INBOUND_EMAIL_DOMAIN", "mail.example.test")
|
||||
|
||||
db, err := gorm.Open(sqlite.Open("file:inbox_handler_channel_depth?mode=memory&cache=shared"), &gorm.Config{
|
||||
Logger: logger.Default.LogMode(logger.Silent),
|
||||
@@ -396,6 +538,14 @@ func setupInboxParityRouter(db *gorm.DB) *gin.Engine {
|
||||
inboxSvc := service.NewInboxService(repository.NewInboxRepo(db), nil, nil, nil, nil, nil, nil)
|
||||
handler := NewInboxHandler(inboxSvc)
|
||||
router := gin.New()
|
||||
router.Use(func(c *gin.Context) {
|
||||
role := c.GetHeader("X-Test-Role")
|
||||
if role == "" {
|
||||
role = "administrator"
|
||||
}
|
||||
c.Set("role", role)
|
||||
c.Next()
|
||||
})
|
||||
inboxes := router.Group("/api/v1/accounts/:id/inboxes")
|
||||
{
|
||||
inboxes.GET("/", handler.List)
|
||||
@@ -410,6 +560,10 @@ func setupInboxParityRouter(db *gorm.DB) *gin.Engine {
|
||||
}
|
||||
|
||||
func inboxParityRequest(t *testing.T, router *gin.Engine, method string, path string, body any) *httptest.ResponseRecorder {
|
||||
return inboxParityRequestWithRole(t, router, method, path, body, "")
|
||||
}
|
||||
|
||||
func inboxParityRequestWithRole(t *testing.T, router *gin.Engine, method string, path string, body any, role string) *httptest.ResponseRecorder {
|
||||
t.Helper()
|
||||
var reader *bytes.Reader
|
||||
if body == nil {
|
||||
@@ -421,6 +575,9 @@ func inboxParityRequest(t *testing.T, router *gin.Engine, method string, path st
|
||||
}
|
||||
req := httptest.NewRequest(method, path, reader)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
if role != "" {
|
||||
req.Header.Set("X-Test-Role", role)
|
||||
}
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, req)
|
||||
return w
|
||||
|
||||
Reference in New Issue
Block a user