package usecase_test import ( "context" "errors" "log/slog" "os" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/paramah/gw_telegram/internal/application/dto" "github.com/paramah/gw_telegram/internal/application/usecase" "github.com/paramah/gw_telegram/internal/domain/apperror" "github.com/paramah/gw_telegram/internal/domain/entity" "github.com/paramah/gw_telegram/test/testutil" ) func newTestLogger() *slog.Logger { return slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelError})) } func validTextDTO() dto.IncomingMessageDTO { return dto.IncomingMessageDTO{ MessageID: 1, ChatID: 100, UserID: 200, Username: "testuser", Text: "Hello, I need help", Timestamp: time.Now(), Language: "en", } } func TestHandleTextMessage_Execute_HappyPath(t *testing.T) { router := &testutil.FakeIntentRouter{ RouteResult: entity.Route{ IntentName: "general_query", Target: entity.RouteTarget{ Type: entity.RouteTargetN8n, WorkflowID: "general-webhook", }, }, } dispatcher := &testutil.FakeWorkflowDispatcher{ Response: entity.WorkflowResponse{ ReplyText: "Here is my response", }, } sessions := &testutil.FakeSessionStore{} gateway := &testutil.FakeMessageGateway{} uc := usecase.NewHandleTextMessage(router, dispatcher, sessions, gateway, newTestLogger()) err := uc.Execute(context.Background(), validTextDTO()) require.NoError(t, err) assert.Equal(t, 1, dispatcher.CallCount) assert.Equal(t, "Here is my response", gateway.LastSentText) assert.Equal(t, int64(100), gateway.LastChatID) } func TestHandleTextMessage_Execute_EmptyText(t *testing.T) { uc := usecase.NewHandleTextMessage( &testutil.FakeIntentRouter{}, &testutil.FakeWorkflowDispatcher{}, &testutil.FakeSessionStore{}, &testutil.FakeMessageGateway{}, newTestLogger(), ) in := validTextDTO() in.Text = "" err := uc.Execute(context.Background(), in) assert.ErrorIs(t, err, apperror.ErrMessageEmpty) } func TestHandleTextMessage_Execute_RouteNotFound(t *testing.T) { router := &testutil.FakeIntentRouter{Error: apperror.ErrRouteNotFound} dispatcher := &testutil.FakeWorkflowDispatcher{} sessions := &testutil.FakeSessionStore{} gateway := &testutil.FakeMessageGateway{} uc := usecase.NewHandleTextMessage(router, dispatcher, sessions, gateway, newTestLogger()) err := uc.Execute(context.Background(), validTextDTO()) assert.Error(t, err) assert.True(t, errors.Is(err, apperror.ErrRouteNotFound)) assert.Equal(t, 0, dispatcher.CallCount) assert.NotEmpty(t, gateway.LastSentText) // error message sent to user } func TestHandleTextMessage_Execute_DispatchError(t *testing.T) { router := &testutil.FakeIntentRouter{ RouteResult: entity.Route{IntentName: "general_query"}, } dispatcher := &testutil.FakeWorkflowDispatcher{ Error: errors.New("n8n unavailable"), } sessions := &testutil.FakeSessionStore{} gateway := &testutil.FakeMessageGateway{} uc := usecase.NewHandleTextMessage(router, dispatcher, sessions, gateway, newTestLogger()) err := uc.Execute(context.Background(), validTextDTO()) assert.Error(t, err) assert.Equal(t, 1, dispatcher.CallCount) assert.NotEmpty(t, gateway.LastSentText) // fallback error message sent to user } func TestHandleTextMessage_Execute_SessionPersisted(t *testing.T) { router := &testutil.FakeIntentRouter{ RouteResult: entity.Route{ IntentName: "general_query", Target: entity.RouteTarget{WorkflowID: "wf-1"}, }, } dispatcher := &testutil.FakeWorkflowDispatcher{ Response: entity.WorkflowResponse{ReplyText: "OK"}, } sessions := &testutil.FakeSessionStore{} gateway := &testutil.FakeMessageGateway{} uc := usecase.NewHandleTextMessage(router, dispatcher, sessions, gateway, newTestLogger()) err := uc.Execute(context.Background(), validTextDTO()) require.NoError(t, err) assert.True(t, sessions.SetCalled) assert.Equal(t, "wf-1", sessions.LastSetSession.CurrentWorkflow) }