Files
lijiaoqiao/projects/ai-customer-service/internal/service/reply/service_test.go
Your Name 687c4535f8 fix: P0-1 RateLimiter并发写安全 + P0-2工单操作错误码区分 + P1 rows.Close修复
P0-1 (limits.go): Allow()方法改为全程使用写锁保护counters map读写,避免RLock写入时的data race
P0-2 (ticket_workflow.go+ticket_handler.go): Assign/Resolve/Close操作先查询ticket存在性和状态,返回明确的CS_TICKET_4001/CS_TKT_4002/CS_TICKET_4092/CS_TICKET_4093错误码,handler根据错误前缀路由HTTP状态码
P1-1 (ticket_store.go): 移除GetStats中3处手动rows.Close(),只保留defer Close()
2026-05-01 20:56:25 +08:00

163 lines
4.5 KiB
Go

package reply
import (
"context"
"strings"
"testing"
"github.com/bridge/ai-customer-service/internal/domain/intent"
"github.com/bridge/ai-customer-service/internal/store/memory"
)
func TestGenerate_NilIntent(t *testing.T) {
knowledge := memory.NewKnowledgeStore()
svc := NewService(knowledge)
result := svc.Generate(context.Background(), nil)
if result == "" {
t.Error("Generate with nil intent should return non-empty answer")
}
// Should return general fallback
if result != knowledge.Answer(intent.IntentGeneral) {
t.Errorf("expected general fallback answer, got %q", result)
}
}
func TestGenerate_ValidIntent(t *testing.T) {
knowledge := memory.NewKnowledgeStore()
svc := NewService(knowledge)
testCases := []struct {
intentName string
expectEmpty bool
}{
{"quota", false},
{"token", false},
{"error", false},
{"general", false},
}
for _, tc := range testCases {
t.Run(tc.intentName, func(t *testing.T) {
intentResult := &intent.Result{Intent: tc.intentName}
result := svc.Generate(context.Background(), intentResult)
if tc.expectEmpty && result != "" {
t.Errorf("expected empty for intent %q, got %q", tc.intentName, result)
}
if !tc.expectEmpty && result == "" {
t.Errorf("expected non-empty for intent %q", tc.intentName)
}
})
}
}
func TestGenerate_UnknownIntent(t *testing.T) {
knowledge := memory.NewKnowledgeStore()
svc := NewService(knowledge)
// Unknown intent should return general fallback
intentResult := &intent.Result{Intent: "unknown-intent-xyz"}
result := svc.Generate(context.Background(), intentResult)
generalAnswer := knowledge.Answer(intent.IntentGeneral)
if result != generalAnswer {
t.Errorf("unknown intent: expected general fallback %q, got %q", generalAnswer, result)
}
}
func TestGenerate_ContentTruncation(t *testing.T) {
knowledge := memory.NewKnowledgeStore()
svc := NewService(knowledge)
// The Generate method itself doesn't truncate content.
// It returns answers from the knowledge store.
// This test verifies the behavior: returns non-empty string.
intentResult := &intent.Result{Intent: "general"}
result := svc.Generate(context.Background(), intentResult)
// Verify we get a non-empty response
if result == "" {
t.Error("Generate should return non-empty answer")
}
// Check that result length is reasonable (not unlimited)
// The knowledge store answers are short by design
if len(result) > 5000 {
t.Logf("Warning: result length %d seems large", len(result))
}
}
func TestGenerate_EmptyContent(t *testing.T) {
knowledge := memory.NewKnowledgeStore()
svc := NewService(knowledge)
// Empty intent content should still return something (general fallback)
intentResult := &intent.Result{Intent: ""}
result := svc.Generate(context.Background(), intentResult)
// Should return general fallback, not empty string
generalAnswer := knowledge.Answer(intent.IntentGeneral)
if result != generalAnswer {
t.Errorf("empty intent: expected general fallback %q, got %q", generalAnswer, result)
}
}
func TestService_NewService(t *testing.T) {
knowledge := memory.NewKnowledgeStore()
svc := NewService(knowledge)
if svc == nil {
t.Error("NewService returned nil")
}
if svc.knowledge == nil {
t.Error("svc.knowledge is nil")
}
}
func TestGenerate_MultipleIntents(t *testing.T) {
knowledge := memory.NewKnowledgeStore()
svc := NewService(knowledge)
intents := []string{"quota", "token", "error", "general"}
results := make([]string, len(intents))
for i, intentName := range intents {
intentResult := &intent.Result{Intent: intentName}
results[i] = svc.Generate(context.Background(), intentResult)
}
// All results should be non-empty
for i, result := range results {
if strings.TrimSpace(result) == "" {
t.Errorf("intent %q returned empty result", intents[i])
}
}
// At least some results should be different (different answers)
differentCount := 0
for i := 1; i < len(results); i++ {
if results[i] != results[0] {
differentCount++
}
}
if differentCount == 0 {
t.Log("Warning: all intents returned the same answer")
}
}
func TestGenerate_ContextCancellation(t *testing.T) {
knowledge := memory.NewKnowledgeStore()
svc := NewService(knowledge)
ctx, cancel := context.WithCancel(context.Background())
cancel() // Cancel immediately
// Should still return a result even with cancelled context
intentResult := &intent.Result{Intent: "general"}
result := svc.Generate(ctx, intentResult)
if result == "" {
t.Error("Generate with cancelled context should still return answer")
}
}