Files
gochat/backend/internal/handler/webhook/coverage3_test.go
T
Rogeeandrogee 2b182f9956 H-300: wire Captain Skills into Web runtime (#48)
* H-300: wire Captain Skills into Web runtime

* H-300: enforce effective model and conservative skill budget

* H-300: fix CI gosec step

* ci: extend golangci-lint timeout

* fix lint findings across backend

* fix(push): resolve delivery protocol blockers

* test(repository): close SQLite test databases

* test(repository): reuse SQLite schema per package

* H-307: restore backend Go cache in CI

* H-307: prefetch modules before cold lint

* H-307: resolve govulncheck security gate

* H-307: build lint with patched Go toolchain

* H-307: clear remaining security scan findings

---------

Co-authored-by: Rogee <rogee@ipao.vip>
2026-08-19 07:08:14 +08:00

1103 lines
34 KiB
Go

package webhook
import (
"bytes"
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"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(context.Background(), 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{}
require.NoError(t, 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(context.Background(), "test.myshopify.com")
assert.Error(t, err)
}
func TestShopify_findShopifyHookByDomain_NilDB_Cov3(t *testing.T) {
h := &ShopifyWebhookHandler{db: nil, clientSecret: "secret"}
_, err := h.findShopifyHookByDomain(context.Background(), "test.myshopify.com")
assert.Error(t, err)
}
func TestShopify_shopifyHooks_NilDB_Cov3(t *testing.T) {
h := &ShopifyWebhookHandler{db: nil, clientSecret: "secret"}
_, err := h.shopifyHooks(context.Background(), "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)