331 lines
10 KiB
Go
331 lines
10 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"gorm.io/driver/sqlite"
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm/logger"
|
|
|
|
"github.com/gochat/gochat/internal/llm"
|
|
"github.com/gochat/gochat/internal/model"
|
|
"github.com/gochat/gochat/internal/repository"
|
|
)
|
|
|
|
// ========== Auto-Reply Rule Service Tests ==========
|
|
|
|
func setupAutoReplyRuleService(t *testing.T) (*gorm.DB, *mockLLMProvider, *AutoReplyRuleService) {
|
|
t.Helper()
|
|
|
|
db, err := gorm.Open(sqlite.Open("file::memory:"), &gorm.Config{
|
|
Logger: logger.Default.LogMode(logger.Silent),
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("failed to open test DB: %v", err)
|
|
}
|
|
if err := db.AutoMigrate(
|
|
&model.Account{},
|
|
&model.Conversation{},
|
|
&model.Message{},
|
|
&model.CaptainAssistant{},
|
|
&model.CaptainAssistantResponse{},
|
|
&model.CaptainAutoReplyRule{},
|
|
); err != nil {
|
|
t.Fatalf("failed to auto-migrate: %v", err)
|
|
}
|
|
t.Cleanup(func() {
|
|
sqlDB, _ := db.DB()
|
|
sqlDB.Close()
|
|
})
|
|
|
|
mockProvider := &mockLLMProvider{}
|
|
assistantRepo := repository.NewCaptainAssistantRepo(db)
|
|
ruleRepo := repository.NewCaptainAutoReplyRuleRepo(db)
|
|
conversationRepo := repository.NewConversationRepo(db)
|
|
svc := NewAutoReplyRuleService(ruleRepo, assistantRepo, conversationRepo, mockProvider)
|
|
|
|
return db, mockProvider, svc
|
|
}
|
|
|
|
// --- CreateRule ---
|
|
func TestAutoReplyRuleService_CreateRule(t *testing.T) {
|
|
db, _, svc := setupAutoReplyRuleService(t)
|
|
|
|
account := createTestAccount(t, db)
|
|
assistant := createTestAssistant(t, db, account.ID, "TestAssistant")
|
|
|
|
req := &CreateAutoReplyRuleRequest{
|
|
AssistantID: assistant.ID,
|
|
Name: "UrgentHandler",
|
|
Mode: "static",
|
|
ResponseText: "We will respond to your urgent inquiry shortly.",
|
|
Priority: 10,
|
|
}
|
|
|
|
result, err := svc.CreateRule(context.Background(), account.ID, req)
|
|
require.NoError(t, err)
|
|
assert.NotZero(t, result.ID)
|
|
assert.Equal(t, "UrgentHandler", result.Name)
|
|
assert.Equal(t, model.AutoReplyRuleStatusDraft, result.Status)
|
|
assert.Equal(t, model.AutoReplyRuleModeStatic, result.Mode)
|
|
}
|
|
|
|
func TestAutoReplyRuleService_CreateRule_AssistantNotFound(t *testing.T) {
|
|
db, _, svc := setupAutoReplyRuleService(t)
|
|
account := createTestAccount(t, db)
|
|
|
|
req := &CreateAutoReplyRuleRequest{
|
|
AssistantID: 9999,
|
|
Name: "Test",
|
|
Mode: "static",
|
|
ResponseText: "test",
|
|
}
|
|
|
|
_, err := svc.CreateRule(context.Background(), account.ID, req)
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "assistant not found")
|
|
}
|
|
|
|
func TestAutoReplyRuleService_CreateRule_WrongAccount(t *testing.T) {
|
|
db, _, svc := setupAutoReplyRuleService(t)
|
|
|
|
account1 := createTestAccountWithName(t, db, "Account1")
|
|
account2 := createTestAccountWithName(t, db, "Account2")
|
|
assistant := createTestAssistant(t, db, account1.ID, "TestAssistant")
|
|
|
|
req := &CreateAutoReplyRuleRequest{
|
|
AssistantID: assistant.ID,
|
|
Name: "Test",
|
|
Mode: "static",
|
|
ResponseText: "test",
|
|
}
|
|
|
|
_, err := svc.CreateRule(context.Background(), account2.ID, req)
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "does not belong to account")
|
|
}
|
|
|
|
// --- GetRule ---
|
|
func TestAutoReplyRuleService_GetRule(t *testing.T) {
|
|
db, _, svc := setupAutoReplyRuleService(t)
|
|
|
|
account := createTestAccount(t, db)
|
|
assistant := createTestAssistant(t, db, account.ID, "TestAssistant")
|
|
|
|
createReq := &CreateAutoReplyRuleRequest{
|
|
AssistantID: assistant.ID,
|
|
Name: "TestRule",
|
|
Mode: "static",
|
|
ResponseText: "test response",
|
|
}
|
|
result, err := svc.CreateRule(context.Background(), account.ID, createReq)
|
|
require.NoError(t, err)
|
|
|
|
found, err := svc.GetRule(context.Background(), account.ID, result.ID)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, result.ID, found.ID)
|
|
assert.Equal(t, "TestRule", found.Name)
|
|
}
|
|
|
|
// --- UpdateRule ---
|
|
func TestAutoReplyRuleService_UpdateRule(t *testing.T) {
|
|
db, _, svc := setupAutoReplyRuleService(t)
|
|
|
|
account := createTestAccount(t, db)
|
|
assistant := createTestAssistant(t, db, account.ID, "TestAssistant")
|
|
|
|
createReq := &CreateAutoReplyRuleRequest{
|
|
AssistantID: assistant.ID,
|
|
Name: "OriginalName",
|
|
Mode: "static",
|
|
ResponseText: "original response",
|
|
}
|
|
result, err := svc.CreateRule(context.Background(), account.ID, createReq)
|
|
require.NoError(t, err)
|
|
|
|
newName := "UpdatedName"
|
|
newStatus := model.AutoReplyRuleStatusActive
|
|
updateReq := &UpdateAutoReplyRuleRequest{
|
|
Name: &newName,
|
|
Status: &newStatus,
|
|
}
|
|
|
|
updated, err := svc.UpdateRule(context.Background(), account.ID, result.ID, updateReq)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "UpdatedName", updated.Name)
|
|
assert.Equal(t, model.AutoReplyRuleStatusActive, updated.Status)
|
|
}
|
|
|
|
// --- DeleteRule ---
|
|
func TestAutoReplyRuleService_DeleteRule(t *testing.T) {
|
|
db, _, svc := setupAutoReplyRuleService(t)
|
|
|
|
account := createTestAccount(t, db)
|
|
assistant := createTestAssistant(t, db, account.ID, "TestAssistant")
|
|
|
|
createReq := &CreateAutoReplyRuleRequest{
|
|
AssistantID: assistant.ID,
|
|
Name: "ToDelete",
|
|
Mode: "static",
|
|
ResponseText: "test",
|
|
}
|
|
result, err := svc.CreateRule(context.Background(), account.ID, createReq)
|
|
require.NoError(t, err)
|
|
|
|
err = svc.DeleteRule(context.Background(), account.ID, result.ID)
|
|
require.NoError(t, err)
|
|
|
|
_, err = svc.GetRule(context.Background(), account.ID, result.ID)
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
// --- ListRules ---
|
|
func TestAutoReplyRuleService_ListRules(t *testing.T) {
|
|
db, _, svc := setupAutoReplyRuleService(t)
|
|
|
|
account := createTestAccount(t, db)
|
|
assistant := createTestAssistant(t, db, account.ID, "TestAssistant")
|
|
|
|
for i := 0; i < 3; i++ {
|
|
req := &CreateAutoReplyRuleRequest{
|
|
AssistantID: assistant.ID,
|
|
Name: "Rule" + string(rune('A'+i)),
|
|
Mode: "static",
|
|
ResponseText: "response " + string(rune('A'+i)),
|
|
}
|
|
_, err := svc.CreateRule(context.Background(), account.ID, req)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
rules, count, err := svc.ListRules(context.Background(), account.ID, 0, 10)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, int64(3), count)
|
|
assert.Len(t, rules, 3)
|
|
}
|
|
|
|
// --- EvaluateRules (condition matching) ---
|
|
func TestAutoReplyRuleService_EvaluateRules_ContainsKeyword(t *testing.T) {
|
|
db, mockProvider, svc := setupAutoReplyRuleService(t)
|
|
|
|
account := createTestAccount(t, db)
|
|
assistant := createTestAssistant(t, db, account.ID, "TestAssistant")
|
|
|
|
req := &CreateAutoReplyRuleRequest{
|
|
AssistantID: assistant.ID,
|
|
Name: "UrgentHandler",
|
|
Mode: "static",
|
|
ResponseText: "We will respond to your urgent inquiry shortly.",
|
|
Priority: 10,
|
|
Conditions: []model.AutoReplyCondition{
|
|
{Field: "message_content", Operator: "contains", Value: "urgent"},
|
|
},
|
|
}
|
|
rule, err := svc.CreateRule(context.Background(), account.ID, req)
|
|
require.NoError(t, err)
|
|
|
|
// Activate the rule
|
|
newStatus := model.AutoReplyRuleStatusActive
|
|
_, err = svc.UpdateRule(context.Background(), account.ID, rule.ID, &UpdateAutoReplyRuleRequest{Status: &newStatus})
|
|
require.NoError(t, err)
|
|
|
|
evalCtx := &AutoReplyEvaluationContext{
|
|
AccountID: account.ID,
|
|
InboxID: 1,
|
|
ConversationID: 1,
|
|
MessageContent: "This is an urgent issue!",
|
|
SenderType: "contact",
|
|
}
|
|
|
|
result, err := svc.EvaluateRules(context.Background(), evalCtx)
|
|
require.NoError(t, err)
|
|
assert.True(t, result.ShouldReply)
|
|
assert.Equal(t, "We will respond to your urgent inquiry shortly.", result.ReplyContent)
|
|
assert.Equal(t, model.AutoReplyRuleModeStatic, result.ReplyMode)
|
|
|
|
// Mock provider should not have been called (static mode)
|
|
assert.Nil(t, mockProvider.lastChatRequest)
|
|
}
|
|
|
|
func TestAutoReplyRuleService_EvaluateRules_NoMatch(t *testing.T) {
|
|
db, _, svc := setupAutoReplyRuleService(t)
|
|
|
|
account := createTestAccount(t, db)
|
|
assistant := createTestAssistant(t, db, account.ID, "TestAssistant")
|
|
|
|
req := &CreateAutoReplyRuleRequest{
|
|
AssistantID: assistant.ID,
|
|
Name: "UrgentHandler",
|
|
Mode: "static",
|
|
ResponseText: "Urgent response",
|
|
Priority: 10,
|
|
Conditions: []model.AutoReplyCondition{
|
|
{Field: "message_content", Operator: "contains", Value: "urgent"},
|
|
},
|
|
}
|
|
rule, err := svc.CreateRule(context.Background(), account.ID, req)
|
|
require.NoError(t, err)
|
|
|
|
newStatus := model.AutoReplyRuleStatusActive
|
|
_, err = svc.UpdateRule(context.Background(), account.ID, rule.ID, &UpdateAutoReplyRuleRequest{Status: &newStatus})
|
|
require.NoError(t, err)
|
|
|
|
evalCtx := &AutoReplyEvaluationContext{
|
|
AccountID: account.ID,
|
|
InboxID: 1,
|
|
ConversationID: 1,
|
|
MessageContent: "Just a casual question about pricing",
|
|
SenderType: "contact",
|
|
}
|
|
|
|
result, err := svc.EvaluateRules(context.Background(), evalCtx)
|
|
require.NoError(t, err)
|
|
assert.False(t, result.ShouldReply)
|
|
}
|
|
|
|
func TestAutoReplyRuleService_EvaluateRules_LLMMode(t *testing.T) {
|
|
db, mockProvider, svc := setupAutoReplyRuleService(t)
|
|
|
|
account := createTestAccount(t, db)
|
|
assistant := createTestAssistant(t, db, account.ID, "TestAssistant")
|
|
|
|
req := &CreateAutoReplyRuleRequest{
|
|
AssistantID: assistant.ID,
|
|
Name: "LLMHandler",
|
|
Mode: "llm",
|
|
Priority: 10,
|
|
Conditions: []model.AutoReplyCondition{
|
|
{Field: "message_content", Operator: "contains", Value: "help"},
|
|
},
|
|
}
|
|
rule, err := svc.CreateRule(context.Background(), account.ID, req)
|
|
require.NoError(t, err)
|
|
|
|
newStatus := model.AutoReplyRuleStatusActive
|
|
_, err = svc.UpdateRule(context.Background(), account.ID, rule.ID, &UpdateAutoReplyRuleRequest{Status: &newStatus})
|
|
require.NoError(t, err)
|
|
|
|
// Setup mock LLM response
|
|
mockProvider.chatResponse = &llm.ChatResponse{
|
|
Choices: []llm.ChatChoice{
|
|
{Message: llm.ChatMessage{Role: "assistant", Content: "Thank you for contacting us. We will review your request shortly."}},
|
|
},
|
|
}
|
|
|
|
evalCtx := &AutoReplyEvaluationContext{
|
|
AccountID: account.ID,
|
|
InboxID: 1,
|
|
ConversationID: 1,
|
|
MessageContent: "Hello, I need help with my account",
|
|
SenderType: "contact",
|
|
}
|
|
|
|
result, err := svc.EvaluateRules(context.Background(), evalCtx)
|
|
require.NoError(t, err)
|
|
assert.True(t, result.ShouldReply)
|
|
assert.Equal(t, model.AutoReplyRuleModeLLM, result.ReplyMode)
|
|
assert.Contains(t, result.ReplyContent, "Thank you")
|
|
assert.NotNil(t, mockProvider.lastChatRequest)
|
|
} |