Files
gochat/internal/channel/provider/provider_mock_test.go
T
2026-06-04 15:44:48 +08:00

330 lines
9.3 KiB
Go

package provider
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/gochat/gochat/internal/model/channel"
)
// MockChannelProvider is a mock implementation of ChannelProvider for testing.
type MockChannelProvider struct {
mock.Mock
}
func (m *MockChannelProvider) Name() string {
args := m.Called()
return args.String(0)
}
func (m *MockChannelProvider) CreateChannel(ctx context.Context, accountID uint, params map[string]interface{}) (channel.Channelable, error) {
args := m.Called(ctx, accountID, params)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).(channel.Channelable), args.Error(1)
}
func (m *MockChannelProvider) UpdateChannel(ctx context.Context, channelID uint, params map[string]interface{}) (channel.Channelable, error) {
args := m.Called(ctx, channelID, params)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).(channel.Channelable), args.Error(1)
}
func (m *MockChannelProvider) DeleteChannel(ctx context.Context, channelID uint) error {
args := m.Called(ctx, channelID)
return args.Error(0)
}
func (m *MockChannelProvider) HandleWebhook(ctx context.Context, payload map[string]interface{}) error {
args := m.Called(ctx, payload)
return args.Error(0)
}
func (m *MockChannelProvider) ProcessIncomingMessage(ctx context.Context, inboxID uint, contactID uint, payload map[string]interface{}) (*IncomingMessageResult, error) {
args := m.Called(ctx, inboxID, contactID, payload)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).(*IncomingMessageResult), args.Error(1)
}
func (m *MockChannelProvider) ProcessOutgoingMessage(ctx context.Context, conversationID uint, messageID uint, content string) error {
args := m.Called(ctx, conversationID, messageID, content)
return args.Error(0)
}
func (m *MockChannelProvider) ValidateConfig(params map[string]interface{}) error {
args := m.Called(params)
return args.Error(0)
}
// === MockChannelProvider Tests ===
func TestMockChannelProvider_Name(t *testing.T) {
mockProvider := new(MockChannelProvider)
mockProvider.On("Name").Return("web_widget")
name := mockProvider.Name()
assert.Equal(t, "web_widget", name)
mockProvider.AssertExpectations(t)
}
func TestMockChannelProvider_CreateChannel(t *testing.T) {
mockProvider := new(MockChannelProvider)
ctx := context.Background()
params := map[string]interface{}{
"website_url": "https://example.com",
"welcome_title": "Welcome",
}
// Create a mock Channelable result - we'll return nil for simplicity
// since Channelable is an interface
mockProvider.On("CreateChannel", ctx, uint(1), params).Return(nil, nil)
result, err := mockProvider.CreateChannel(ctx, 1, params)
assert.Nil(t, result)
assert.NoError(t, err)
mockProvider.AssertExpectations(t)
}
func TestMockChannelProvider_CreateChannel_Error(t *testing.T) {
mockProvider := new(MockChannelProvider)
ctx := context.Background()
params := map[string]interface{}{
"bot_token": "", // empty bot token should fail
}
mockProvider.On("CreateChannel", ctx, uint(1), params).Return(nil, assert.AnError)
result, err := mockProvider.CreateChannel(ctx, 1, params)
assert.Nil(t, result)
assert.Error(t, err)
mockProvider.AssertExpectations(t)
}
func TestMockChannelProvider_UpdateChannel(t *testing.T) {
mockProvider := new(MockChannelProvider)
ctx := context.Background()
params := map[string]interface{}{
"welcome_title": "Updated Welcome",
}
mockProvider.On("UpdateChannel", ctx, uint(1), params).Return(nil, nil)
result, err := mockProvider.UpdateChannel(ctx, 1, params)
assert.Nil(t, result)
assert.NoError(t, err)
mockProvider.AssertExpectations(t)
}
func TestMockChannelProvider_DeleteChannel(t *testing.T) {
mockProvider := new(MockChannelProvider)
ctx := context.Background()
mockProvider.On("DeleteChannel", ctx, uint(1)).Return(nil)
err := mockProvider.DeleteChannel(ctx, 1)
assert.NoError(t, err)
mockProvider.AssertExpectations(t)
}
func TestMockChannelProvider_DeleteChannel_Error(t *testing.T) {
mockProvider := new(MockChannelProvider)
ctx := context.Background()
mockProvider.On("DeleteChannel", ctx, uint(999)).Return(assert.AnError)
err := mockProvider.DeleteChannel(ctx, 999)
assert.Error(t, err)
mockProvider.AssertExpectations(t)
}
func TestMockChannelProvider_HandleWebhook(t *testing.T) {
mockProvider := new(MockChannelProvider)
ctx := context.Background()
payload := map[string]interface{}{
"message": map[string]interface{}{
"text": "Hello from Telegram",
"chat": map[string]interface{}{
"id": 12345,
},
},
}
mockProvider.On("HandleWebhook", ctx, payload).Return(nil)
err := mockProvider.HandleWebhook(ctx, payload)
assert.NoError(t, err)
mockProvider.AssertExpectations(t)
}
func TestMockChannelProvider_ProcessIncomingMessage(t *testing.T) {
mockProvider := new(MockChannelProvider)
ctx := context.Background()
payload := map[string]interface{}{
"text": "Hello",
"from": map[string]interface{}{
"id": 100,
"name": "Test User",
},
}
expectedResult := &IncomingMessageResult{
Content: "Hello",
ContentType: "text",
SourceID: "100",
Extras: map[string]interface{}{"sender_name": "Test User"},
}
mockProvider.On("ProcessIncomingMessage", ctx, uint(1), uint(2), payload).Return(expectedResult, nil)
result, err := mockProvider.ProcessIncomingMessage(ctx, 1, 2, payload)
assert.NoError(t, err)
assert.Equal(t, "Hello", result.Content)
assert.Equal(t, "text", result.ContentType)
assert.Equal(t, "100", result.SourceID)
mockProvider.AssertExpectations(t)
}
func TestMockChannelProvider_ProcessOutgoingMessage(t *testing.T) {
mockProvider := new(MockChannelProvider)
ctx := context.Background()
mockProvider.On("ProcessOutgoingMessage", ctx, uint(1), uint(10), "Reply message").Return(nil)
err := mockProvider.ProcessOutgoingMessage(ctx, 1, 10, "Reply message")
assert.NoError(t, err)
mockProvider.AssertExpectations(t)
}
func TestMockChannelProvider_ValidateConfig(t *testing.T) {
mockProvider := new(MockChannelProvider)
// Valid config
validParams := map[string]interface{}{
"bot_token": "123456:ABC-DEF",
}
mockProvider.On("ValidateConfig", validParams).Return(nil)
err := mockProvider.ValidateConfig(validParams)
assert.NoError(t, err)
mockProvider.AssertExpectations(t)
}
func TestMockChannelProvider_ValidateConfig_Invalid(t *testing.T) {
mockProvider := new(MockChannelProvider)
// Invalid config - empty bot token
invalidParams := map[string]interface{}{
"bot_token": "",
}
mockProvider.On("ValidateConfig", invalidParams).Return(assert.AnError)
err := mockProvider.ValidateConfig(invalidParams)
assert.Error(t, err)
mockProvider.AssertExpectations(t)
}
// === Multi-provider registration tests ===
func TestMultipleProvidersDifferentNames(t *testing.T) {
webWidgetProvider := new(MockChannelProvider)
telegramProvider := new(MockChannelProvider)
webWidgetProvider.On("Name").Return("web_widget")
telegramProvider.On("Name").Return("telegram")
assert.Equal(t, "web_widget", webWidgetProvider.Name())
assert.Equal(t, "telegram", telegramProvider.Name())
webWidgetProvider.AssertExpectations(t)
telegramProvider.AssertExpectations(t)
}
// === Provider lifecycle tests (Chatwoot pattern) ===
func TestProviderCreateUpdateDeleteLifecycle(t *testing.T) {
mockProvider := new(MockChannelProvider)
ctx := context.Background()
// Step 1: Create
createParams := map[string]interface{}{
"website_url": "https://example.com",
}
mockProvider.On("CreateChannel", ctx, uint(1), createParams).Return(nil, nil)
_, err := mockProvider.CreateChannel(ctx, 1, createParams)
assert.NoError(t, err)
// Step 2: Update
updateParams := map[string]interface{}{
"welcome_title": "New Welcome",
}
mockProvider.On("UpdateChannel", ctx, uint(1), updateParams).Return(nil, nil)
_, err = mockProvider.UpdateChannel(ctx, 1, updateParams)
assert.NoError(t, err)
// Step 3: Delete
mockProvider.On("DeleteChannel", ctx, uint(1)).Return(nil)
err = mockProvider.DeleteChannel(ctx, 1)
assert.NoError(t, err)
mockProvider.AssertExpectations(t)
}
// === Webhook + message processing pipeline test (Chatwoot pattern) ===
func TestProviderWebhookToMessagePipeline(t *testing.T) {
mockProvider := new(MockChannelProvider)
ctx := context.Background()
// Step 1: Handle incoming webhook
webhookPayload := map[string]interface{}{
"update_id": 12345,
"message": map[string]interface{}{
"text": "Customer question",
"from": map[string]interface{}{
"id": 999,
"name": "Customer",
},
},
}
mockProvider.On("HandleWebhook", ctx, webhookPayload).Return(nil)
err := mockProvider.HandleWebhook(ctx, webhookPayload)
assert.NoError(t, err)
// Step 2: Process the incoming message
incomingResult := &IncomingMessageResult{
Content: "Customer question",
ContentType: "text",
SourceID: "999",
Extras: map[string]interface{}{"sender_name": "Customer"},
}
mockProvider.On("ProcessIncomingMessage", ctx, uint(1), uint(5), webhookPayload).Return(incomingResult, nil)
result, err := mockProvider.ProcessIncomingMessage(ctx, 1, 5, webhookPayload)
assert.NoError(t, err)
assert.Equal(t, "Customer question", result.Content)
// Step 3: Send outgoing reply
mockProvider.On("ProcessOutgoingMessage", ctx, uint(1), uint(100), "Agent reply").Return(nil)
err = mockProvider.ProcessOutgoingMessage(ctx, 1, 100, "Agent reply")
assert.NoError(t, err)
mockProvider.AssertExpectations(t)
}