Files
dobotai-platform/internal/coverage/service_test.go

216 lines
5.2 KiB
Go

package coverage
import (
"context"
"testing"
"gitea.zheromiku.top/Weknora/dobotai-platform/internal/core"
)
// mockRepo implements Repository for testing.
type mockRepo struct {
saved []Decision
err error
}
func (m *mockRepo) SaveCoverageDecision(_ context.Context, d Decision) error {
if m.err != nil {
return m.err
}
m.saved = append(m.saved, d)
return nil
}
func TestNewService_RejectsNilRepo(t *testing.T) {
_, err := NewService(nil)
if err == nil {
t.Fatal("expected error for nil repo, got nil")
}
}
func TestNewService_SucceedsWithRepo(t *testing.T) {
s, err := NewService(&mockRepo{})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if s == nil {
t.Fatal("expected non-nil service")
}
}
func TestService_EvaluateAndPersist_Covered(t *testing.T) {
repo := &mockRepo{}
s, err := NewService(repo)
if err != nil {
t.Fatal(err)
}
turn := core.Turn{
ID: "turn-1",
TenantKey: "t1",
Channel: "dingtalk",
Status: "succeeded",
Answer: "根据知识库,答案是...",
References: []core.Reference{
{ID: "r1", Score: 0.9},
},
}
d, err := s.EvaluateAndPersist(context.Background(), turn)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if d.Verdict != VerdictCovered {
t.Errorf("verdict = %s, want covered", d.Verdict)
}
if len(repo.saved) != 1 {
t.Fatalf("expected 1 saved decision, got %d", len(repo.saved))
}
if repo.saved[0].Verdict != VerdictCovered {
t.Errorf("saved verdict = %s, want covered", repo.saved[0].Verdict)
}
}
func TestService_EvaluateAndPersist_UncoveredNoReferences(t *testing.T) {
repo := &mockRepo{}
s, _ := NewService(repo)
turn := core.Turn{
ID: "turn-2",
TenantKey: "t1",
Channel: "dingtalk",
Status: "succeeded",
Answer: "正常回答但没有引用",
KnowledgeSearched: true,
}
d, err := s.EvaluateAndPersist(context.Background(), turn)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if d.Verdict != VerdictUncovered {
t.Errorf("verdict = %s, want uncovered", d.Verdict)
}
if d.ReasonCode != ReasonUncoveredNoReferences {
t.Errorf("reason_code = %s, want %s", d.ReasonCode, ReasonUncoveredNoReferences)
}
if len(repo.saved) != 1 {
t.Fatalf("expected 1 saved decision, got %d", len(repo.saved))
}
}
func TestService_EvaluateAndPersist_UncoveredExplicitNoAnswer(t *testing.T) {
repo := &mockRepo{}
s, _ := NewService(repo)
turn := core.Turn{
ID: "turn-3",
TenantKey: "t1",
Channel: "dingtalk",
Status: "succeeded",
Answer: "未找到相关结果",
References: []core.Reference{
{ID: "r1", Score: 0.5},
},
}
d, err := s.EvaluateAndPersist(context.Background(), turn)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if d.Verdict != VerdictUncovered {
t.Errorf("verdict = %s, want uncovered", d.Verdict)
}
if d.ReasonCode != ReasonUncoveredExplicitNoAnswer {
t.Errorf("reason_code = %s, want %s", d.ReasonCode, ReasonUncoveredExplicitNoAnswer)
}
}
func TestService_EvaluateAndPersist_RejectsFailedTurn(t *testing.T) {
repo := &mockRepo{}
s, _ := NewService(repo)
turn := core.Turn{
ID: "turn-4",
Status: "failed",
}
_, err := s.EvaluateAndPersist(context.Background(), turn)
if err == nil {
t.Fatal("expected error for failed turn, got nil")
}
if len(repo.saved) != 0 {
t.Errorf("expected 0 saved decisions for failed turn, got %d", len(repo.saved))
}
}
func TestService_EvaluateAndPersist_PropagatesRepoError(t *testing.T) {
repo := &mockRepo{err: ErrRepository}
s, _ := NewService(repo)
turn := core.Turn{
ID: "turn-5",
Status: "succeeded",
Answer: "正常回答",
References: []core.Reference{
{ID: "r1", Score: 0.5},
},
}
_, err := s.EvaluateAndPersist(context.Background(), turn)
if err == nil {
t.Fatal("expected error from repo, got nil")
}
}
func TestService_ObserveSucceededTurnPersistsDecision(t *testing.T) {
repo := &mockRepo{}
s, err := NewService(repo)
if err != nil {
t.Fatal(err)
}
turn := core.Turn{
ID: "turn-observed",
TenantKey: "tenant",
Channel: "dingtalk",
Status: "succeeded",
Answer: "正常回答",
References: []core.Reference{
{ID: "r1", Score: 0.5},
},
}
if err := s.ObserveSucceededTurn(context.Background(), turn); err != nil {
t.Fatalf("ObserveSucceededTurn: %v", err)
}
if len(repo.saved) != 1 || repo.saved[0].TurnID != "turn-observed" {
t.Fatalf("saved=%+v, want turn-observed", repo.saved)
}
}
func TestService_EvaluateAndPersist_DecisionHasEvaluatorVersion(t *testing.T) {
repo := &mockRepo{}
s, _ := NewService(repo)
turn := core.Turn{
ID: "turn-6",
TenantKey: "t1",
Channel: "dingtalk",
Status: "succeeded",
Answer: "正常回答",
References: []core.Reference{
{ID: "r1", Score: 0.5},
},
}
d, err := s.EvaluateAndPersist(context.Background(), turn)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if d.EvaluatorVersion != EvaluatorVersion {
t.Errorf("evaluator_version = %s, want %s", d.EvaluatorVersion, EvaluatorVersion)
}
if repo.saved[0].EvaluatorVersion != EvaluatorVersion {
t.Errorf("saved evaluator_version = %s, want %s", repo.saved[0].EvaluatorVersion, EvaluatorVersion)
}
}