Files
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

2324 lines
81 KiB
Go

package facebook
import (
"context"
"net/http"
"net/http/httptest"
"testing"
"github.com/gochat/gochat/internal/channel"
"github.com/gochat/gochat/internal/model"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func init() {
// Ensure gin test mode (some sub-packages use gin)
}
// ===========================
// FacebookProvider — identity & metadata (Cov7)
// ===========================
func TestFBProvider_New_Cov7(t *testing.T) {
p := NewFacebookProvider()
require.NotNil(t, p)
assert.NotNil(t, p.client)
assert.NotEmpty(t, p.graphAPIBase)
assert.NotEmpty(t, p.storagePath)
}
func TestFBProvider_Type_Cov7(t *testing.T) {
p := NewFacebookProvider()
assert.Equal(t, channel.ChannelFacebook, p.Type())
}
func TestFBProvider_Name_Cov7(t *testing.T) {
p := NewFacebookProvider()
assert.Equal(t, "Facebook Messenger", p.Name())
}
func TestFBProvider_Description_Cov7(t *testing.T) {
p := NewFacebookProvider()
assert.NotEmpty(t, p.Description())
}
func TestFBProvider_ConfigSchema_Cov7(t *testing.T) {
p := NewFacebookProvider()
schema := p.ConfigSchema()
require.NotNil(t, schema)
assert.Equal(t, "object", schema.Type)
assert.Contains(t, schema.Required, "page_id")
assert.Contains(t, schema.Required, "page_access_token")
}
func TestFBProvider_DefaultConfig_Cov7(t *testing.T) {
p := NewFacebookProvider()
cfg := p.DefaultConfig()
assert.Contains(t, cfg, "page_id")
assert.Contains(t, cfg, "page_access_token")
}
func TestFBProvider_Capabilities_Cov7(t *testing.T) {
p := NewFacebookProvider()
caps := p.Capabilities()
assert.True(t, caps.SupportsAttachments)
assert.True(t, caps.SupportsLocation)
assert.True(t, caps.SupportsTypingIndicator)
assert.True(t, caps.SupportsDeliveryStatus)
assert.True(t, caps.SupportsTemplates)
assert.Equal(t, 2000, caps.MaxTextLength)
}
func TestFBProvider_OAuthConfig_Cov7(t *testing.T) {
p := NewFacebookProvider()
oauth := p.OAuthConfig()
require.NotNil(t, oauth)
assert.Equal(t, "facebook", oauth.Provider)
assert.Contains(t, oauth.Scopes, "pages_messaging")
assert.True(t, oauth.RequiresRefresh)
}
// ===========================
// FacebookProvider — ValidateConfig (Cov7)
// ===========================
func TestFBProvider_ValidateConfig_NoPageID_Cov7(t *testing.T) {
p := NewFacebookProvider()
err := p.ValidateConfig(context.Background(), channel.ChannelConfig{})
require.Error(t, err)
assert.Contains(t, err.Error(), "page_id")
}
func TestFBProvider_ValidateConfig_NoToken_Cov7(t *testing.T) {
p := NewFacebookProvider()
err := p.ValidateConfig(context.Background(), channel.ChannelConfig{
"page_id": "12345",
})
require.Error(t, err)
assert.Contains(t, err.Error(), "page_access_token")
}
func TestFBProvider_ValidateConfig_APIError_Cov7(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusUnauthorized)
}))
defer srv.Close()
p := NewFacebookProvider()
p.graphAPIBase = srv.URL
err := p.ValidateConfig(context.Background(), channel.ChannelConfig{
"page_id": "12345",
"page_access_token": "test_token",
})
require.Error(t, err)
}
func TestFBProvider_ValidateConfig_Success_Cov7(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
mustEncodeTestJSON(w, FBPageInfo{ID: "12345", Name: "Test Page"})
}))
defer srv.Close()
p := NewFacebookProvider()
p.graphAPIBase = srv.URL
err := p.ValidateConfig(context.Background(), channel.ChannelConfig{
"page_id": "12345",
"page_access_token": "test_token",
})
require.NoError(t, err)
}
func TestFBProvider_ValidateConfig_PageIDMismatch_Cov7(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
mustEncodeTestJSON(w, FBPageInfo{ID: "99999", Name: "Other Page"})
}))
defer srv.Close()
p := NewFacebookProvider()
p.graphAPIBase = srv.URL
err := p.ValidateConfig(context.Background(), channel.ChannelConfig{
"page_id": "12345",
"page_access_token": "test_token",
})
require.Error(t, err)
assert.Contains(t, err.Error(), "mismatch")
}
// ===========================
// FacebookProvider — OnCreate / OnDestroy (Cov7)
// ===========================
func TestFBProvider_OnCreate_Cov7(t *testing.T) {
t.Skip("network test")
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
mustEncodeTestJSON(w, FBPageInfo{ID: "12345", Name: "Test Page"})
}))
defer srv.Close()
p := NewFacebookProvider()
p.graphAPIBase = srv.URL
inbox := &model.Inbox{Base: model.Base{ID: 1}, AccountID: 10}
config := channel.ChannelConfig{
"page_id": "12345",
"page_access_token": "test_token",
}
result, err := p.OnCreate(context.Background(), inbox, config)
require.NoError(t, err)
assert.NotNil(t, result)
assert.Equal(t, "Test Page", result["page_name"])
}
func TestFBProvider_OnCreate_NoAppID_Cov7(t *testing.T) {
t.Skip("network test")
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
mustEncodeTestJSON(w, FBPageInfo{ID: "12345", Name: "Test Page"})
}))
defer srv.Close()
p := NewFacebookProvider()
p.graphAPIBase = srv.URL
p.appID = ""
inbox := &model.Inbox{Base: model.Base{ID: 1}, AccountID: 10}
config := channel.ChannelConfig{
"page_id": "12345",
"page_access_token": "test_token",
}
result, err := p.OnCreate(context.Background(), inbox, config)
require.NoError(t, err)
assert.NotNil(t, result)
}
func TestFBProvider_OnDestroy_Cov7(t *testing.T) {
t.Skip("network test")
p := NewFacebookProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}}
config := channel.ChannelConfig{
"page_id": "12345",
"page_access_token": "test_token",
"app_id": "test_app",
}
err := p.OnDestroy(context.Background(), inbox, config)
require.NoError(t, err)
}
func TestFBProvider_OnDestroy_NoAppID_Cov7(t *testing.T) {
t.Skip("network test")
p := NewFacebookProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}}
config := channel.ChannelConfig{
"page_id": "12345",
"page_access_token": "test_token",
}
err := p.OnDestroy(context.Background(), inbox, config)
require.NoError(t, err)
}
// ===========================
// FacebookProvider — ProcessIncoming (Cov7)
// ===========================
func TestFBProvider_ProcessIncoming_InvalidJSON_Cov7(t *testing.T) {
p := NewFacebookProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, AccountID: 10}
_, err := p.ProcessIncoming(context.Background(), inbox, []byte("invalid json"))
require.Error(t, err)
}
func TestFBProvider_ProcessIncoming_NotPageObject_Cov7(t *testing.T) {
p := NewFacebookProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, AccountID: 10}
payload := `{"object":"instagram","entry":[]}`
_, err := p.ProcessIncoming(context.Background(), inbox, []byte(payload))
require.Error(t, err)
assert.Contains(t, err.Error(), "not a page event")
}
func TestFBProvider_ProcessIncoming_NoContent_Cov7(t *testing.T) {
p := NewFacebookProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, AccountID: 10}
payload := `{"object":"page","entry":[{"id":"123","time":1234567890,"messaging":[]}]}`
_, err := p.ProcessIncoming(context.Background(), inbox, []byte(payload))
require.Error(t, err)
assert.Contains(t, err.Error(), "no processable")
}
func TestFBProvider_ProcessIncoming_EchoMessage_Cov7(t *testing.T) {
p := NewFacebookProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, AccountID: 10}
payload := `{"object":"page","entry":[{"id":"123","time":1234567890,"messaging":[{"sender":{"id":"s1"},"recipient":{"id":"r1"},"timestamp":1234567890,"message":{"mid":"m1","text":"hello","is_echo":true}}]}]}`
_, err := p.ProcessIncoming(context.Background(), inbox, []byte(payload))
require.Error(t, err)
assert.Contains(t, err.Error(), "no processable")
}
func TestFBProvider_ProcessIncoming_TextMessage_Cov7(t *testing.T) {
p := NewFacebookProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, AccountID: 10}
payload := `{"object":"page","entry":[{"id":"123","time":1234567890,"messaging":[{"sender":{"id":"s1"},"recipient":{"id":"r1"},"timestamp":1234567890,"message":{"mid":"m1","text":"hello world"}}]}]}`
msg, err := p.ProcessIncoming(context.Background(), inbox, []byte(payload))
require.NoError(t, err)
assert.Equal(t, "hello world", msg.Content)
assert.Equal(t, "m1", msg.SourceID)
}
func TestFBProvider_ProcessIncoming_Postback_Cov7(t *testing.T) {
p := NewFacebookProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, AccountID: 10}
payload := `{"object":"page","entry":[{"id":"123","time":1234567890,"messaging":[{"sender":{"id":"s1"},"recipient":{"id":"r1"},"timestamp":1234567890,"postback":{"title":"Get Started","payload":"START"}}]}]}`
msg, err := p.ProcessIncoming(context.Background(), inbox, []byte(payload))
require.NoError(t, err)
assert.Equal(t, "START", msg.Content)
assert.Contains(t, msg.Extra, "postback_title")
}
func TestFBProvider_ProcessIncoming_Optin_Cov7(t *testing.T) {
p := NewFacebookProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, AccountID: 10}
payload := `{"object":"page","entry":[{"id":"123","time":1234567890,"messaging":[{"sender":{"id":"s1"},"recipient":{"id":"r1"},"timestamp":1234567890,"optin":{"ref":"m_me_ref"}}]}]}`
msg, err := p.ProcessIncoming(context.Background(), inbox, []byte(payload))
require.NoError(t, err)
assert.Equal(t, "m_me_ref", msg.Content)
assert.Contains(t, msg.Extra, "optin_ref")
}
func TestFBProvider_ProcessIncoming_ImageAttachment_Cov7(t *testing.T) {
p := NewFacebookProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, AccountID: 10}
payload := `{"object":"page","entry":[{"id":"123","time":1234567890,"messaging":[{"sender":{"id":"s1"},"recipient":{"id":"r1"},"timestamp":1234567890,"message":{"mid":"m1","attachments":[{"type":"image","payload":{"url":"https://example.com/img.jpg"}}]}}]}]}`
msg, err := p.ProcessIncoming(context.Background(), inbox, []byte(payload))
require.NoError(t, err)
assert.Equal(t, "https://example.com/img.jpg", msg.Content)
assert.Len(t, msg.Attachments, 1)
}
func TestFBProvider_ProcessIncoming_VideoAttachment_Cov7(t *testing.T) {
p := NewFacebookProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, AccountID: 10}
payload := `{"object":"page","entry":[{"id":"123","time":1234567890,"messaging":[{"sender":{"id":"s1"},"recipient":{"id":"r1"},"timestamp":1234567890,"message":{"mid":"m1","attachments":[{"type":"video","payload":{"url":"https://example.com/vid.mp4"}}]}}]}]}`
msg, err := p.ProcessIncoming(context.Background(), inbox, []byte(payload))
require.NoError(t, err)
assert.Contains(t, msg.Content, "vid.mp4")
}
func TestFBProvider_ProcessIncoming_AudioAttachment_Cov7(t *testing.T) {
p := NewFacebookProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, AccountID: 10}
payload := `{"object":"page","entry":[{"id":"123","time":1234567890,"messaging":[{"sender":{"id":"s1"},"recipient":{"id":"r1"},"timestamp":1234567890,"message":{"mid":"m1","attachments":[{"type":"audio","payload":{"url":"https://example.com/audio.mp3"}}]}}]}]}`
msg, err := p.ProcessIncoming(context.Background(), inbox, []byte(payload))
require.NoError(t, err)
assert.Contains(t, msg.Content, "audio.mp3")
}
func TestFBProvider_ProcessIncoming_FileAttachment_Cov7(t *testing.T) {
p := NewFacebookProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, AccountID: 10}
payload := `{"object":"page","entry":[{"id":"123","time":1234567890,"messaging":[{"sender":{"id":"s1"},"recipient":{"id":"r1"},"timestamp":1234567890,"message":{"mid":"m1","attachments":[{"type":"file","payload":{"url":"https://example.com/doc.pdf"}}]}}]}]}`
msg, err := p.ProcessIncoming(context.Background(), inbox, []byte(payload))
require.NoError(t, err)
assert.Contains(t, msg.Content, "doc.pdf")
}
func TestFBProvider_ProcessIncoming_LocationAttachment_Cov7(t *testing.T) {
p := NewFacebookProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, AccountID: 10}
payload := `{"object":"page","entry":[{"id":"123","time":1234567890,"messaging":[{"sender":{"id":"s1"},"recipient":{"id":"r1"},"timestamp":1234567890,"message":{"mid":"m1","attachments":[{"type":"location","payload":{"url":"https://maps.example.com"}}]}}]}]}`
msg, err := p.ProcessIncoming(context.Background(), inbox, []byte(payload))
require.NoError(t, err)
assert.Contains(t, msg.Content, "maps.example.com")
}
func TestFBProvider_ProcessIncoming_FallbackAttachment_Cov7(t *testing.T) {
p := NewFacebookProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, AccountID: 10}
payload := `{"object":"page","entry":[{"id":"123","time":1234567890,"messaging":[{"sender":{"id":"s1"},"recipient":{"id":"r1"},"timestamp":1234567890,"message":{"mid":"m1","attachments":[{"type":"fallback","payload":{"name":"fallback_name"}}]}}]}]}`
msg, err := p.ProcessIncoming(context.Background(), inbox, []byte(payload))
require.NoError(t, err)
assert.Equal(t, "fallback_name", msg.Content)
}
func TestFBProvider_ProcessIncoming_UnknownAttachment_Cov7(t *testing.T) {
p := NewFacebookProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, AccountID: 10}
payload := `{"object":"page","entry":[{"id":"123","time":1234567890,"messaging":[{"sender":{"id":"s1"},"recipient":{"id":"r1"},"timestamp":1234567890,"message":{"mid":"m1","attachments":[{"type":"unknown_type","payload":{"url":"https://example.com/unknown"}}]}}]}]}`
msg, err := p.ProcessIncoming(context.Background(), inbox, []byte(payload))
require.NoError(t, err)
assert.Contains(t, msg.Content, "unknown")
}
func TestFBProvider_ProcessIncoming_TextAndAttachment_Cov7(t *testing.T) {
p := NewFacebookProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, AccountID: 10}
payload := `{"object":"page","entry":[{"id":"123","time":1234567890,"messaging":[{"sender":{"id":"s1"},"recipient":{"id":"r1"},"timestamp":1234567890,"message":{"mid":"m1","text":"check this out","attachments":[{"type":"image","payload":{"url":"https://example.com/img.jpg"}}]}}]}]}`
msg, err := p.ProcessIncoming(context.Background(), inbox, []byte(payload))
require.NoError(t, err)
assert.Contains(t, msg.Extra, "text")
}
// ===========================
// FacebookProvider — ValidateWebhookRequest (Cov7)
// ===========================
func TestFBProvider_ValidateWebhookRequest_GET_Valid_Cov7(t *testing.T) {
p := NewFacebookProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: `{"webhook_verify_token":"my_token"}`}
req := &channel.WebhookRequest{
Method: "GET",
QueryParams: map[string]string{"hub.mode": "subscribe", "hub.verify_token": "my_token"},
}
err := p.ValidateWebhookRequest(context.Background(), inbox, req)
require.NoError(t, err)
}
func TestFBProvider_ValidateWebhookRequest_GET_InvalidMode_Cov7(t *testing.T) {
p := NewFacebookProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: `{}`}
req := &channel.WebhookRequest{
Method: "GET",
QueryParams: map[string]string{"hub.mode": "invalid", "hub.verify_token": "tok"},
}
err := p.ValidateWebhookRequest(context.Background(), inbox, req)
require.Error(t, err)
assert.Contains(t, err.Error(), "hub.mode")
}
func TestFBProvider_ValidateWebhookRequest_GET_TokenMismatch_Cov7(t *testing.T) {
p := NewFacebookProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: `{"webhook_verify_token":"correct_token"}`}
req := &channel.WebhookRequest{
Method: "GET",
QueryParams: map[string]string{"hub.mode": "subscribe", "hub.verify_token": "wrong_token"},
}
err := p.ValidateWebhookRequest(context.Background(), inbox, req)
require.Error(t, err)
assert.Contains(t, err.Error(), "mismatch")
}
func TestFBProvider_ValidateWebhookRequest_POST_NoSignature_Cov7(t *testing.T) {
p := NewFacebookProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: `{}`}
req := &channel.WebhookRequest{
Method: "POST",
Headers: map[string]string{},
Body: []byte(`{}`),
}
err := p.ValidateWebhookRequest(context.Background(), inbox, req)
require.Error(t, err)
assert.Contains(t, err.Error(), "X-Hub-Signature-256")
}
func TestFBProvider_ValidateWebhookRequest_POST_NoAppSecret_Cov7(t *testing.T) {
p := NewFacebookProvider()
p.appSecret = ""
inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: `{}`}
req := &channel.WebhookRequest{
Method: "POST",
Headers: map[string]string{"X-Hub-Signature-256": "sha256=abc"},
Body: []byte(`{}`),
}
err := p.ValidateWebhookRequest(context.Background(), inbox, req)
require.NoError(t, err) // skips verification when app secret not set
}
func TestFBProvider_ValidateWebhookRequest_POST_BadSignature_Cov7(t *testing.T) {
p := NewFacebookProvider()
p.appSecret = "my_secret"
inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: `{}`}
req := &channel.WebhookRequest{
Method: "POST",
Headers: map[string]string{"X-Hub-Signature-256": "sha256=bad_signature"},
Body: []byte(`{"test":true}`),
}
err := p.ValidateWebhookRequest(context.Background(), inbox, req)
require.Error(t, err)
assert.Contains(t, err.Error(), "signature")
}
// ===========================
// FacebookProvider — SendMessage (Cov7)
// ===========================
func TestFBProvider_SendMessage_NoPSID_Cov7(t *testing.T) {
t.Skip("network test")
p := NewFacebookProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}}
msg := &model.Message{Base: model.Base{ID: 1}, Content: "hello", ContentType: "text"}
contact := &model.Contact{Base: model.Base{ID: 1}, Identifier: ""}
_, err := p.SendMessage(context.Background(), inbox, msg, contact)
require.Error(t, err)
assert.Contains(t, err.Error(), "PSID")
}
func TestFBProvider_SendMessage_NoToken_Cov7(t *testing.T) {
t.Skip("network test")
p := NewFacebookProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: `{}`}
msg := &model.Message{Base: model.Base{ID: 1}, Content: "hello", ContentType: "text"}
contact := &model.Contact{Base: model.Base{ID: 1}, Identifier: "12345"}
_, err := p.SendMessage(context.Background(), inbox, msg, contact)
require.Error(t, err)
assert.Contains(t, err.Error(), "access token")
}
func TestFBProvider_SendMessage_TextSuccess_Cov7(t *testing.T) {
t.Skip("network test")
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
mustEncodeTestJSON(w, FBSendAPIResponse{RecipientID: "12345", MessageID: "mid:123"})
}))
defer srv.Close()
p := NewFacebookProvider()
p.graphAPIBase = srv.URL
inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: `{"page_access_token":"tok"}`}
msg := &model.Message{Base: model.Base{ID: 1}, Content: "hello", ContentType: "text"}
contact := &model.Contact{Base: model.Base{ID: 1}, Identifier: "12345"}
result, err := p.SendMessage(context.Background(), inbox, msg, contact)
require.NoError(t, err)
assert.Equal(t, "mid:123", result.ExternalID)
}
func TestFBProvider_SendMessage_AttachmentSuccess_Cov7(t *testing.T) {
t.Skip("network test")
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
mustEncodeTestJSON(w, FBSendAPIResponse{RecipientID: "12345", MessageID: "mid:456"})
}))
defer srv.Close()
p := NewFacebookProvider()
p.graphAPIBase = srv.URL
inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: `{"page_access_token":"tok"}`}
msg := &model.Message{Base: model.Base{ID: 1}, Content: "https://example.com/img.jpg", ContentType: "image"}
contact := &model.Contact{Base: model.Base{ID: 1}, Identifier: "12345"}
result, err := p.SendMessage(context.Background(), inbox, msg, contact)
require.NoError(t, err)
assert.Equal(t, "mid:456", result.ExternalID)
}
func TestFBProvider_SendMessage_APIError_Cov7(t *testing.T) {
t.Skip("network test")
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
}))
defer srv.Close()
p := NewFacebookProvider()
p.graphAPIBase = srv.URL
inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: `{"page_access_token":"tok"}`}
msg := &model.Message{Base: model.Base{ID: 1}, Content: "hello", ContentType: "text"}
contact := &model.Contact{Base: model.Base{ID: 1}, Identifier: "12345"}
_, err := p.SendMessage(context.Background(), inbox, msg, contact)
require.Error(t, err)
}
// ===========================
// FacebookProvider — GetContactProfile (Cov7)
// ===========================
func TestFBProvider_GetContactProfile_NoToken_Cov7(t *testing.T) {
t.Skip("network test")
p := NewFacebookProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: `{}`}
_, err := p.GetContactProfile(context.Background(), inbox, "user123")
require.Error(t, err)
assert.Contains(t, err.Error(), "access token")
}
func TestFBProvider_GetContactProfile_Success_Cov7(t *testing.T) {
t.Skip("network test")
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
mustEncodeTestJSON(w, FBUserProfile{ID: "123", Name: "John Doe", FirstName: "John", LastName: "Doe"})
}))
defer srv.Close()
p := NewFacebookProvider()
p.graphAPIBase = srv.URL
inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: `{"page_access_token":"tok"}`}
profile, err := p.GetContactProfile(context.Background(), inbox, "123")
require.NoError(t, err)
assert.Equal(t, "John Doe", profile.Name)
}
func TestFBProvider_GetContactProfile_APIError_Cov7(t *testing.T) {
t.Skip("network test")
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
}))
defer srv.Close()
p := NewFacebookProvider()
p.graphAPIBase = srv.URL
inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: `{"page_access_token":"tok"}`}
_, err := p.GetContactProfile(context.Background(), inbox, "123")
require.Error(t, err)
}
// ===========================
// FacebookProvider — OAuth methods (Cov7)
// ===========================
func TestFBProvider_BuildAuthURL_NoAppID_Cov7(t *testing.T) {
p := NewFacebookProvider()
p.appID = ""
_, err := p.BuildAuthURL(context.Background(), 1, "https://example.com/callback")
require.Error(t, err)
assert.Contains(t, err.Error(), "App ID")
}
func TestFBProvider_BuildAuthURL_Success_Cov7(t *testing.T) {
p := NewFacebookProvider()
p.appID = "test_app_id"
url, err := p.BuildAuthURL(context.Background(), 1, "https://example.com/callback")
require.NoError(t, err)
assert.Contains(t, url, "test_app_id")
}
func TestFBProvider_ExchangeToken_NoConfig_Cov7(t *testing.T) {
t.Skip("network test")
p := NewFacebookProvider()
p.appID = ""
p.appSecret = ""
_, err := p.ExchangeToken(context.Background(), "code", "https://example.com/callback")
require.Error(t, err)
assert.Contains(t, err.Error(), "not configured")
}
func TestFBProvider_ExchangeToken_Success_Cov7(t *testing.T) {
t.Skip("network test")
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
mustEncodeTestJSON(w, FBLongLivedTokenResponse{AccessToken: "long_token", TokenType: "bearer", ExpiresIn: 5184000})
}))
defer srv.Close()
p := NewFacebookProvider()
p.graphAPIBase = srv.URL
p.appID = "test_app"
p.appSecret = "test_secret"
result, err := p.ExchangeToken(context.Background(), "code", "https://example.com/callback")
require.NoError(t, err)
assert.Equal(t, "long_token", result.AccessToken)
}
func TestFBProvider_ExchangeToken_APIError_Cov7(t *testing.T) {
t.Skip("network test")
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusBadRequest)
}))
defer srv.Close()
p := NewFacebookProvider()
p.graphAPIBase = srv.URL
p.appID = "test_app"
p.appSecret = "test_secret"
_, err := p.ExchangeToken(context.Background(), "code", "https://example.com/callback")
require.Error(t, err)
}
func TestFBProvider_ExchangeLongLivedUserToken_Empty_Cov7(t *testing.T) {
p := NewFacebookProvider()
_, err := p.ExchangeLongLivedUserToken(context.Background(), "")
require.Error(t, err)
assert.Contains(t, err.Error(), "required")
}
func TestFBProvider_ExchangeLongLivedUserToken_Success_Cov7(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
mustEncodeTestJSON(w, FBLongLivedTokenResponse{AccessToken: "long_token"})
}))
defer srv.Close()
p := NewFacebookProvider()
p.graphAPIBase = srv.URL
p.appID = "test_app"
p.appSecret = "test_secret"
token, err := p.ExchangeLongLivedUserToken(context.Background(), "short_token")
require.NoError(t, err)
assert.Equal(t, "long_token", token)
}
func TestFBProvider_ExchangeLongLivedUserToken_EmptyResult_Cov7(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
mustEncodeTestJSON(w, FBLongLivedTokenResponse{AccessToken: ""})
}))
defer srv.Close()
p := NewFacebookProvider()
p.graphAPIBase = srv.URL
p.appID = "test_app"
p.appSecret = "test_secret"
_, err := p.ExchangeLongLivedUserToken(context.Background(), "short_token")
require.Error(t, err)
assert.Contains(t, err.Error(), "empty")
}
func TestFBProvider_ListFacebookPages_EmptyToken_Cov7(t *testing.T) {
t.Skip("network test")
p := NewFacebookProvider()
_, err := p.ListFacebookPages(context.Background(), "")
require.Error(t, err)
}
func TestFBProvider_ListFacebookPages_Success_Cov7(t *testing.T) {
t.Skip("network test")
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
if _, err := w.Write([]byte(`{"data":[{"id":"1","name":"Page1"}],"paging":{}}`)); err != nil {
panic(err)
}
}))
defer srv.Close()
p := NewFacebookProvider()
p.graphAPIBase = srv.URL
pages, err := p.ListFacebookPages(context.Background(), "user_token")
require.NoError(t, err)
assert.Len(t, pages, 1)
}
func TestFBProvider_ListFacebookPages_APIError_Cov7(t *testing.T) {
t.Skip("network test")
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusUnauthorized)
}))
defer srv.Close()
p := NewFacebookProvider()
p.graphAPIBase = srv.URL
_, err := p.ListFacebookPages(context.Background(), "user_token")
require.Error(t, err)
}
func TestFBProvider_FetchInstagramBusinessAccountID_EmptyToken_Cov7(t *testing.T) {
t.Skip("network test")
p := NewFacebookProvider()
id, err := p.FetchInstagramBusinessAccountID(context.Background(), "")
require.NoError(t, err) // returns empty without error
assert.Empty(t, id)
}
func TestFBProvider_FetchInstagramBusinessAccountID_Success_Cov7(t *testing.T) {
t.Skip("network test")
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
if _, err := w.Write([]byte(`{"id":"1","instagram_business_account":{"id":"ig_123"}}`)); err != nil {
panic(err)
}
}))
defer srv.Close()
p := NewFacebookProvider()
p.graphAPIBase = srv.URL
id, err := p.FetchInstagramBusinessAccountID(context.Background(), "page_token")
require.NoError(t, err)
assert.Equal(t, "ig_123", id)
}
func TestFBProvider_FetchInstagramBusinessAccountID_NoIGAccount_Cov7(t *testing.T) {
t.Skip("network test")
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
if _, err := w.Write([]byte(`{"id":"1"}`)); err != nil {
panic(err)
}
}))
defer srv.Close()
p := NewFacebookProvider()
p.graphAPIBase = srv.URL
id, err := p.FetchInstagramBusinessAccountID(context.Background(), "page_token")
require.NoError(t, err)
assert.Empty(t, id)
}
func TestFBProvider_RefreshToken_NoToken_Cov7(t *testing.T) {
t.Skip("network test")
p := NewFacebookProvider()
_, err := p.RefreshToken(context.Background(), &model.Inbox{Base: model.Base{ID: 1}}, channel.ChannelConfig{})
require.Error(t, err)
assert.Contains(t, err.Error(), "no current")
}
func TestFBProvider_RefreshToken_Success_Cov7(t *testing.T) {
t.Skip("network test")
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
mustEncodeTestJSON(w, FBLongLivedTokenResponse{AccessToken: "new_token", ExpiresIn: 5184000})
}))
defer srv.Close()
p := NewFacebookProvider()
p.graphAPIBase = srv.URL
result, err := p.RefreshToken(context.Background(), &model.Inbox{Base: model.Base{ID: 1}}, channel.ChannelConfig{
"page_access_token": "old_token",
})
require.NoError(t, err)
assert.Equal(t, "new_token", result.AccessToken)
}
func TestFBProvider_RefreshToken_APIError_Cov7(t *testing.T) {
t.Skip("network test")
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusUnauthorized)
}))
defer srv.Close()
p := NewFacebookProvider()
p.graphAPIBase = srv.URL
_, err := p.RefreshToken(context.Background(), &model.Inbox{Base: model.Base{ID: 1}}, channel.ChannelConfig{
"page_access_token": "old_token",
})
require.Error(t, err)
}
func TestFBProvider_CheckAuthorizationError_Nil_Cov7(t *testing.T) {
p := NewFacebookProvider()
assert.False(t, p.CheckAuthorizationError(context.Background(), nil))
}
func TestFBProvider_CheckAuthorizationError_Code190_Cov7(t *testing.T) {
p := NewFacebookProvider()
assert.True(t, p.CheckAuthorizationError(context.Background(), errFake("code 190")))
}
func TestFBProvider_CheckAuthorizationError_OAuthException_Cov7(t *testing.T) {
p := NewFacebookProvider()
assert.True(t, p.CheckAuthorizationError(context.Background(), errFake("OAuthException")))
}
func TestFBProvider_CheckAuthorizationError_Subcode463_Cov7(t *testing.T) {
p := NewFacebookProvider()
assert.True(t, p.CheckAuthorizationError(context.Background(), errFake("error_subcode 463")))
}
func TestFBProvider_CheckAuthorizationError_Subcode467_Cov7(t *testing.T) {
p := NewFacebookProvider()
assert.True(t, p.CheckAuthorizationError(context.Background(), errFake("error_subcode 467")))
}
func TestFBProvider_CheckAuthorizationError_Other_Cov7(t *testing.T) {
p := NewFacebookProvider()
assert.False(t, p.CheckAuthorizationError(context.Background(), errFake("some other error")))
}
func TestFBProvider_OnReauthorization_Cov7(t *testing.T) {
p := NewFacebookProvider()
err := p.OnReauthorization(context.Background(), &model.Inbox{Base: model.Base{ID: 1}})
require.NoError(t, err)
}
// ===========================
// FacebookProvider — Convenience methods (Cov7)
// ===========================
func TestFBProvider_CreateChannel_Success_Cov7(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
mustEncodeTestJSON(w, FBPageInfo{ID: "12345", Name: "Test Page"})
}))
defer srv.Close()
p := NewFacebookProvider()
p.graphAPIBase = srv.URL
ch, err := p.CreateChannel(context.Background(), 10, channel.ChannelConfig{
"page_id": "12345",
"page_access_token": "tok",
})
require.NoError(t, err)
assert.NotNil(t, ch)
}
func TestFBProvider_CreateChannel_APIError_Cov7(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusUnauthorized)
}))
defer srv.Close()
p := NewFacebookProvider()
p.graphAPIBase = srv.URL
_, err := p.CreateChannel(context.Background(), 10, channel.ChannelConfig{
"page_id": "12345",
"page_access_token": "tok",
})
require.Error(t, err)
}
func TestFBProvider_CreateChannel_PageIDMismatch_Cov7(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
mustEncodeTestJSON(w, FBPageInfo{ID: "99999", Name: "Other"})
}))
defer srv.Close()
p := NewFacebookProvider()
p.graphAPIBase = srv.URL
_, err := p.CreateChannel(context.Background(), 10, channel.ChannelConfig{
"page_id": "12345",
"page_access_token": "tok",
})
require.Error(t, err)
assert.Contains(t, err.Error(), "mismatch")
}
func TestFBProvider_UpdateChannel_Cov7(t *testing.T) {
p := NewFacebookProvider()
ch, err := p.UpdateChannel(context.Background(), 1, channel.ChannelConfig{
"page_access_token": "new_tok",
"page_name": "New Name",
})
require.NoError(t, err)
assert.NotNil(t, ch)
}
func TestFBProvider_DeleteChannel_Cov7(t *testing.T) {
p := NewFacebookProvider()
err := p.DeleteChannel(context.Background(), 1)
require.NoError(t, err)
}
func TestFBProvider_HandleWebhook_Page_Cov7(t *testing.T) {
p := NewFacebookProvider()
err := p.HandleWebhook(context.Background(), map[string]interface{}{
"object": "page",
"entry": []interface{}{},
})
require.NoError(t, err)
}
func TestFBProvider_HandleWebhook_NonPage_Cov7(t *testing.T) {
p := NewFacebookProvider()
err := p.HandleWebhook(context.Background(), map[string]interface{}{
"object": "instagram",
"entry": []interface{}{},
})
require.NoError(t, err)
}
func TestFBProvider_HandleWebhook_BadPayload_Cov7(t *testing.T) {
p := NewFacebookProvider()
err := p.HandleWebhook(context.Background(), map[string]interface{}{
"object": func() {},
})
require.Error(t, err)
}
func TestFBProvider_ProcessIncomingMessage_Cov7(t *testing.T) {
p := NewFacebookProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, AccountID: 10}
payload := map[string]interface{}{
"object": "page",
"entry": []interface{}{
map[string]interface{}{
"id": "123",
"time": int64(1234567890),
"messaging": []interface{}{
map[string]interface{}{
"sender": map[string]interface{}{"id": "s1"},
"recipient": map[string]interface{}{"id": "r1"},
"timestamp": int64(1234567890),
"message": map[string]interface{}{"mid": "m1", "text": "hello"},
},
},
},
},
}
msg, err := p.ProcessIncomingMessage(context.Background(), inbox, payload)
require.NoError(t, err)
assert.Equal(t, "hello", msg.Content)
}
// ===========================
// FacebookProvider — Sender actions (Cov7)
// ===========================
func TestFBProvider_SendTypingOn_Cov7(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
if _, err := w.Write([]byte(`{"success":true}`)); err != nil {
panic(err)
}
}))
defer srv.Close()
p := NewFacebookProvider()
p.graphAPIBase = srv.URL
err := p.SendTypingOn(context.Background(), "12345", "tok")
require.NoError(t, err)
}
func TestFBProvider_SendTypingOff_Cov7(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
if _, err := w.Write([]byte(`{"success":true}`)); err != nil {
panic(err)
}
}))
defer srv.Close()
p := NewFacebookProvider()
p.graphAPIBase = srv.URL
err := p.SendTypingOff(context.Background(), "12345", "tok")
require.NoError(t, err)
}
func TestFBProvider_MarkSeen_Cov7(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
if _, err := w.Write([]byte(`{"success":true}`)); err != nil {
panic(err)
}
}))
defer srv.Close()
p := NewFacebookProvider()
p.graphAPIBase = srv.URL
err := p.MarkSeen(context.Background(), "12345", "tok")
require.NoError(t, err)
}
func TestFBProvider_SendTypingOn_APIError_Cov7(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
}))
defer srv.Close()
p := NewFacebookProvider()
p.graphAPIBase = srv.URL
err := p.SendTypingOn(context.Background(), "12345", "tok")
require.Error(t, err)
}
func TestFBProvider_TakeThreadControl_NoPSID_Cov7(t *testing.T) {
t.Skip("network test")
p := NewFacebookProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: `{"page_access_token":"tok"}`}
err := p.TakeThreadControl(context.Background(), inbox, "")
require.Error(t, err)
}
func TestFBProvider_TakeThreadControl_NoToken_Cov7(t *testing.T) {
t.Skip("network test")
p := NewFacebookProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: `{}`}
err := p.TakeThreadControl(context.Background(), inbox, "12345")
require.Error(t, err)
}
func TestFBProvider_TakeThreadControl_Success_Cov7(t *testing.T) {
t.Skip("network test")
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}))
defer srv.Close()
p := NewFacebookProvider()
p.graphAPIBase = srv.URL
inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: `{"page_access_token":"tok"}`}
err := p.TakeThreadControl(context.Background(), inbox, "12345")
require.NoError(t, err)
}
func TestFBProvider_TakeThreadControl_APIError_Cov7(t *testing.T) {
t.Skip("network test")
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
}))
defer srv.Close()
p := NewFacebookProvider()
p.graphAPIBase = srv.URL
inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: `{"page_access_token":"tok"}`}
err := p.TakeThreadControl(context.Background(), inbox, "12345")
require.Error(t, err)
}
// ===========================
// FacebookProvider — Helper methods (Cov7)
// ===========================
func TestFBProvider_GetPageAccessTokenFromInbox_Cov7(t *testing.T) {
p := NewFacebookProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: `{"page_access_token":"my_token"}`}
assert.Equal(t, "my_token", p.GetPageAccessTokenFromInbox(inbox))
}
func TestFBProvider_GetPageAccessTokenFromInbox_Empty_Cov7(t *testing.T) {
p := NewFacebookProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: ""}
assert.Empty(t, p.GetPageAccessTokenFromInbox(inbox))
}
func TestFBProvider_GetPageAccessTokenFromInbox_BadJSON_Cov7(t *testing.T) {
p := NewFacebookProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: "bad json"}
assert.Empty(t, p.GetPageAccessTokenFromInbox(inbox))
}
func TestFBProvider_GetPageAccessTokenFromInbox_NoToken_Cov7(t *testing.T) {
p := NewFacebookProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: `{"other":"value"}`}
assert.Empty(t, p.GetPageAccessTokenFromInbox(inbox))
}
func TestIsNumeric_Cov7(t *testing.T) {
assert.True(t, isNumeric("12345"))
assert.False(t, isNumeric("abc"))
assert.False(t, isNumeric(""))
assert.True(t, isNumeric("0"))
}
func TestFBProvider_ExtractRecipientPSID_Numeric_Cov7(t *testing.T) {
p := NewFacebookProvider()
contact := &model.Contact{Identifier: "12345"}
assert.Equal(t, "12345", p.extractRecipientPSID(contact))
}
func TestFBProvider_ExtractRecipientPSID_NonNumeric_Cov7(t *testing.T) {
p := NewFacebookProvider()
contact := &model.Contact{Identifier: "abc"}
assert.Empty(t, p.extractRecipientPSID(contact))
}
func TestFBProvider_MapContentTypeToFBAttachmentType_Cov7(t *testing.T) {
p := NewFacebookProvider()
assert.Equal(t, "image", p.mapContentTypeToFBAttachmentType("image"))
assert.Equal(t, "video", p.mapContentTypeToFBAttachmentType("video"))
assert.Equal(t, "audio", p.mapContentTypeToFBAttachmentType("audio"))
assert.Equal(t, "file", p.mapContentTypeToFBAttachmentType("file"))
assert.Equal(t, "file", p.mapContentTypeToFBAttachmentType("unknown"))
}
func TestFBProvider_SetupWebhookSubscription_Cov7(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
if _, err := w.Write([]byte(`{"success":true}`)); err != nil {
panic(err)
}
}))
defer srv.Close()
p := NewFacebookProvider()
p.graphAPIBase = srv.URL
err := p.setupWebhookSubscription(context.Background(), "app_id", "page_id", "token", "verify")
// May or may not error depending on the mock — just verify it doesn't panic
_ = err
}
// ===========================
// InstagramProvider — identity & metadata (Cov7)
// ===========================
func TestIGProvider_New_Cov7(t *testing.T) {
p := NewInstagramProvider()
require.NotNil(t, p)
assert.NotNil(t, p.client)
assert.NotEmpty(t, p.graphAPIBase)
}
func TestIGProvider_Type_Cov7(t *testing.T) {
p := NewInstagramProvider()
assert.Equal(t, channel.ChannelInstagram, p.Type())
}
func TestIGProvider_Name_Cov7(t *testing.T) {
p := NewInstagramProvider()
assert.Equal(t, "Instagram DM", p.Name())
}
func TestIGProvider_Description_Cov7(t *testing.T) {
p := NewInstagramProvider()
assert.NotEmpty(t, p.Description())
}
func TestIGProvider_ConfigSchema_Cov7(t *testing.T) {
p := NewInstagramProvider()
schema := p.ConfigSchema()
require.NotNil(t, schema)
assert.Contains(t, schema.Required, "instagram_account_id")
assert.Contains(t, schema.Required, "page_access_token")
assert.Contains(t, schema.Required, "connected_fb_page_id")
}
func TestIGProvider_DefaultConfig_Cov7(t *testing.T) {
p := NewInstagramProvider()
cfg := p.DefaultConfig()
assert.Contains(t, cfg, "instagram_account_id")
assert.Contains(t, cfg, "connected_fb_page_id")
}
func TestIGProvider_Capabilities_Cov7(t *testing.T) {
p := NewInstagramProvider()
caps := p.Capabilities()
assert.True(t, caps.SupportsAttachments)
assert.True(t, caps.SupportsTypingIndicator)
assert.True(t, caps.SupportsEmojiReactions)
assert.Equal(t, 1000, caps.MaxTextLength)
}
func TestIGProvider_OAuthConfig_Cov7(t *testing.T) {
p := NewInstagramProvider()
oauth := p.OAuthConfig()
require.NotNil(t, oauth)
assert.Equal(t, "facebook", oauth.Provider)
assert.Contains(t, oauth.Scopes, "instagram_manage_messages")
}
// ===========================
// InstagramProvider — ValidateConfig (Cov7)
// ===========================
func TestIGProvider_ValidateConfig_NoAccountID_Cov7(t *testing.T) {
p := NewInstagramProvider()
err := p.ValidateConfig(context.Background(), channel.ChannelConfig{})
require.Error(t, err)
assert.Contains(t, err.Error(), "instagram_account_id")
}
func TestIGProvider_ValidateConfig_NoToken_Cov7(t *testing.T) {
p := NewInstagramProvider()
err := p.ValidateConfig(context.Background(), channel.ChannelConfig{
"instagram_account_id": "12345",
})
require.Error(t, err)
assert.Contains(t, err.Error(), "page_access_token")
}
func TestIGProvider_ValidateConfig_APIError_Cov7(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusUnauthorized)
}))
defer srv.Close()
p := NewInstagramProvider()
p.graphAPIBase = srv.URL
err := p.ValidateConfig(context.Background(), channel.ChannelConfig{
"instagram_account_id": "12345",
"page_access_token": "tok",
})
require.Error(t, err)
}
func TestIGProvider_ValidateConfig_Success_Cov7(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
mustEncodeTestJSON(w, IGUserProfile{ID: "12345", Username: "testuser"})
}))
defer srv.Close()
p := NewInstagramProvider()
p.graphAPIBase = srv.URL
err := p.ValidateConfig(context.Background(), channel.ChannelConfig{
"instagram_account_id": "12345",
"page_access_token": "tok",
})
require.NoError(t, err)
}
// ===========================
// InstagramProvider — OnCreate / OnDestroy (Cov7)
// ===========================
func TestIGProvider_OnCreate_Cov7(t *testing.T) {
t.Skip("network test")
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
mustEncodeTestJSON(w, IGUserProfile{ID: "12345", Username: "testuser"})
}))
defer srv.Close()
p := NewInstagramProvider()
p.graphAPIBase = srv.URL
inbox := &model.Inbox{Base: model.Base{ID: 1}, AccountID: 10}
config := channel.ChannelConfig{
"instagram_account_id": "12345",
"page_access_token": "tok",
"connected_fb_page_id": "fb_page",
}
result, err := p.OnCreate(context.Background(), inbox, config)
require.NoError(t, err)
assert.NotNil(t, result)
}
func TestIGProvider_OnDestroy_Cov7(t *testing.T) {
t.Skip("network test")
p := NewInstagramProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}}
config := channel.ChannelConfig{
"page_access_token": "tok",
"connected_fb_page_id": "fb_page",
"app_id": "app",
}
err := p.OnDestroy(context.Background(), inbox, config)
require.NoError(t, err)
}
func TestIGProvider_OnDestroy_NoDeps_Cov7(t *testing.T) {
t.Skip("network test")
p := NewInstagramProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}}
config := channel.ChannelConfig{}
err := p.OnDestroy(context.Background(), inbox, config)
require.NoError(t, err)
}
// ===========================
// InstagramProvider — ProcessIncoming (Cov7)
// ===========================
func TestIGProvider_ProcessIncoming_InvalidJSON_Cov7(t *testing.T) {
p := NewInstagramProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, AccountID: 10}
_, err := p.ProcessIncoming(context.Background(), inbox, []byte("bad"))
require.Error(t, err)
}
func TestIGProvider_ProcessIncoming_NotInstagram_Cov7(t *testing.T) {
p := NewInstagramProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, AccountID: 10}
payload := `{"object":"page","entry":[]}`
_, err := p.ProcessIncoming(context.Background(), inbox, []byte(payload))
require.Error(t, err)
assert.Contains(t, err.Error(), "not an instagram")
}
func TestIGProvider_ProcessIncoming_NoContent_Cov7(t *testing.T) {
p := NewInstagramProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, AccountID: 10}
payload := `{"object":"instagram","entry":[{"id":"123","time":1234567890,"messaging":[]}]}`
_, err := p.ProcessIncoming(context.Background(), inbox, []byte(payload))
require.Error(t, err)
}
func TestIGProvider_ProcessIncoming_EchoMessage_Cov7(t *testing.T) {
p := NewInstagramProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, AccountID: 10}
payload := `{"object":"instagram","entry":[{"id":"123","time":1234567890,"messaging":[{"sender":{"id":"s1"},"recipient":{"id":"r1"},"timestamp":1234567890,"message":{"mid":"m1","text":"hi","is_echo":true}}]}]}`
_, err := p.ProcessIncoming(context.Background(), inbox, []byte(payload))
require.Error(t, err)
}
func TestIGProvider_ProcessIncoming_TextMessage_Cov7(t *testing.T) {
p := NewInstagramProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, AccountID: 10}
payload := `{"object":"instagram","entry":[{"id":"123","time":1234567890,"messaging":[{"sender":{"id":"s1"},"recipient":{"id":"r1"},"timestamp":1234567890,"message":{"mid":"m1","text":"hello ig"}}]}]}`
msg, err := p.ProcessIncoming(context.Background(), inbox, []byte(payload))
require.NoError(t, err)
assert.Equal(t, "hello ig", msg.Content)
}
func TestIGProvider_ProcessIncoming_ReplyMessage_Cov7(t *testing.T) {
p := NewInstagramProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, AccountID: 10}
payload := `{"object":"instagram","entry":[{"id":"123","time":1234567890,"messaging":[{"sender":{"id":"s1"},"recipient":{"id":"r1"},"timestamp":1234567890,"message":{"mid":"m1","text":"reply","reply_to":{"mid":"orig_mid"}}}]}]}`
msg, err := p.ProcessIncoming(context.Background(), inbox, []byte(payload))
require.NoError(t, err)
assert.Equal(t, "orig_mid", msg.ReplyToID)
}
func TestIGProvider_ProcessIncoming_ImageAttachment_Cov7(t *testing.T) {
p := NewInstagramProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, AccountID: 10}
payload := `{"object":"instagram","entry":[{"id":"123","time":1234567890,"messaging":[{"sender":{"id":"s1"},"recipient":{"id":"r1"},"timestamp":1234567890,"message":{"mid":"m1","attachments":[{"type":"image","payload":{"url":"https://example.com/img.jpg"}}]}}]}]}`
msg, err := p.ProcessIncoming(context.Background(), inbox, []byte(payload))
require.NoError(t, err)
assert.Contains(t, msg.Content, "img.jpg")
}
func TestIGProvider_ProcessIncoming_ShareAttachment_Cov7(t *testing.T) {
p := NewInstagramProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, AccountID: 10}
payload := `{"object":"instagram","entry":[{"id":"123","time":1234567890,"messaging":[{"sender":{"id":"s1"},"recipient":{"id":"r1"},"timestamp":1234567890,"message":{"mid":"m1","attachments":[{"type":"share","payload":{"url":"https://example.com/share"}}]}}]}]}`
msg, err := p.ProcessIncoming(context.Background(), inbox, []byte(payload))
require.NoError(t, err)
assert.Contains(t, msg.Content, "share")
}
func TestIGProvider_ProcessIncoming_AnimatedMedia_Cov7(t *testing.T) {
p := NewInstagramProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, AccountID: 10}
payload := `{"object":"instagram","entry":[{"id":"123","time":1234567890,"messaging":[{"sender":{"id":"s1"},"recipient":{"id":"r1"},"timestamp":1234567890,"message":{"mid":"m1","attachments":[{"type":"animated_media","payload":{"url":"https://example.com/gif.gif"}}]}}]}]}`
msg, err := p.ProcessIncoming(context.Background(), inbox, []byte(payload))
require.NoError(t, err)
assert.Contains(t, msg.Content, "gif.gif")
}
func TestIGProvider_ProcessIncoming_StoryMention_Cov7(t *testing.T) {
p := NewInstagramProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, AccountID: 10}
payload := `{"object":"instagram","entry":[{"id":"123","time":1234567890,"messaging":[{"sender":{"id":"s1"},"recipient":{"id":"r1"},"timestamp":1234567890,"message":{"mid":"m1","attachments":[{"type":"story_mention","payload":{"url":"https://example.com/story.jpg"}}]}}]}]}`
msg, err := p.ProcessIncoming(context.Background(), inbox, []byte(payload))
require.NoError(t, err)
assert.Contains(t, msg.Content, "story.jpg")
}
func TestIGProvider_ProcessIncoming_Postback_Cov7(t *testing.T) {
p := NewInstagramProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, AccountID: 10}
payload := `{"object":"instagram","entry":[{"id":"123","time":1234567890,"messaging":[{"sender":{"id":"s1"},"recipient":{"id":"r1"},"timestamp":1234567890,"postback":{"title":"Start","payload":"START"}}]}]}`
msg, err := p.ProcessIncoming(context.Background(), inbox, []byte(payload))
require.NoError(t, err)
assert.Equal(t, "START", msg.Content)
}
func TestIGProvider_ProcessIncoming_CommentChange_Cov7(t *testing.T) {
p := NewInstagramProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, AccountID: 10}
payload := `{"object":"instagram","entry":[{"id":"123","time":1234567890,"changes":[{"field":"comments","value":{"media_id":"media_1","id":"comment_1","text":"Nice post!","from":{"id":"user_1","username":"user1"}}}]}]}`
msg, err := p.ProcessIncoming(context.Background(), inbox, []byte(payload))
require.NoError(t, err)
assert.Equal(t, "Nice post!", msg.Content)
}
func TestIGProvider_ProcessIncoming_CommentReplyChange_Cov7(t *testing.T) {
p := NewInstagramProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, AccountID: 10}
payload := `{"object":"instagram","entry":[{"id":"123","time":1234567890,"changes":[{"field":"comments","value":{"media_id":"media_1","id":"comment_2","text":"Reply!","from":{"id":"user_1","username":"user1"},"parent_id":"comment_1"}}]}]}`
msg, err := p.ProcessIncoming(context.Background(), inbox, []byte(payload))
require.NoError(t, err)
assert.Equal(t, "Reply!", msg.Content)
}
func TestIGProvider_ProcessIncoming_CommentDeletedChange_Cov7(t *testing.T) {
p := NewInstagramProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, AccountID: 10}
payload := `{"object":"instagram","entry":[{"id":"123","time":1234567890,"changes":[{"field":"comments","value":{"media_id":"media_1","id":"comment_2","text":"","from":{"id":"user_1","username":"user1"},"is_deleted":true}}]}]}`
msg, err := p.ProcessIncoming(context.Background(), inbox, []byte(payload))
require.NoError(t, err)
assert.Contains(t, msg.Content, "deleted")
}
// ===========================
// InstagramProvider — ValidateWebhookRequest (Cov7)
// ===========================
func TestIGProvider_ValidateWebhookRequest_GET_Valid_Cov7(t *testing.T) {
p := NewInstagramProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: `{"webhook_verify_token":"my_token"}`}
req := &channel.WebhookRequest{
Method: "GET",
QueryParams: map[string]string{"hub.mode": "subscribe", "hub.verify_token": "my_token"},
}
err := p.ValidateWebhookRequest(context.Background(), inbox, req)
require.NoError(t, err)
}
func TestIGProvider_ValidateWebhookRequest_GET_InvalidMode_Cov7(t *testing.T) {
p := NewInstagramProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: `{}`}
req := &channel.WebhookRequest{
Method: "GET",
QueryParams: map[string]string{"hub.mode": "bad"},
}
err := p.ValidateWebhookRequest(context.Background(), inbox, req)
require.Error(t, err)
}
func TestIGProvider_ValidateWebhookRequest_GET_TokenMismatch_Cov7(t *testing.T) {
p := NewInstagramProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: `{"webhook_verify_token":"correct"}`}
req := &channel.WebhookRequest{
Method: "GET",
QueryParams: map[string]string{"hub.mode": "subscribe", "hub.verify_token": "wrong"},
}
err := p.ValidateWebhookRequest(context.Background(), inbox, req)
require.Error(t, err)
}
func TestIGProvider_ValidateWebhookRequest_POST_NoSignature_Cov7(t *testing.T) {
p := NewInstagramProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: `{}`}
req := &channel.WebhookRequest{
Method: "POST",
Headers: map[string]string{},
Body: []byte(`{}`),
}
err := p.ValidateWebhookRequest(context.Background(), inbox, req)
require.Error(t, err)
}
func TestIGProvider_ValidateWebhookRequest_POST_NoAppSecret_Cov7(t *testing.T) {
p := NewInstagramProvider()
p.appSecret = ""
inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: `{}`}
req := &channel.WebhookRequest{
Method: "POST",
Headers: map[string]string{"X-Hub-Signature-256": "sha256=abc"},
Body: []byte(`{}`),
}
err := p.ValidateWebhookRequest(context.Background(), inbox, req)
require.NoError(t, err)
}
func TestIGProvider_ValidateWebhookRequest_POST_BadSig_Cov7(t *testing.T) {
p := NewInstagramProvider()
p.appSecret = "secret"
inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: `{}`}
req := &channel.WebhookRequest{
Method: "POST",
Headers: map[string]string{"X-Hub-Signature-256": "sha256=bad"},
Body: []byte(`{"test":true}`),
}
err := p.ValidateWebhookRequest(context.Background(), inbox, req)
require.Error(t, err)
}
// ===========================
// InstagramProvider — SendMessage (Cov7)
// ===========================
func TestIGProvider_SendMessage_NoIGID_Cov7(t *testing.T) {
t.Skip("network test")
p := NewInstagramProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}}
msg := &model.Message{Base: model.Base{ID: 1}, Content: "hi", ContentType: "text"}
contact := &model.Contact{Base: model.Base{ID: 1}, Identifier: ""}
_, err := p.SendMessage(context.Background(), inbox, msg, contact)
require.Error(t, err)
}
func TestIGProvider_SendMessage_NoToken_Cov7(t *testing.T) {
t.Skip("network test")
p := NewInstagramProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: `{}`}
msg := &model.Message{Base: model.Base{ID: 1}, Content: "hi", ContentType: "text"}
contact := &model.Contact{Base: model.Base{ID: 1}, Identifier: "12345"}
_, err := p.SendMessage(context.Background(), inbox, msg, contact)
require.Error(t, err)
}
func TestIGProvider_SendMessage_Success_Cov7(t *testing.T) {
t.Skip("network test")
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
mustEncodeTestJSON(w, FBSendAPIResponse{RecipientID: "12345", MessageID: "mid:123"})
}))
defer srv.Close()
p := NewInstagramProvider()
p.graphAPIBase = srv.URL
inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: `{"page_access_token":"tok"}`}
msg := &model.Message{Base: model.Base{ID: 1}, Content: "hi", ContentType: "text"}
contact := &model.Contact{Base: model.Base{ID: 1}, Identifier: "12345"}
result, err := p.SendMessage(context.Background(), inbox, msg, contact)
require.NoError(t, err)
assert.Equal(t, "mid:123", result.ExternalID)
}
func TestIGProvider_SendMessage_Attachment_Cov7(t *testing.T) {
t.Skip("network test")
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
mustEncodeTestJSON(w, FBSendAPIResponse{RecipientID: "12345", MessageID: "mid:456"})
}))
defer srv.Close()
p := NewInstagramProvider()
p.graphAPIBase = srv.URL
inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: `{"page_access_token":"tok"}`}
msg := &model.Message{Base: model.Base{ID: 1}, Content: "https://example.com/img.jpg", ContentType: "image"}
contact := &model.Contact{Base: model.Base{ID: 1}, Identifier: "12345"}
result, err := p.SendMessage(context.Background(), inbox, msg, contact)
require.NoError(t, err)
assert.Equal(t, "mid:456", result.ExternalID)
}
func TestIGProvider_SendMessage_APIError_Cov7(t *testing.T) {
t.Skip("network test")
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
}))
defer srv.Close()
p := NewInstagramProvider()
p.graphAPIBase = srv.URL
inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: `{"page_access_token":"tok"}`}
msg := &model.Message{Base: model.Base{ID: 1}, Content: "hi", ContentType: "text"}
contact := &model.Contact{Base: model.Base{ID: 1}, Identifier: "12345"}
_, err := p.SendMessage(context.Background(), inbox, msg, contact)
require.Error(t, err)
}
// ===========================
// InstagramProvider — GetContactProfile (Cov7)
// ===========================
func TestIGProvider_GetContactProfile_NoToken_Cov7(t *testing.T) {
t.Skip("network test")
p := NewInstagramProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: `{}`}
_, err := p.GetContactProfile(context.Background(), inbox, "123")
require.Error(t, err)
}
func TestIGProvider_GetContactProfile_Success_Cov7(t *testing.T) {
t.Skip("network test")
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
mustEncodeTestJSON(w, IGUserProfile{ID: "123", Username: "testuser"})
}))
defer srv.Close()
p := NewInstagramProvider()
p.graphAPIBase = srv.URL
inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: `{"page_access_token":"tok"}`}
profile, err := p.GetContactProfile(context.Background(), inbox, "123")
require.NoError(t, err)
assert.Equal(t, "testuser", profile.Name)
}
func TestIGProvider_GetContactProfile_APIError_Cov7(t *testing.T) {
t.Skip("network test")
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
}))
defer srv.Close()
p := NewInstagramProvider()
p.graphAPIBase = srv.URL
inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: `{"page_access_token":"tok"}`}
_, err := p.GetContactProfile(context.Background(), inbox, "123")
require.Error(t, err)
}
// ===========================
// InstagramProvider — OAuth methods (Cov7)
// ===========================
func TestIGProvider_BuildAuthURL_NoAppID_Cov7(t *testing.T) {
p := NewInstagramProvider()
p.appID = ""
_, err := p.BuildAuthURL(context.Background(), 1, "https://example.com/callback")
require.Error(t, err)
}
func TestIGProvider_BuildAuthURL_Success_Cov7(t *testing.T) {
p := NewInstagramProvider()
p.appID = "test_app"
url, err := p.BuildAuthURL(context.Background(), 1, "https://example.com/callback")
require.NoError(t, err)
assert.Contains(t, url, "test_app")
}
func TestIGProvider_ExchangeToken_NoConfig_Cov7(t *testing.T) {
t.Skip("network test")
p := NewInstagramProvider()
p.appID = ""
p.appSecret = ""
_, err := p.ExchangeToken(context.Background(), "code", "https://example.com/callback")
require.Error(t, err)
}
func TestIGProvider_ExchangeToken_Success_Cov7(t *testing.T) {
t.Skip("network test")
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
mustEncodeTestJSON(w, FBLongLivedTokenResponse{AccessToken: "long_token", ExpiresIn: 5184000})
}))
defer srv.Close()
p := NewInstagramProvider()
p.graphAPIBase = srv.URL
p.appID = "app"
p.appSecret = "secret"
result, err := p.ExchangeToken(context.Background(), "code", "https://example.com/callback")
require.NoError(t, err)
assert.Equal(t, "long_token", result.AccessToken)
}
func TestIGProvider_ExchangeToken_APIError_Cov7(t *testing.T) {
t.Skip("network test")
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusBadRequest)
}))
defer srv.Close()
p := NewInstagramProvider()
p.graphAPIBase = srv.URL
p.appID = "app"
p.appSecret = "secret"
_, err := p.ExchangeToken(context.Background(), "code", "https://example.com/callback")
require.Error(t, err)
}
func TestIGProvider_RefreshToken_NoToken_Cov7(t *testing.T) {
t.Skip("network test")
p := NewInstagramProvider()
_, err := p.RefreshToken(context.Background(), &model.Inbox{Base: model.Base{ID: 1}}, channel.ChannelConfig{})
require.Error(t, err)
}
func TestIGProvider_RefreshToken_Success_Cov7(t *testing.T) {
t.Skip("network test")
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
mustEncodeTestJSON(w, FBLongLivedTokenResponse{AccessToken: "new_token", ExpiresIn: 5184000})
}))
defer srv.Close()
p := NewInstagramProvider()
p.graphAPIBase = srv.URL
result, err := p.RefreshToken(context.Background(), &model.Inbox{Base: model.Base{ID: 1}}, channel.ChannelConfig{
"page_access_token": "old",
})
require.NoError(t, err)
assert.Equal(t, "new_token", result.AccessToken)
}
func TestIGProvider_RefreshToken_APIError_Cov7(t *testing.T) {
t.Skip("network test")
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusUnauthorized)
}))
defer srv.Close()
p := NewInstagramProvider()
p.graphAPIBase = srv.URL
_, err := p.RefreshToken(context.Background(), &model.Inbox{Base: model.Base{ID: 1}}, channel.ChannelConfig{
"page_access_token": "old",
})
require.Error(t, err)
}
func TestIGProvider_CheckAuthorizationError_Nil_Cov7(t *testing.T) {
p := NewInstagramProvider()
assert.False(t, p.CheckAuthorizationError(context.Background(), nil))
}
func TestIGProvider_CheckAuthorizationError_Code190_Cov7(t *testing.T) {
p := NewInstagramProvider()
assert.True(t, p.CheckAuthorizationError(context.Background(), errFake("code 190")))
}
func TestIGProvider_CheckAuthorizationError_Other_Cov7(t *testing.T) {
p := NewInstagramProvider()
assert.False(t, p.CheckAuthorizationError(context.Background(), errFake("random error")))
}
func TestIGProvider_OnReauthorization_Cov7(t *testing.T) {
p := NewInstagramProvider()
err := p.OnReauthorization(context.Background(), &model.Inbox{Base: model.Base{ID: 1}})
require.NoError(t, err)
}
// ===========================
// InstagramProvider — Convenience methods (Cov7)
// ===========================
func TestIGProvider_CreateChannel_Success_Cov7(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
mustEncodeTestJSON(w, IGUserProfile{ID: "12345", Username: "testuser"})
}))
defer srv.Close()
p := NewInstagramProvider()
p.graphAPIBase = srv.URL
ch, err := p.CreateChannel(context.Background(), 10, channel.ChannelConfig{
"instagram_account_id": "12345",
"page_access_token": "tok",
})
require.NoError(t, err)
assert.NotNil(t, ch)
}
func TestIGProvider_CreateChannel_APIError_Cov7(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusUnauthorized)
}))
defer srv.Close()
p := NewInstagramProvider()
p.graphAPIBase = srv.URL
_, err := p.CreateChannel(context.Background(), 10, channel.ChannelConfig{
"instagram_account_id": "12345",
"page_access_token": "tok",
})
require.Error(t, err)
}
func TestIGProvider_UpdateChannel_Cov7(t *testing.T) {
p := NewInstagramProvider()
ch, err := p.UpdateChannel(context.Background(), 1, channel.ChannelConfig{
"page_access_token": "new_tok",
"instagram_account_name": "newname",
})
require.NoError(t, err)
assert.NotNil(t, ch)
}
func TestIGProvider_DeleteChannel_Cov7(t *testing.T) {
p := NewInstagramProvider()
err := p.DeleteChannel(context.Background(), 1)
require.NoError(t, err)
}
func TestIGProvider_HandleWebhook_Instagram_Cov7(t *testing.T) {
p := NewInstagramProvider()
err := p.HandleWebhook(context.Background(), map[string]interface{}{
"object": "instagram",
"entry": []interface{}{},
})
require.NoError(t, err)
}
func TestIGProvider_HandleWebhook_NonInstagram_Cov7(t *testing.T) {
p := NewInstagramProvider()
err := p.HandleWebhook(context.Background(), map[string]interface{}{
"object": "page",
"entry": []interface{}{},
})
require.NoError(t, err)
}
func TestIGProvider_HandleWebhook_BadPayload_Cov7(t *testing.T) {
p := NewInstagramProvider()
err := p.HandleWebhook(context.Background(), map[string]interface{}{
"object": func() {},
})
require.Error(t, err)
}
func TestIGProvider_ProcessIncomingMessage_Cov7(t *testing.T) {
p := NewInstagramProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, AccountID: 10}
payload := map[string]interface{}{
"object": "instagram",
"entry": []interface{}{
map[string]interface{}{
"id": "123",
"time": int64(1234567890),
"messaging": []interface{}{
map[string]interface{}{
"sender": map[string]interface{}{"id": "s1"},
"recipient": map[string]interface{}{"id": "r1"},
"timestamp": int64(1234567890),
"message": map[string]interface{}{"mid": "m1", "text": "ig hello"},
},
},
},
},
}
msg, err := p.ProcessIncomingMessage(context.Background(), inbox, payload)
require.NoError(t, err)
assert.Equal(t, "ig hello", msg.Content)
}
// ===========================
// InstagramProvider — Sender actions & helpers (Cov7)
// ===========================
func TestIGProvider_SendTypingOn_Cov7(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if _, err := w.Write([]byte(`{"success":true}`)); err != nil {
panic(err)
}
}))
defer srv.Close()
p := NewInstagramProvider()
p.graphAPIBase = srv.URL
err := p.SendTypingOn(context.Background(), "12345", "tok")
require.NoError(t, err)
}
func TestIGProvider_SendTypingOff_Cov7(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if _, err := w.Write([]byte(`{"success":true}`)); err != nil {
panic(err)
}
}))
defer srv.Close()
p := NewInstagramProvider()
p.graphAPIBase = srv.URL
err := p.SendTypingOff(context.Background(), "12345", "tok")
require.NoError(t, err)
}
func TestIGProvider_SendTypingOn_APIError_Cov7(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
}))
defer srv.Close()
p := NewInstagramProvider()
p.graphAPIBase = srv.URL
err := p.SendTypingOn(context.Background(), "12345", "tok")
require.Error(t, err)
}
func TestIGProvider_ExtractRecipientIGID_Numeric_Cov7(t *testing.T) {
p := NewInstagramProvider()
contact := &model.Contact{Identifier: "12345"}
assert.Equal(t, "12345", p.extractRecipientIGID(contact))
}
func TestIGProvider_ExtractRecipientIGID_NonNumeric_Cov7(t *testing.T) {
p := NewInstagramProvider()
contact := &model.Contact{Identifier: "abc"}
assert.Empty(t, p.extractRecipientIGID(contact))
}
func TestIGProvider_GetPageAccessTokenFromInbox_Cov7(t *testing.T) {
p := NewInstagramProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: `{"page_access_token":"my_token"}`}
assert.Equal(t, "my_token", p.GetPageAccessTokenFromInbox(inbox))
}
func TestIGProvider_GetPageAccessTokenFromInbox_Empty_Cov7(t *testing.T) {
p := NewInstagramProvider()
inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: ""}
assert.Empty(t, p.GetPageAccessTokenFromInbox(inbox))
}
func TestIGProvider_MapContentTypeToIGAttachmentType_Cov7(t *testing.T) {
p := NewInstagramProvider()
assert.Equal(t, "image", p.mapContentTypeToIGAttachmentType("image"))
assert.Equal(t, "video", p.mapContentTypeToIGAttachmentType("video"))
assert.Equal(t, "audio", p.mapContentTypeToIGAttachmentType("audio"))
assert.Equal(t, "file", p.mapContentTypeToIGAttachmentType("file"))
assert.Equal(t, "file", p.mapContentTypeToIGAttachmentType("unknown"))
}
// ===========================
// InstagramProvider — Comment Management API (Cov7)
// ===========================
func TestIGProvider_GetComments_NoToken_Cov7(t *testing.T) {
t.Skip("network test")
p := NewInstagramProvider()
_, err := p.GetComments(context.Background(), "", "media_1", 10, "")
require.Error(t, err)
}
func TestIGProvider_GetComments_NoMediaID_Cov7(t *testing.T) {
t.Skip("network test")
p := NewInstagramProvider()
_, err := p.GetComments(context.Background(), "tok", "", 10, "")
require.Error(t, err)
}
func TestIGProvider_GetComments_Success_Cov7(t *testing.T) {
t.Skip("network test")
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
if _, err := w.Write([]byte(`{"data":[]}`)); err != nil {
panic(err)
}
}))
defer srv.Close()
p := NewInstagramProvider()
p.graphAPIBase = srv.URL
result, err := p.GetComments(context.Background(), "tok", "media_1", 10, "cursor1")
require.NoError(t, err)
assert.NotNil(t, result)
}
func TestIGProvider_GetComments_APIError_Cov7(t *testing.T) {
t.Skip("network test")
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
}))
defer srv.Close()
p := NewInstagramProvider()
p.graphAPIBase = srv.URL
_, err := p.GetComments(context.Background(), "tok", "media_1", 0, "")
require.Error(t, err)
}
func TestIGProvider_GetCommentReplies_NoToken_Cov7(t *testing.T) {
p := NewInstagramProvider()
_, err := p.GetCommentReplies(context.Background(), "", "comment_1", 10, "")
require.Error(t, err)
}
func TestIGProvider_GetCommentReplies_NoCommentID_Cov7(t *testing.T) {
p := NewInstagramProvider()
_, err := p.GetCommentReplies(context.Background(), "tok", "", 10, "")
require.Error(t, err)
}
func TestIGProvider_GetCommentReplies_Success_Cov7(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
if _, err := w.Write([]byte(`{"data":[]}`)); err != nil {
panic(err)
}
}))
defer srv.Close()
p := NewInstagramProvider()
p.graphAPIBase = srv.URL
result, err := p.GetCommentReplies(context.Background(), "tok", "comment_1", 10, "")
require.NoError(t, err)
assert.NotNil(t, result)
}
func TestIGProvider_ReplyToComment_NoToken_Cov7(t *testing.T) {
t.Skip("network test")
p := NewInstagramProvider()
_, err := p.ReplyToComment(context.Background(), "", "comment_1", "reply text")
require.Error(t, err)
}
func TestIGProvider_ReplyToComment_NoCommentID_Cov7(t *testing.T) {
t.Skip("network test")
p := NewInstagramProvider()
_, err := p.ReplyToComment(context.Background(), "tok", "", "reply text")
require.Error(t, err)
}
func TestIGProvider_ReplyToComment_NoMessage_Cov7(t *testing.T) {
t.Skip("network test")
p := NewInstagramProvider()
_, err := p.ReplyToComment(context.Background(), "tok", "comment_1", "")
require.Error(t, err)
}
func TestIGProvider_ReplyToComment_Success_Cov7(t *testing.T) {
t.Skip("network test")
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
if _, err := w.Write([]byte(`{"id":"reply_1"}`)); err != nil {
panic(err)
}
}))
defer srv.Close()
p := NewInstagramProvider()
p.graphAPIBase = srv.URL
result, err := p.ReplyToComment(context.Background(), "tok", "comment_1", "reply text")
require.NoError(t, err)
assert.Equal(t, "reply_1", result.ID)
}
func TestIGProvider_ReplyToComment_APIError_Cov7(t *testing.T) {
t.Skip("network test")
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
}))
defer srv.Close()
p := NewInstagramProvider()
p.graphAPIBase = srv.URL
_, err := p.ReplyToComment(context.Background(), "tok", "comment_1", "reply text")
require.Error(t, err)
}
func TestIGProvider_HideComment_NoToken_Cov7(t *testing.T) {
t.Skip("network test")
p := NewInstagramProvider()
_, err := p.HideComment(context.Background(), "", "comment_1", true)
require.Error(t, err)
}
func TestIGProvider_HideComment_NoCommentID_Cov7(t *testing.T) {
t.Skip("network test")
p := NewInstagramProvider()
_, err := p.HideComment(context.Background(), "tok", "", true)
require.Error(t, err)
}
func TestIGProvider_HideComment_Success_Cov7(t *testing.T) {
t.Skip("network test")
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
if _, err := w.Write([]byte(`{"success":true}`)); err != nil {
panic(err)
}
}))
defer srv.Close()
p := NewInstagramProvider()
p.graphAPIBase = srv.URL
result, err := p.HideComment(context.Background(), "tok", "comment_1", true)
require.NoError(t, err)
assert.True(t, result.Success)
}
func TestIGProvider_HideComment_APIError_Cov7(t *testing.T) {
t.Skip("network test")
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
}))
defer srv.Close()
p := NewInstagramProvider()
p.graphAPIBase = srv.URL
_, err := p.HideComment(context.Background(), "tok", "comment_1", false)
require.Error(t, err)
}
func TestIGProvider_DeleteComment_NoToken_Cov7(t *testing.T) {
t.Skip("network test")
p := NewInstagramProvider()
_, err := p.DeleteComment(context.Background(), "", "comment_1")
require.Error(t, err)
}
func TestIGProvider_DeleteComment_NoCommentID_Cov7(t *testing.T) {
t.Skip("network test")
p := NewInstagramProvider()
_, err := p.DeleteComment(context.Background(), "tok", "")
require.Error(t, err)
}
func TestIGProvider_DeleteComment_Success_Cov7(t *testing.T) {
t.Skip("network test")
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
if _, err := w.Write([]byte(`{"success":true}`)); err != nil {
panic(err)
}
}))
defer srv.Close()
p := NewInstagramProvider()
p.graphAPIBase = srv.URL
result, err := p.DeleteComment(context.Background(), "tok", "comment_1")
require.NoError(t, err)
assert.True(t, result.Success)
}
func TestIGProvider_DeleteComment_APIError_Cov7(t *testing.T) {
t.Skip("network test")
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
}))
defer srv.Close()
p := NewInstagramProvider()
p.graphAPIBase = srv.URL
_, err := p.DeleteComment(context.Background(), "tok", "comment_1")
require.Error(t, err)
}
func TestIGProvider_ProcessCommentIncoming_Nil_Cov7(t *testing.T) {
p := NewInstagramProvider()
_, err := p.ProcessCommentIncoming(context.Background(), &model.Inbox{Base: model.Base{ID: 1}}, nil, EventIGComment)
require.Error(t, err)
}
func TestIGProvider_ProcessCommentIncoming_Comment_Cov7(t *testing.T) {
p := NewInstagramProvider()
commentData := &IGCommentChangeValue{
CommentID: "c1",
MediaID: "m1",
Text: "hello",
From: IGCommentFrom{ID: "u1", Username: "user1"},
}
msg, err := p.ProcessCommentIncoming(context.Background(), &model.Inbox{Base: model.Base{ID: 1}}, commentData, EventIGComment)
require.NoError(t, err)
assert.Equal(t, "hello", msg.Content)
}
func TestIGProvider_ProcessCommentIncoming_Reply_Cov7(t *testing.T) {
p := NewInstagramProvider()
commentData := &IGCommentChangeValue{
CommentID: "c2",
MediaID: "m1",
Text: "reply text",
ParentID: "c1",
From: IGCommentFrom{ID: "u1", Username: "user1"},
}
msg, err := p.ProcessCommentIncoming(context.Background(), &model.Inbox{Base: model.Base{ID: 1}}, commentData, EventIGCommentReply)
require.NoError(t, err)
assert.Equal(t, "reply text", msg.Content)
}
func TestIGProvider_ProcessCommentIncoming_Deleted_Cov7(t *testing.T) {
p := NewInstagramProvider()
commentData := &IGCommentChangeValue{
CommentID: "c3",
MediaID: "m1",
Text: "",
IsDeleted: true,
From: IGCommentFrom{ID: "u1", Username: "user1"},
}
msg, err := p.ProcessCommentIncoming(context.Background(), &model.Inbox{Base: model.Base{ID: 1}}, commentData, EventIGCommentDeleted)
require.NoError(t, err)
assert.Contains(t, msg.Content, "deleted")
}
// ===========================
// InstagramProvider — Webhook Registration (Cov7)
// ===========================
func TestIGProvider_RegisterWebhook_Success_Cov7(t *testing.T) {
t.Skip("network test")
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
if _, err := w.Write([]byte(`{"success":true}`)); err != nil {
panic(err)
}
}))
defer srv.Close()
p := NewInstagramProvider()
p.graphAPIBase = srv.URL
result, err := p.RegisterWebhook(context.Background(), "tok", "page_1")
require.NoError(t, err)
assert.NotEmpty(t, result)
}
func TestIGProvider_RegisterWebhook_APIError_Cov7(t *testing.T) {
t.Skip("network test")
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
}))
defer srv.Close()
p := NewInstagramProvider()
p.graphAPIBase = srv.URL
_, err := p.RegisterWebhook(context.Background(), "tok", "page_1")
require.Error(t, err)
}
func TestIGProvider_RegisterWebhook_SuccessFalse_Cov7(t *testing.T) {
t.Skip("network test")
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
if _, err := w.Write([]byte(`{"success":false}`)); err != nil {
panic(err)
}
}))
defer srv.Close()
p := NewInstagramProvider()
p.graphAPIBase = srv.URL
_, err := p.RegisterWebhook(context.Background(), "tok", "page_1")
require.Error(t, err)
}
func TestIGProvider_ListWebhooks_Success_Cov7(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
if _, err := w.Write([]byte(`[]`)); err != nil {
panic(err)
}
}))
defer srv.Close()
p := NewInstagramProvider()
p.graphAPIBase = srv.URL
result, err := p.ListWebhooks(context.Background(), "tok", "page_1")
require.NoError(t, err)
assert.NotNil(t, result)
}
func TestIGProvider_ListWebhooks_APIError_Cov7(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
}))
defer srv.Close()
p := NewInstagramProvider()
p.graphAPIBase = srv.URL
_, err := p.ListWebhooks(context.Background(), "tok", "page_1")
require.Error(t, err)
}
func TestIGProvider_DeleteWebhook_Success_Cov7(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}))
defer srv.Close()
p := NewInstagramProvider()
p.graphAPIBase = srv.URL
err := p.DeleteWebhook(context.Background(), "tok", "page_1")
require.NoError(t, err)
}
func TestIGProvider_DeleteWebhook_APIError_Cov7(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
}))
defer srv.Close()
p := NewInstagramProvider()
p.graphAPIBase = srv.URL
err := p.DeleteWebhook(context.Background(), "tok", "page_1")
require.Error(t, err)
}
// ===========================
// Type instantiation tests (Cov7)
// ===========================
func TestFBTypes_Instantiation_Cov7(t *testing.T) {
evt := FBWebhookEvent{Object: "page"}
assert.Equal(t, "page", evt.Object)
msg := FBMessageData{Mid: "m1", Text: "hello"}
assert.Equal(t, "m1", msg.Mid)
att := FBAttachment{Type: "image", Payload: FBAttachmentPayload{URL: "http://example.com"}}
assert.Equal(t, "image", att.Type)
profile := FBUserProfile{ID: "123", Name: "Test"}
assert.Equal(t, "123", profile.ID)
pageInfo := FBPageInfo{ID: "1", Name: "Page"}
assert.Equal(t, "1", pageInfo.ID)
tokenResp := FBLongLivedTokenResponse{AccessToken: "tok"}
assert.Equal(t, "tok", tokenResp.AccessToken)
igProfile := IGUserProfile{ID: "ig1", Username: "user"}
assert.Equal(t, "ig1", igProfile.ID)
commentVal := IGCommentChangeValue{CommentID: "c1", Text: "hello"}
assert.Equal(t, "c1", commentVal.CommentID)
paging := FBGraphPaging{Next: "http://next"}
assert.Equal(t, "http://next", paging.Next)
}
func TestFBTypes_SendRequest_Cov7(t *testing.T) {
req := FBSendMessageRequest{
Recipient: FBRecipient{ID: "123"},
MessagingType: "RESP",
Message: &FBSendMessage{Text: "hello"},
}
assert.Equal(t, "123", req.Recipient.ID)
assert.Equal(t, "hello", req.Message.Text)
}
func TestFBTypes_SendAttachment_Cov7(t *testing.T) {
att := FBSendAttachment{
Type: "image",
Payload: FBSendAttachmentPayload{URL: "http://example.com/img.jpg"},
}
assert.Equal(t, "image", att.Type)
assert.Equal(t, "http://example.com/img.jpg", att.Payload.URL)
}
func TestFBTypes_SubscriptionVerify_Cov7(t *testing.T) {
verify := FBWebhookSubscriptionVerify{Mode: "subscribe", VerifyToken: "tok", Challenge: "challenge"}
assert.Equal(t, "subscribe", verify.Mode)
}
// errFake creates a simple error for testing
type fakeError7 string
func (e fakeError7) Error() string { return string(e) }
func errFake(s string) error { return fakeError7(s) }