Files
gochat/internal/service/auto_reply_rule_service_test.go
T
2026-06-04 15:44:48 +08:00

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