330 lines
9.3 KiB
Go
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)
|
|
} |