test(webhook): cover provider ingress persistence fixtures
This commit is contained in:
@@ -10,12 +10,17 @@ import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gochat/gochat/internal/channel"
|
||||
linechannel "github.com/gochat/gochat/internal/channel/line"
|
||||
channelprovider "github.com/gochat/gochat/internal/channel/provider"
|
||||
tiktokchannel "github.com/gochat/gochat/internal/channel/tiktok"
|
||||
twiliochannel "github.com/gochat/gochat/internal/channel/twilio"
|
||||
whatsappchannel "github.com/gochat/gochat/internal/channel/whatsapp"
|
||||
"github.com/gochat/gochat/internal/model"
|
||||
channelmodel "github.com/gochat/gochat/internal/model/channel"
|
||||
"gorm.io/driver/sqlite"
|
||||
@@ -51,6 +56,7 @@ func newWebhookLookupTestDB(t *testing.T) *gorm.DB {
|
||||
&channelmodel.ChannelTelegram{},
|
||||
&channelmodel.ChannelLINE{},
|
||||
&channelmodel.ChannelTwilioSMS{},
|
||||
&channelmodel.ChannelWhatsApp{},
|
||||
&channelmodel.ChannelTikTok{},
|
||||
&channelmodel.ChannelInstagram{},
|
||||
&model.IntegrationHook{},
|
||||
@@ -288,6 +294,34 @@ func TestLineWebhookLookupInboxByLineChannelID(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestLineWebhookPersistsIncomingMessage(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
db := newWebhookLookupTestDB(t)
|
||||
inbox := seedWebhookInbox(t, db, "line")
|
||||
channelRecord := channelmodel.ChannelLINE{AccountID: 1, InboxID: inbox.ID, ChannelID: "line-channel-1", Name: "LINE OA"}
|
||||
if err := db.Create(&channelRecord).Error; err != nil {
|
||||
t.Fatalf("create line channel: %v", err)
|
||||
}
|
||||
|
||||
lineRepo := linechannel.NewRepository(db)
|
||||
lineService := linechannel.NewLineService(lineRepo)
|
||||
linePipeline := linechannel.NewIncomingProcessor(lineService)
|
||||
h := NewLineWebhookHandler(nil, linePipeline, lineService, db)
|
||||
r := gin.New()
|
||||
r.POST("/webhooks/line/:line_channel_id", h.HandleLineWebhook)
|
||||
body := []byte(`{"destination":"line-channel-1","events":[{"type":"message","replyToken":"reply-1","timestamp":1710000000000,"source":{"type":"user","userId":"line-user-1"},"message":{"type":"text","id":"line-msg-1","text":"hello line"}}]}`)
|
||||
req := httptest.NewRequest(http.MethodPost, "/webhooks/line/line-channel-1", bytes.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("expected 200, got %d body=%s", w.Code, w.Body.String())
|
||||
}
|
||||
assertPersistedMessage(t, db, inbox.ID, "line-msg-1", "hello line")
|
||||
}
|
||||
|
||||
func TestTwilioWebhookLookupInboxByPhoneNumber(t *testing.T) {
|
||||
db := newWebhookLookupTestDB(t)
|
||||
inbox := seedWebhookInbox(t, db, "twilio_sms")
|
||||
@@ -312,6 +346,40 @@ func TestTwilioWebhookLookupInboxByPhoneNumber(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTwilioWebhookPersistsIncomingMessage(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
db := newWebhookLookupTestDB(t)
|
||||
inbox := seedWebhookInbox(t, db, "twilio_sms")
|
||||
channelRecord := channelmodel.ChannelTwilioSMS{AccountID: 1, InboxID: inbox.ID, AccountSID: "AC123", PhoneNumber: "+15551234567"}
|
||||
if err := db.Create(&channelRecord).Error; err != nil {
|
||||
t.Fatalf("create twilio channel: %v", err)
|
||||
}
|
||||
|
||||
twilioRepo := twiliochannel.NewRepository(db)
|
||||
twilioService := twiliochannel.NewTwilioService(twilioRepo)
|
||||
twilioPipeline := twiliochannel.NewIncomingProcessor(twilioService)
|
||||
twilioWebhook := twiliochannel.NewWebhookHandler(twilioPipeline, twilioService)
|
||||
h := NewTwilioWebhookHandler(twilioWebhook, db)
|
||||
r := gin.New()
|
||||
r.POST("/webhooks/sms/:phone_number", h.HandleTwilioInboundSMS)
|
||||
form := url.Values{}
|
||||
form.Set("MessageSid", "SMIN1")
|
||||
form.Set("AccountSid", "AC123")
|
||||
form.Set("From", "+15550002222")
|
||||
form.Set("To", "+15551234567")
|
||||
form.Set("Body", "hello sms")
|
||||
req := httptest.NewRequest(http.MethodPost, "/webhooks/sms/+15551234567", strings.NewReader(form.Encode()))
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("expected 200, got %d body=%s", w.Code, w.Body.String())
|
||||
}
|
||||
assertPersistedMessage(t, db, inbox.ID, "SMIN1", "hello sms")
|
||||
}
|
||||
|
||||
func TestTwilioDeliveryStatusUpdatesExistingMessage(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
db := newWebhookLookupTestDB(t)
|
||||
@@ -358,6 +426,36 @@ func TestTwilioDeliveryStatusUpdatesExistingMessage(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestWhatsAppWebhookPersistsIncomingMessage(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
db := newWebhookLookupTestDB(t)
|
||||
inbox := seedWebhookInbox(t, db, "whatsapp")
|
||||
waChannel := channelmodel.ChannelWhatsApp{AccountID: 1, InboxID: inbox.ID, PhoneNumber: "+15551230000", PhoneNumberID: "phone-id-1", AccessToken: "token"}
|
||||
if err := db.Create(&waChannel).Error; err != nil {
|
||||
t.Fatalf("create whatsapp channel: %v", err)
|
||||
}
|
||||
|
||||
waRepo := whatsappchannel.NewRepository(db)
|
||||
waService := whatsappchannel.NewWhatsAppService(waRepo)
|
||||
waPipeline := whatsappchannel.NewIncomingPipeline(waService)
|
||||
waProvider := whatsappchannel.NewWhatsAppProvider(waService, waRepo, waPipeline)
|
||||
waWebhook := whatsappchannel.NewWebhookHandler(waProvider)
|
||||
h := NewWhatsAppWebhookHandler(waProvider, waWebhook, db)
|
||||
r := gin.New()
|
||||
r.POST("/webhooks/whatsapp/:phone_number", h.HandleWhatsAppWebhook)
|
||||
body := []byte(`{"object":"whatsapp_business_account","entry":[{"id":"waba-1","changes":[{"field":"messages","value":{"messaging_product":"whatsapp","metadata":{"display_phone_number":"+15551230000","phone_number_id":"phone-id-1"},"contacts":[{"wa_id":"15550001111","profile":{"name":"WhatsApp User"}}],"messages":[{"from":"15550001111","id":"wamid-1","timestamp":"1710000000","type":"text","text":{"body":"hello whatsapp"}}]}}]}]}`)
|
||||
req := httptest.NewRequest(http.MethodPost, "/webhooks/whatsapp/+15551230000", bytes.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("expected 200, got %d body=%s", w.Code, w.Body.String())
|
||||
}
|
||||
assertPersistedMessage(t, db, inbox.ID, "wamid-1", "hello whatsapp")
|
||||
}
|
||||
|
||||
func TestTikTokWebhookLookupInboxByBusinessIDAndPayloadExtractor(t *testing.T) {
|
||||
db := newWebhookLookupTestDB(t)
|
||||
inbox := seedWebhookInbox(t, db, "tiktok")
|
||||
@@ -385,6 +483,35 @@ func TestTikTokWebhookLookupInboxByBusinessIDAndPayloadExtractor(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTikTokWebhookPersistsIncomingMessage(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
db := newWebhookLookupTestDB(t)
|
||||
inbox := seedWebhookInbox(t, db, "tiktok")
|
||||
channelRecord := channelmodel.ChannelTikTok{AccountID: 1, InboxID: inbox.ID, TikTokBusinessID: "biz-123", WebhookVerifyToken: "verify-token"}
|
||||
if err := db.Create(&channelRecord).Error; err != nil {
|
||||
t.Fatalf("create tiktok channel: %v", err)
|
||||
}
|
||||
|
||||
ttRepo := tiktokchannel.NewRepository(db)
|
||||
ttService := tiktokchannel.NewTikTokService(ttRepo)
|
||||
ttPipeline := tiktokchannel.NewIncomingProcessor(ttService, ttRepo)
|
||||
ttWebhook := tiktokchannel.NewWebhookHandler(ttService, ttPipeline)
|
||||
h := NewTikTokWebhookHandler(ttWebhook, ttPipeline, db)
|
||||
r := gin.New()
|
||||
r.POST("/webhooks/tiktok", h.HandleTikTokWebhook)
|
||||
body := []byte(`{"type":"message.received","timestamp":1710000000,"biz_id":"biz-123","data":{"message_id":"tt-msg-1","from_user_id":"tt-user-1","to_user_id":"biz-123","content_type":"text","content":"hello tiktok","timestamp":1710000000,"conversation_id":"tt-conv-1"}}`)
|
||||
req := httptest.NewRequest(http.MethodPost, "/webhooks/tiktok", bytes.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("expected 200, got %d body=%s", w.Code, w.Body.String())
|
||||
}
|
||||
assertPersistedMessage(t, db, inbox.ID, "tt-msg-1", "hello tiktok")
|
||||
}
|
||||
|
||||
func TestShopifyWebhookShopRedactDeletesMatchingHook(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
db := newWebhookLookupTestDB(t)
|
||||
@@ -490,4 +617,17 @@ func TestInstagramWebhookEventsVerifySignatureAndResolveInbox(t *testing.T) {
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("expected 200, got %d body=%s", w.Code, w.Body.String())
|
||||
}
|
||||
assertPersistedMessage(t, db, inbox.ID, "mid-1", "hello")
|
||||
}
|
||||
|
||||
func assertPersistedMessage(t *testing.T, db *gorm.DB, inboxID uint, sourceID string, content string) model.Message {
|
||||
t.Helper()
|
||||
var message model.Message
|
||||
if err := db.Where("inbox_id = ? AND source_id = ?", inboxID, sourceID).First(&message).Error; err != nil {
|
||||
t.Fatalf("expected message source_id=%s persisted: %v", sourceID, err)
|
||||
}
|
||||
if message.Content != content || message.MessageType != string(model.MessageTypeIncoming) {
|
||||
t.Fatalf("unexpected message source_id=%s: %#v", sourceID, message)
|
||||
}
|
||||
return message
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user