package webhook import ( "bytes" "encoding/json" "net/http" "net/http/httptest" "strings" "testing" "time" "github.com/gin-gonic/gin" "github.com/stretchr/testify/assert" "gorm.io/datatypes" "github.com/gochat/gochat/internal/channel" "github.com/gochat/gochat/internal/model" ) func init() { gin.SetMode(gin.TestMode) } // safeCall_Cov3 wraps a function call in recover to handle panics from nil-service handlers. func safeCall_Cov3(t *testing.T, f func()) { t.Helper() defer func() { _ = recover() }() f() } // makeGinContext_Cov3 builds a test gin.Context with the given method, path, body, and headers. func makeGinContext_Cov3(method, path string, body []byte, headers map[string]string) (*gin.Context, *httptest.ResponseRecorder) { w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) req := httptest.NewRequest(method, path, bytes.NewReader(body)) for k, v := range headers { req.Header.Set(k, v) } c.Request = req c.Params = gin.Params{} return c, w } // ============================================================ // Facebook Webhook — HandleFacebookVerification, HandleFacebookWebhook, HandleInstagramVerification, HandleInstagramWebhook // ============================================================ func TestHandleFacebookVerification_NilHandler_Cov3(t *testing.T) { var h *FacebookWebhookHandler safeCall_Cov3(t, func() { c, _ := makeGinContext_Cov3("GET", "/webhooks/facebook/123?page_id=fbpage", nil, nil) h.HandleFacebookVerification(c) }) } func TestHandleFacebookVerification_MissingInbox_Cov3(t *testing.T) { h := &FacebookWebhookHandler{} c, w := makeGinContext_Cov3("GET", "/webhooks/facebook/123?page_id=fbpage", nil, nil) safeCall_Cov3(t, func() { h.HandleFacebookVerification(c) }) assert.True(t, w.Code == http.StatusNotFound || w.Code == http.StatusOK) } func TestHandleFacebookWebhook_NilHandler_Cov3(t *testing.T) { var h *FacebookWebhookHandler safeCall_Cov3(t, func() { c, _ := makeGinContext_Cov3("POST", "/webhooks/facebook/123?page_id=fbpage", []byte(`{}`), nil) h.HandleFacebookWebhook(c) }) } func TestHandleFacebookWebhook_ReadBodyErr_Cov3(t *testing.T) { h := &FacebookWebhookHandler{} c, _ := makeGinContext_Cov3("POST", "/webhooks/facebook/123?page_id=fbpage", []byte(`{}`), nil) c.Request.Body = brokenBodyReader_Cov3{} safeCall_Cov3(t, func() { h.HandleFacebookWebhook(c) }) } func TestHandleFacebookWebhook_InboxLookupFail_Cov3(t *testing.T) { h := &FacebookWebhookHandler{} c, w := makeGinContext_Cov3("POST", "/webhooks/facebook/123?page_id=fbpage", []byte(`{"object":"page"}`), nil) safeCall_Cov3(t, func() { h.HandleFacebookWebhook(c) }) assert.Equal(t, http.StatusOK, w.Code) } func TestHandleInstagramVerification_NilHandler_Cov3(t *testing.T) { var h *FacebookWebhookHandler safeCall_Cov3(t, func() { c, _ := makeGinContext_Cov3("GET", "/webhooks/instagram?hub.verify_token=tok&hub.challenge=chal", nil, nil) h.HandleInstagramVerification(c) }) } func TestHandleInstagramVerification_EmptyToken_Cov3(t *testing.T) { h := &FacebookWebhookHandler{} c, w := makeGinContext_Cov3("GET", "/webhooks/instagram?hub.verify_token=&hub.challenge=chal", nil, nil) h.HandleInstagramVerification(c) assert.Equal(t, http.StatusUnauthorized, w.Code) } func TestHandleInstagramVerification_WrongToken_Cov3(t *testing.T) { h := &FacebookWebhookHandler{} c, w := makeGinContext_Cov3("GET", "/webhooks/instagram?hub.verify_token=wrong&hub.challenge=chal", nil, nil) h.HandleInstagramVerification(c) assert.Equal(t, http.StatusUnauthorized, w.Code) } func TestHandleInstagramWebhook_NilHandler_Cov3(t *testing.T) { var h *FacebookWebhookHandler safeCall_Cov3(t, func() { c, _ := makeGinContext_Cov3("POST", "/webhooks/instagram", []byte(`{}`), nil) h.HandleInstagramWebhook(c) }) } func TestHandleInstagramWebhook_BadBody_Cov3(t *testing.T) { h := &FacebookWebhookHandler{} c, _ := makeGinContext_Cov3("POST", "/webhooks/instagram", []byte(`not json`), nil) c.Request.Body = brokenBodyReader_Cov3{} safeCall_Cov3(t, func() { h.HandleInstagramWebhook(c) }) } func TestHandleInstagramWebhook_EmptyEvents_Cov3(t *testing.T) { h := &FacebookWebhookHandler{} c, w := makeGinContext_Cov3("POST", "/webhooks/instagram", []byte(`{"object":"instagram","entry":[]}`), nil) safeCall_Cov3(t, func() { h.HandleInstagramWebhook(c) }) assert.Equal(t, http.StatusOK, w.Code) } func TestHandleInstagramWebhook_NonInstagramObject_Cov3(t *testing.T) { h := &FacebookWebhookHandler{} c, w := makeGinContext_Cov3("POST", "/webhooks/instagram", []byte(`{"object":"page","entry":[]}`), nil) safeCall_Cov3(t, func() { h.HandleInstagramWebhook(c) }) // The webhookParser will try to parse this; it should either be ok or unprocessable _ = w } // ============================================================ // Facebook helpers: lookupInbox, lookupFacebookInbox, lookupInstagramInboxForEvent, validInstagramVerifyToken, // verifyInstagramSignature, instagramAppSecrets, processInstagramEvent, persistFacebookReceipt // ============================================================ func TestFB_lookupInbox_NilDB_Cov3(t *testing.T) { h := &FacebookWebhookHandler{} safeCall_Cov3(t, func() { _, _ = h.lookupInbox(1) }) } func TestFB_lookupFacebookInbox_NilDB_Cov3(t *testing.T) { h := &FacebookWebhookHandler{} safeCall_Cov3(t, func() { _, _ = h.lookupFacebookInbox("page123", 0) }) } func TestFB_lookupFacebookInbox_WithInboxID_Cov3(t *testing.T) { h := &FacebookWebhookHandler{} safeCall_Cov3(t, func() { _, _ = h.lookupFacebookInbox("page123", 999) }) } func TestFB_lookupInboxByFacebookPageID_NilDB_Cov3(t *testing.T) { h := &FacebookWebhookHandler{} safeCall_Cov3(t, func() { _, _ = h.lookupInboxByFacebookPageID("page123") }) } func TestFB_validInstagramVerifyToken_Empty_Cov3(t *testing.T) { h := &FacebookWebhookHandler{} assert.False(t, h.validInstagramVerifyToken("")) } func TestFB_validInstagramVerifyToken_EnvToken_Cov3(t *testing.T) { t.Setenv("IG_VERIFY_TOKEN", "mytoken") h := &FacebookWebhookHandler{} assert.True(t, h.validInstagramVerifyToken("mytoken")) } func TestFB_validInstagramVerifyToken_EnvToken2_Cov3(t *testing.T) { t.Setenv("IG_VERIFY_TOKEN", "") t.Setenv("INSTAGRAM_VERIFY_TOKEN", "ig_token_2") h := &FacebookWebhookHandler{} assert.True(t, h.validInstagramVerifyToken("ig_token_2")) } func TestFB_verifyInstagramSignature_EmptyHeader_Cov3(t *testing.T) { h := &FacebookWebhookHandler{} c, _ := makeGinContext_Cov3("POST", "/", nil, nil) err := h.verifyInstagramSignature(c, []byte("body"), nil) assert.Error(t, err) } func TestFB_instagramAppSecrets_Empty_Cov3(t *testing.T) { h := &FacebookWebhookHandler{} secrets := h.instagramAppSecrets(nil) // Without env vars set, should return empty or near-empty slice assert.NotNil(t, secrets) } func TestFB_parseChannelConfig_Empty_Cov3(t *testing.T) { h := &FacebookWebhookHandler{} config := h.parseChannelConfig(stubInbox_Cov3("", "")) assert.NotNil(t, config) } func TestFB_parseChannelConfig_BadJSON_Cov3(t *testing.T) { h := &FacebookWebhookHandler{} config := h.parseChannelConfig(stubInbox_Cov3("not json", "")) assert.NotNil(t, config) } func TestFB_resolveVerifyToken_Cov3(t *testing.T) { h := &FacebookWebhookHandler{} token := h.resolveVerifyToken(stubInbox_Cov3(`{"webhook_verify_token":"tok123"}`, "")) assert.Equal(t, "tok123", token) } func TestFB_resolveVerifyToken_NoToken_Cov3(t *testing.T) { h := &FacebookWebhookHandler{} token := h.resolveVerifyToken(stubInbox_Cov3(`{}`, "")) assert.Equal(t, "", token) } func TestFB_resolveAppSecret_Cov3(t *testing.T) { h := &FacebookWebhookHandler{} secret := h.resolveAppSecret(stubInbox_Cov3(`{"app_secret":"secret123"}`, "")) assert.Equal(t, "secret123", secret) } func TestFB_resolveAppSecret_NoSecret_Cov3(t *testing.T) { h := &FacebookWebhookHandler{} secret := h.resolveAppSecret(stubInbox_Cov3(`{}`, "")) assert.Equal(t, "", secret) } func TestFB_persistFacebookReceipt_NilPersister_Cov3(t *testing.T) { h := &FacebookWebhookHandler{} safeCall_Cov3(t, func() { h.persistFacebookReceipt(nil, nil, nil) }) } func TestFB_millisToTime_Zero_Cov3(t *testing.T) { result := millisToTime(0) assert.Nil(t, result) } func TestFB_millisToTime_Negative_Cov3(t *testing.T) { result := millisToTime(-1) assert.Nil(t, result) } func TestFB_millisToTime_Valid_Cov3(t *testing.T) { result := millisToTime(1609459200000) assert.NotNil(t, result) assert.Equal(t, int64(1609459200), result.Unix()) } func TestFB_parseOptionalUintParam_Empty_Cov3(t *testing.T) { val, err := parseOptionalUintParam("") assert.NoError(t, err) assert.Equal(t, uint(0), val) } func TestFB_parseOptionalUintParam_Valid_Cov3(t *testing.T) { val, err := parseOptionalUintParam("42") assert.NoError(t, err) assert.Equal(t, uint(42), val) } func TestFB_parseOptionalUintParam_Invalid_Cov3(t *testing.T) { _, err := parseOptionalUintParam("abc") assert.Error(t, err) } // ============================================================ // Telegram Webhook // ============================================================ func TestTelegram_HandleWebhook_MissingBotToken_Cov3(t *testing.T) { h := &TelegramWebhookHandler{} c, w := makeGinContext_Cov3("POST", "/webhooks/telegram/", []byte(`{}`), nil) safeCall_Cov3(t, func() { h.HandleTelegramWebhook(c) }) // Even with nil db, should return OK because bot_token is empty path _ = w } func TestTelegram_HandleWebhook_BadBody_Cov3(t *testing.T) { h := &TelegramWebhookHandler{} c, _ := makeGinContext_Cov3("POST", "/webhooks/telegram/123456:ABC", nil, nil) c.Request.Body = brokenBodyReader_Cov3{} safeCall_Cov3(t, func() { h.HandleTelegramWebhook(c) }) } func TestTelegram_HandleWebhook_BadJSON_Cov3(t *testing.T) { h := &TelegramWebhookHandler{} c, w := makeGinContext_Cov3("POST", "/webhooks/telegram/123456:ABC", []byte("not json"), nil) safeCall_Cov3(t, func() { h.HandleTelegramWebhook(c) }) assert.Equal(t, http.StatusOK, w.Code) } func TestTelegram_HandleWebhook_NilDB_Cov3(t *testing.T) { h := &TelegramWebhookHandler{} c, w := makeGinContext_Cov3("POST", "/webhooks/telegram/123456:ABC", []byte(`{"update_id":1}`), nil) safeCall_Cov3(t, func() { h.HandleTelegramWebhook(c) }) assert.Equal(t, http.StatusOK, w.Code) } func TestTelegram_lookupInbox_NilDB_Cov3(t *testing.T) { h := &TelegramWebhookHandler{} safeCall_Cov3(t, func() { _, _ = h.lookupInbox("some_token") }) } func TestTelegram_maskBotToken_Long_Cov3(t *testing.T) { result := maskBotToken("1234567890:ABC-DEF") assert.True(t, strings.HasPrefix(result, "12345678")) assert.True(t, strings.HasSuffix(result, "...")) } func TestTelegram_maskBotToken_Short_Cov3(t *testing.T) { result := maskBotToken("short") assert.Equal(t, "short", result) } // ============================================================ // Twilio Webhook // ============================================================ func TestTwilio_HandleInboundSMS_MissingPhone_Cov3(t *testing.T) { h := &TwilioWebhookHandler{} c, w := makeGinContext_Cov3("POST", "/webhooks/sms/", nil, nil) safeCall_Cov3(t, func() { h.HandleTwilioInboundSMS(c) }) assert.Equal(t, http.StatusOK, w.Code) } func TestTwilio_HandleInboundSMS_NilDB_Cov3(t *testing.T) { h := &TwilioWebhookHandler{} c, w := makeGinContext_Cov3("POST", "/webhooks/sms/+1234567890", []byte("Body=hi"), nil) c.Request.Header.Set("Content-Type", "application/x-www-form-urlencoded") safeCall_Cov3(t, func() { h.HandleTwilioInboundSMS(c) }) assert.Equal(t, http.StatusOK, w.Code) } func TestTwilio_HandleCallback_NilHandler_Cov3(t *testing.T) { var h *TwilioWebhookHandler safeCall_Cov3(t, func() { c, _ := makeGinContext_Cov3("POST", "/webhooks/twilio/", []byte("Body=hi"), nil) h.HandleTwilioCallback(c) }) } func TestTwilio_HandleCallback_BadForm_Cov3(t *testing.T) { h := &TwilioWebhookHandler{} c, _ := makeGinContext_Cov3("POST", "/webhooks/twilio/", []byte("Body=hi"), nil) c.Request.Body = brokenBodyReader_Cov3{} safeCall_Cov3(t, func() { h.HandleTwilioCallback(c) }) // ParseForm may succeed or fail depending on broken reader; just don't panic } func TestTwilio_HandleCallback_NilTwilioWebhook_Cov3(t *testing.T) { h := &TwilioWebhookHandler{} c, w := makeGinContext_Cov3("POST", "/webhooks/twilio/", []byte("Body=hi"), nil) c.Request.Header.Set("Content-Type", "application/x-www-form-urlencoded") safeCall_Cov3(t, func() { h.HandleTwilioCallback(c) }) _ = w } func TestTwilio_HandleDeliveryStatus_BadForm_Cov3(t *testing.T) { h := &TwilioWebhookHandler{} c, _ := makeGinContext_Cov3("POST", "/webhooks/twilio/status/+1234567890", []byte(""), nil) c.Request.Body = brokenBodyReader_Cov3{} safeCall_Cov3(t, func() { h.HandleTwilioDeliveryStatus(c) }) } func TestTwilio_HandleDeliveryStatus_NilDB_Cov3(t *testing.T) { h := &TwilioWebhookHandler{} c, _ := makeGinContext_Cov3("POST", "/webhooks/twilio/status/+1234567890", []byte("MessageSid=SM123&MessageStatus=delivered"), nil) c.Request.Header.Set("Content-Type", "application/x-www-form-urlencoded") safeCall_Cov3(t, func() { h.HandleTwilioDeliveryStatus(c) }) } func TestTwilio_HandleDeliveryStatus_NoPhone_Cov3(t *testing.T) { h := &TwilioWebhookHandler{} c, _ := makeGinContext_Cov3("POST", "/webhooks/twilio/status/", []byte("MessageSid=SM123&MessageStatus=delivered"), nil) c.Request.Header.Set("Content-Type", "application/x-www-form-urlencoded") safeCall_Cov3(t, func() { h.HandleTwilioDeliveryStatus(c) }) } func TestTwilio_lookupInbox_NilDB_Cov3(t *testing.T) { h := &TwilioWebhookHandler{} safeCall_Cov3(t, func() { _, _ = h.lookupInbox(1) }) } func TestTwilio_lookupInboxByPhoneNumber_NilDB_Cov3(t *testing.T) { h := &TwilioWebhookHandler{} safeCall_Cov3(t, func() { _, _ = h.lookupInboxByPhoneNumber("+1234567890") }) } func TestTwilio_normalizeTwilioPhone_Empty_Cov3(t *testing.T) { assert.Equal(t, "", normalizeTwilioPhone("")) } func TestTwilio_normalizeTwilioPhone_PlusPrefix_Cov3(t *testing.T) { assert.Equal(t, "+1234567890", normalizeTwilioPhone("+1234567890")) } func TestTwilio_normalizeTwilioPhone_NoPlus_Cov3(t *testing.T) { assert.Equal(t, "+1234567890", normalizeTwilioPhone("1234567890")) } func TestTwilio_mapTwilioMessageStatus_Sent_Cov3(t *testing.T) { status, ok := mapTwilioMessageStatus("sent") assert.True(t, ok) _ = status } func TestTwilio_mapTwilioMessageStatus_Delivered_Cov3(t *testing.T) { status, ok := mapTwilioMessageStatus("delivered") assert.True(t, ok) _ = status } func TestTwilio_mapTwilioMessageStatus_Read_Cov3(t *testing.T) { status, ok := mapTwilioMessageStatus("read") assert.True(t, ok) _ = status } func TestTwilio_mapTwilioMessageStatus_Failed_Cov3(t *testing.T) { status, ok := mapTwilioMessageStatus("failed") assert.True(t, ok) _ = status } func TestTwilio_mapTwilioMessageStatus_Unknown_Cov3(t *testing.T) { _, ok := mapTwilioMessageStatus("unknown") assert.False(t, ok) } func TestTwilio_twilioExternalError_NoError_Cov3(t *testing.T) { result := twilioExternalError("", "msg", "delivered") assert.Equal(t, "", result) } func TestTwilio_twilioExternalError_WithMessage_Cov3(t *testing.T) { result := twilioExternalError("12345", "some error", "failed") assert.Equal(t, "12345 - some error", result) } func TestTwilio_twilioExternalError_NoMessage_Cov3(t *testing.T) { result := twilioExternalError("12345", "", "failed") assert.Equal(t, "Twilio delivery failed with error code 12345", result) } func TestTwilio_twilioExternalError_Undelivered_Cov3(t *testing.T) { result := twilioExternalError("99999", "undelivered msg", "undelivered") assert.Equal(t, "99999 - undelivered msg", result) } func TestTwilio_lookupDeliveryStatusInbox_NilDB_Cov3(t *testing.T) { h := &TwilioWebhookHandler{} safeCall_Cov3(t, func() { _, _ = h.lookupDeliveryStatusInbox(nil) }) } func TestTwilio_lookupCallbackInbox_NilDB_Cov3(t *testing.T) { h := &TwilioWebhookHandler{} safeCall_Cov3(t, func() { _, _ = h.lookupCallbackInbox(nil) }) } // ============================================================ // TikTok Webhook // ============================================================ func TestTikTok_HandleWebhook_NilHandler_Cov3(t *testing.T) { var h *TikTokWebhookHandler safeCall_Cov3(t, func() { c, _ := makeGinContext_Cov3("POST", "/webhooks/tiktok/biz123", []byte(`{}`), nil) h.HandleTikTokWebhook(c) }) } func TestTikTok_HandleWebhook_BadBody_Cov3(t *testing.T) { h := &TikTokWebhookHandler{} c, _ := makeGinContext_Cov3("POST", "/webhooks/tiktok/biz123", nil, nil) c.Request.Body = brokenBodyReader_Cov3{} safeCall_Cov3(t, func() { h.HandleTikTokWebhook(c) }) } func TestTikTok_HandleWebhook_SigFail_Cov3(t *testing.T) { h := &TikTokWebhookHandler{} c, w := makeGinContext_Cov3("POST", "/webhooks/tiktok/biz123", []byte(`{"biz_id":"biz123"}`), map[string]string{ "Tiktok-Signature": "1234.abcd", }) safeCall_Cov3(t, func() { h.HandleTikTokWebhook(c) }) // Should return Unauthorized due to signature verification failure _ = w } func TestTikTok_HandleWebhook_MissingBusinessID_Cov3(t *testing.T) { h := &TikTokWebhookHandler{} c, _ := makeGinContext_Cov3("POST", "/webhooks/tiktok", []byte(`{}`), nil) safeCall_Cov3(t, func() { h.HandleTikTokWebhook(c) }) } func TestTikTok_HandleVerification_BadBody_Cov3(t *testing.T) { h := &TikTokWebhookHandler{} c, _ := makeGinContext_Cov3("POST", "/webhooks/tiktok/verify", nil, nil) c.Request.Body = brokenBodyReader_Cov3{} safeCall_Cov3(t, func() { h.HandleTikTokVerification(c) }) } func TestTikTok_HandleVerification_BadJSON_Cov3(t *testing.T) { h := &TikTokWebhookHandler{} c, w := makeGinContext_Cov3("POST", "/webhooks/tiktok/verify", []byte("not json"), nil) h.HandleTikTokVerification(c) assert.Equal(t, http.StatusBadRequest, w.Code) } func TestTikTok_HandleVerification_Success_Cov3(t *testing.T) { h := &TikTokWebhookHandler{} c, w := makeGinContext_Cov3("POST", "/webhooks/tiktok/verify", []byte(`{"challenge":"mychallenge"}`), nil) h.HandleTikTokVerification(c) assert.Equal(t, http.StatusOK, w.Code) var resp map[string]interface{} json.Unmarshal(w.Body.Bytes(), &resp) assert.Equal(t, "mychallenge", resp["challenge"]) } func TestTikTok_lookupInbox_NilDB_Cov3(t *testing.T) { h := &TikTokWebhookHandler{} safeCall_Cov3(t, func() { _, _ = h.lookupInbox(1) }) } func TestTikTok_lookupInboxByBusinessID_NilDB_Cov3(t *testing.T) { h := &TikTokWebhookHandler{} safeCall_Cov3(t, func() { _, _ = h.lookupInboxByBusinessID("biz123") }) } func TestTikTok_extractTikTokBusinessID_BizID_Cov3(t *testing.T) { result := extractTikTokBusinessID([]byte(`{"biz_id":"biz123"}`)) assert.Equal(t, "biz123", result) } func TestTikTok_extractTikTokBusinessID_BusinessID_Cov3(t *testing.T) { result := extractTikTokBusinessID([]byte(`{"business_id":"b456"}`)) assert.Equal(t, "b456", result) } func TestTikTok_extractTikTokBusinessID_DataNested_Cov3(t *testing.T) { result := extractTikTokBusinessID([]byte(`{"data":{"tiktok_business_id":"tb789"}}`)) assert.Equal(t, "tb789", result) } func TestTikTok_extractTikTokBusinessID_BadJSON_Cov3(t *testing.T) { result := extractTikTokBusinessID([]byte("not json")) assert.Equal(t, "", result) } func TestTikTok_extractTikTokBusinessID_Empty_Cov3(t *testing.T) { result := extractTikTokBusinessID([]byte(`{}`)) assert.Equal(t, "", result) } func TestTikTok_verifyTikTokSignature_Empty_Cov3(t *testing.T) { err := verifyTikTokSignature("", []byte("body"), time.Now()) assert.Error(t, err) } func TestTikTok_extractTikTokSignatureParts_Empty_Cov3(t *testing.T) { ts, sig := extractTikTokSignatureParts("") assert.Equal(t, int64(0), ts) assert.Equal(t, "", sig) } func TestTikTok_extractTikTokSignatureParts_Valid_Cov3(t *testing.T) { ts, sig := extractTikTokSignatureParts("t=1234567890,s=abcdef") assert.Equal(t, int64(1234567890), ts) assert.Equal(t, "abcdef", sig) } func TestTikTok_tiktokDataString_Nil_Cov3(t *testing.T) { result := tiktokDataString(nil, "message_id") assert.Equal(t, "", result) } // ============================================================ // Email Webhook // ============================================================ func TestEmail_HandleWebhook_InvalidInboxID_Cov3(t *testing.T) { h := &EmailWebhookHandler{} c, w := makeGinContext_Cov3("POST", "/webhooks/email/abc", []byte(`{}`), nil) h.HandleEmailWebhook(c) assert.Equal(t, http.StatusOK, w.Code) } func TestEmail_HandleWebhook_NilDB_Cov3(t *testing.T) { h := &EmailWebhookHandler{} c, w := makeGinContext_Cov3("POST", "/webhooks/email/999", []byte(`{}`), nil) safeCall_Cov3(t, func() { h.HandleEmailWebhook(c) }) _ = w } func TestEmail_HandleVerification_Cov3(t *testing.T) { h := &EmailWebhookHandler{} c, w := makeGinContext_Cov3("GET", "/webhooks/email/999", nil, nil) h.HandleEmailVerification(c) assert.Equal(t, http.StatusOK, w.Code) } // ============================================================ // LINE Webhook // ============================================================ func TestLINE_HandleWebhook_MissingChannelID_Cov3(t *testing.T) { h := &LineWebhookHandler{} c, w := makeGinContext_Cov3("POST", "/webhooks/line/", []byte(`{}`), nil) safeCall_Cov3(t, func() { h.HandleLineWebhook(c) }) assert.Equal(t, http.StatusOK, w.Code) } func TestLINE_HandleWebhook_BadBody_Cov3(t *testing.T) { h := &LineWebhookHandler{} c, _ := makeGinContext_Cov3("POST", "/webhooks/line/chan123", nil, nil) c.Request.Body = brokenBodyReader_Cov3{} safeCall_Cov3(t, func() { h.HandleLineWebhook(c) }) } func TestLINE_HandleWebhook_NilDB_Cov3(t *testing.T) { h := &LineWebhookHandler{} c, w := makeGinContext_Cov3("POST", "/webhooks/line/chan123", []byte(`{"events":[]}`), nil) safeCall_Cov3(t, func() { h.HandleLineWebhook(c) }) _ = w } func TestLINE_HandleVerification_Cov3(t *testing.T) { h := &LineWebhookHandler{} c, w := makeGinContext_Cov3("GET", "/webhooks/line/verify", nil, nil) h.HandleLineVerification(c) assert.Equal(t, http.StatusOK, w.Code) } func TestLINE_lookupInbox_NilDB_Cov3(t *testing.T) { h := &LineWebhookHandler{} safeCall_Cov3(t, func() { _, _ = h.lookupInbox(1) }) } func TestLINE_lookupInboxByLineChannelID_NilDB_Cov3(t *testing.T) { h := &LineWebhookHandler{} safeCall_Cov3(t, func() { _, _ = h.lookupInboxByLineChannelID("chan123") }) } func TestLINE_parseChannelConfig_Empty_Cov3(t *testing.T) { h := &LineWebhookHandler{} config := h.parseChannelConfig(stubInbox_Cov3("", "")) assert.NotNil(t, config) } func TestLINE_parseChannelConfig_BadJSON_Cov3(t *testing.T) { h := &LineWebhookHandler{} config := h.parseChannelConfig(stubInbox_Cov3("not json", "")) assert.NotNil(t, config) } // ============================================================ // Shopify Webhook // ============================================================ func TestShopify_HandleWebhook_BadBody_Cov3(t *testing.T) { h := &ShopifyWebhookHandler{db: nil, clientSecret: "secret"} c, _ := makeGinContext_Cov3("POST", "/webhooks/shopify", nil, nil) c.Request.Body = brokenBodyReader_Cov3{} safeCall_Cov3(t, func() { h.HandleShopifyWebhook(c) }) } func TestShopify_HandleWebhook_HMACFail_Cov3(t *testing.T) { h := &ShopifyWebhookHandler{db: nil, clientSecret: "secret"} c, w := makeGinContext_Cov3("POST", "/webhooks/shopify", []byte(`{"shop_domain":"test.myshopify.com"}`), map[string]string{ "X-Shopify-Hmac-SHA256": "invalid", }) c.Request.Header.Set("X-Shopify-Hmac-SHA256", "invalid") safeCall_Cov3(t, func() { h.HandleShopifyWebhook(c) }) // With a valid secret and bad signature, should return 401 _ = w } func TestShopify_HandleWebhook_NoSecret_Cov3(t *testing.T) { h := &ShopifyWebhookHandler{db: nil, clientSecret: ""} t.Setenv("SHOPIFY_CLIENT_SECRET", "") c, w := makeGinContext_Cov3("POST", "/webhooks/shopify", []byte(`{}`), nil) safeCall_Cov3(t, func() { h.HandleShopifyWebhook(c) }) _ = w } func TestShopify_verifyHMAC_NoSecret_Cov3(t *testing.T) { h := &ShopifyWebhookHandler{clientSecret: ""} t.Setenv("SHOPIFY_CLIENT_SECRET", "") err := h.verifyHMAC("sig", []byte("body")) assert.Error(t, err) } func TestShopify_verifyHMAC_EmptySig_Cov3(t *testing.T) { h := &ShopifyWebhookHandler{clientSecret: "secret"} err := h.verifyHMAC("", []byte("body")) assert.Error(t, err) } func TestShopify_verifyHMAC_BadSig_Cov3(t *testing.T) { h := &ShopifyWebhookHandler{clientSecret: "secret"} err := h.verifyHMAC("badsig", []byte("body")) assert.Error(t, err) } func TestShopify_deleteShopifyHooksByDomain_NilDB_Cov3(t *testing.T) { h := &ShopifyWebhookHandler{db: nil, clientSecret: "secret"} err := h.deleteShopifyHooksByDomain(nil, "test.myshopify.com") assert.Error(t, err) } func TestShopify_findShopifyHookByDomain_NilDB_Cov3(t *testing.T) { h := &ShopifyWebhookHandler{db: nil, clientSecret: "secret"} _, err := h.findShopifyHookByDomain(nil, "test.myshopify.com") assert.Error(t, err) } func TestShopify_shopifyHooks_NilDB_Cov3(t *testing.T) { h := &ShopifyWebhookHandler{db: nil, clientSecret: "secret"} _, err := h.shopifyHooks(nil, "test.myshopify.com") assert.Error(t, err) } // ============================================================ // Webhook Handler (webhook_handler.go) — Handle // ============================================================ func TestWebhookHandler_Handle_NilHandler_Cov3(t *testing.T) { var h *WebhookHandler safeCall_Cov3(t, func() { c, _ := makeGinContext_Cov3("POST", "/webhooks/test", []byte(`{}`), nil) h.Handle(c) }) } // ============================================================ // WhatsApp Webhook — constructor + delegates // ============================================================ func TestWhatsApp_NewHandler_Cov3(t *testing.T) { h := NewWhatsAppWebhookHandler(nil, nil, nil) assert.NotNil(t, h) } func TestWhatsApp_WithWorkerPool_Cov3(t *testing.T) { h := NewWhatsAppWebhookHandler(nil, nil, nil) result := h.WithWorkerPool(nil) assert.NotNil(t, result) } func TestWhatsApp_WithSearchIndexer_Cov3(t *testing.T) { h := NewWhatsAppWebhookHandler(nil, nil, nil) result := h.WithSearchIndexer(nil) assert.NotNil(t, result) } func TestWhatsApp_HandleVerification_Cov3(t *testing.T) { h := NewWhatsAppWebhookHandler(nil, nil, nil) c, w := makeGinContext_Cov3("GET", "/webhooks/whatsapp?hub.mode=subscribe&hub.verify_token=tok&hub.challenge=chal", nil, nil) safeCall_Cov3(t, func() { h.HandleWhatsAppVerification(c) }) _ = w } func TestWhatsApp_HandleWebhook_NilHandler_Cov3(t *testing.T) { var h *WhatsAppWebhookHandler safeCall_Cov3(t, func() { c, _ := makeGinContext_Cov3("POST", "/webhooks/whatsapp", []byte(`{}`), nil) h.HandleWhatsAppWebhook(c) }) } func TestWhatsApp_HandleWebhook_BadBody_Cov3(t *testing.T) { h := NewWhatsAppWebhookHandler(nil, nil, nil) c, _ := makeGinContext_Cov3("POST", "/webhooks/whatsapp", nil, nil) c.Request.Body = brokenBodyReader_Cov3{} safeCall_Cov3(t, func() { h.HandleWhatsAppWebhook(c) }) } // ============================================================ // IncomingPersister — SetWorkerPool, SetSearchIndexer, NewIncomingPersister // ============================================================ func TestIncomingPersister_SetWorkerPool_Nil_Cov3(t *testing.T) { p := NewIncomingPersister(nil) p.SetWorkerPool(nil) } func TestIncomingPersister_SetSearchIndexer_Nil_Cov3(t *testing.T) { p := NewIncomingPersister(nil) p.SetSearchIndexer(nil) } func TestIncomingPersister_NewWithDispatcher_Cov3(t *testing.T) { safeCall_Cov3(t, func() { p := NewIncomingPersister(nil, nil) _ = p }) } // ============================================================ // IncomingPersister Jobs helpers // ============================================================ func TestJobs_validProviderMessageStatus_Cov3(t *testing.T) { assert.True(t, validProviderMessageStatus("sent")) assert.False(t, validProviderMessageStatus("invalid")) } func TestJobs_validProviderMessageStatusTransition_Cov3(t *testing.T) { assert.True(t, validProviderMessageStatusTransition("sent", "delivered")) assert.False(t, validProviderMessageStatusTransition("delivered", "sent")) } func TestJobs_providerMessageStatusRank_Cov3(t *testing.T) { r1 := providerMessageStatusRank("sent") r2 := providerMessageStatusRank("delivered") assert.True(t, r2 > r1) } func TestJobs_setProviderMessageExternalError_Cov3(t *testing.T) { result := setProviderMessageExternalError(datatypes.JSON(nil), "", "err1") _ = result } func TestJobs_timeToUnixNano_Cov3(t *testing.T) { tm := time.Now() result := timeToUnixNano(&tm) assert.Equal(t, tm.UnixNano(), result) } func TestJobs_timeToUnixNano_Nil_Cov3(t *testing.T) { result := timeToUnixNano(nil) assert.Equal(t, int64(0), result) } func TestJobs_unixNanoToTime_Cov3(t *testing.T) { result := unixNanoToTime(0) assert.Nil(t, result) } func TestJobs_unixNanoToTime_Valid_Cov3(t *testing.T) { now := time.Now() result := unixNanoToTime(now.UnixNano()) assert.NotNil(t, result) } func TestJobs_incomingMessageQueue_Cov3(t *testing.T) { q := incomingMessageQueue(channel.ChannelType("test")) _ = q } // ============================================================ // IncomingPersister — validateIncomingMessage // ============================================================ func TestPersister_validateIncomingMessage_NilMsg_Cov3(t *testing.T) { p := NewIncomingPersister(nil) safeCall_Cov3(t, func() { _ = validateIncomingMessage(p, nil, nil) }) } // ============================================================ // Facebook constructor + WithWorkerPool + WithSearchIndexer // ============================================================ func TestFB_NewHandler_Cov3(t *testing.T) { h := NewFacebookWebhookHandler(nil, nil, nil) assert.NotNil(t, h) } func TestFB_WithWorkerPool_Cov3(t *testing.T) { h := NewFacebookWebhookHandler(nil, nil, nil) result := h.WithWorkerPool(nil) assert.NotNil(t, result) } func TestFB_WithSearchIndexer_Cov3(t *testing.T) { h := NewFacebookWebhookHandler(nil, nil, nil) result := h.WithSearchIndexer(nil) assert.NotNil(t, result) } // ============================================================ // Telegram constructor + WithWorkerPool + WithSearchIndexer // ============================================================ func TestTelegram_NewHandler_Cov3(t *testing.T) { h := NewTelegramWebhookHandler(nil, nil, nil) assert.NotNil(t, h) } func TestTelegram_WithWorkerPool_Cov3(t *testing.T) { h := NewTelegramWebhookHandler(nil, nil, nil) result := h.WithWorkerPool(nil) assert.NotNil(t, result) } func TestTelegram_WithSearchIndexer_Cov3(t *testing.T) { h := NewTelegramWebhookHandler(nil, nil, nil) result := h.WithSearchIndexer(nil) assert.NotNil(t, result) } // ============================================================ // TikTok constructor + WithWorkerPool + WithSearchIndexer // ============================================================ func TestTikTok_NewHandler_Cov3(t *testing.T) { h := NewTikTokWebhookHandler(nil, nil, nil) assert.NotNil(t, h) } func TestTikTok_WithWorkerPool_Cov3(t *testing.T) { h := NewTikTokWebhookHandler(nil, nil, nil) result := h.WithWorkerPool(nil) assert.NotNil(t, result) } func TestTikTok_WithSearchIndexer_Cov3(t *testing.T) { h := NewTikTokWebhookHandler(nil, nil, nil) result := h.WithSearchIndexer(nil) assert.NotNil(t, result) } // ============================================================ // Twilio constructor + WithWorkerPool + WithSearchIndexer // ============================================================ func TestTwilio_NewHandler_Cov3(t *testing.T) { h := NewTwilioWebhookHandler(nil, nil) assert.NotNil(t, h) } func TestTwilio_WithWorkerPool_Cov3(t *testing.T) { h := NewTwilioWebhookHandler(nil, nil) result := h.WithWorkerPool(nil) assert.NotNil(t, result) } func TestTwilio_WithSearchIndexer_Cov3(t *testing.T) { h := NewTwilioWebhookHandler(nil, nil) result := h.WithSearchIndexer(nil) assert.NotNil(t, result) } // ============================================================ // LINE constructor + WithWorkerPool + WithSearchIndexer // ============================================================ func TestLINE_NewHandler_Cov3(t *testing.T) { h := NewLineWebhookHandler(nil, nil, nil, nil) assert.NotNil(t, h) } func TestLINE_WithWorkerPool_Cov3(t *testing.T) { h := NewLineWebhookHandler(nil, nil, nil, nil) result := h.WithWorkerPool(nil) assert.NotNil(t, result) } func TestLINE_WithSearchIndexer_Cov3(t *testing.T) { h := NewLineWebhookHandler(nil, nil, nil, nil) result := h.WithSearchIndexer(nil) assert.NotNil(t, result) } // ============================================================ // Email constructor // ============================================================ func TestEmail_NewHandler_Cov3(t *testing.T) { h := NewEmailWebhookHandler(nil, nil, nil) assert.NotNil(t, h) } // ============================================================ // Shopify constructor // ============================================================ func TestShopify_NewHandler_Cov3(t *testing.T) { h := NewShopifyWebhookHandler(nil, "secret") assert.NotNil(t, h) } // ============================================================ // Helpers and stubs // ============================================================ // brokenBodyReader_Cov3 is an io.ReadCloser that always returns an error on Read. type brokenBodyReader_Cov3 struct{} func (brokenBodyReader_Cov3) Read(p []byte) (int, error) { return 0, assertError_Cov3 } func (brokenBodyReader_Cov3) Close() error { return nil } // stubInbox_Cov3 creates an Inbox with the given channel config JSON. func stubInbox_Cov3(configJSON string, _ string) *model.Inbox { return &model.Inbox{ ChannelConfig: configJSON, } } // assertError_Cov3 is a sentinel error for broken body reader. var assertError_Cov3 = &testError_Cov3{} type testError_Cov3 struct{} func (testError_Cov3) Error() string { return "read error" } // Ensure imports are used var _ = channel.ChannelType("test") var _ = datatypes.JSON(nil)