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) }