Files
gochat/backend/internal/service/captain_assistant_retrieval_test.go
T
Rogeeandrogee 60ad320e8d [H-337] Fix Captain provider runtime and knowledge flow (#61)
* fix(H-337): configure Captain provider runtime

* fix(captain): make knowledge rebuild atomic

* fix(captain): scope retrieval provider failures

---------

Co-authored-by: Rogee <rogee@ipao.vip>
2026-08-20 14:46:01 +08:00

88 lines
3.8 KiB
Go

package service
import (
"context"
"errors"
"net"
"testing"
"github.com/gochat/gochat/internal/llm"
"github.com/gochat/gochat/internal/model"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestCaptainAssistantFAQRetrievalSeparatesEmptyAndFailures(t *testing.T) {
assistant := &model.CaptainAssistant{Name: "Fin", Status: model.AssistantStatusActive, Config: []byte(`{"feature_faq":true}`)}
history := []PlaygroundMessage{{Role: "user", Content: "How do refunds work?"}}
embedding := &llm.EmbeddingResponse{Data: []llm.EmbeddingData{{Embedding: []float64{0.1, 0.2, 0.3}}}}
t.Run("disabled FAQ skips retrieval and still allows chat", func(t *testing.T) {
provider := &mockLLMProvider{
embeddingError: errors.New("must not be called"),
chatResponse: &llm.ChatResponse{Choices: []llm.ChatChoice{{Message: llm.ChatMessage{Content: "Chat without FAQ."}}}},
}
repo := &mockResponseRepo{searchByEmbeddingError: errors.New("must not be called")}
svc := &CaptainAssistantService{responseRepo: repo, llmProvider: provider, promptBuilder: NewSystemPromptBuilder()}
disabled := &model.CaptainAssistant{Name: "Fin", Status: model.AssistantStatusActive, Config: []byte(`{"feature_faq":false}`)}
content, err := svc.generatePlaygroundLLMResponse(context.Background(), disabled, history)
require.NoError(t, err)
assert.Equal(t, "Chat without FAQ.", content)
assert.Zero(t, provider.embeddingCalls)
assert.Zero(t, repo.searchByEmbeddingCalls)
require.NotNil(t, provider.lastChatRequest)
})
t.Run("empty results still allow chat", func(t *testing.T) {
provider := &mockLLMProvider{
embeddingResponse: embedding,
chatResponse: &llm.ChatResponse{Choices: []llm.ChatChoice{{Message: llm.ChatMessage{Content: "No matching FAQ."}}}},
}
svc := &CaptainAssistantService{responseRepo: &mockResponseRepo{}, llmProvider: provider, promptBuilder: NewSystemPromptBuilder()}
content, err := svc.generatePlaygroundLLMResponse(context.Background(), assistant, history)
require.NoError(t, err)
assert.Equal(t, "No matching FAQ.", content)
require.NotNil(t, provider.lastChatRequest)
assert.NotContains(t, provider.lastChatRequest.Messages[0].Content, "[FAQ 1]")
})
t.Run("embedding provider failure stops chat", func(t *testing.T) {
provider := &mockLLMProvider{embeddingError: &net.OpError{Op: "dial", Net: "tcp", Err: errors.New("connection refused")}}
svc := &CaptainAssistantService{responseRepo: &mockResponseRepo{}, llmProvider: provider, promptBuilder: NewSystemPromptBuilder()}
_, err := svc.generatePlaygroundLLMResponse(context.Background(), assistant, history)
require.ErrorIs(t, err, ErrCaptainKnowledgeRetrieval)
assert.Nil(t, provider.lastChatRequest)
})
for name, response := range map[string]*llm.EmbeddingResponse{
"nil response": nil,
"empty data": {},
"empty first vector": {Data: []llm.EmbeddingData{{}}},
} {
t.Run(name+" stops chat", func(t *testing.T) {
provider := &mockLLMProvider{embeddingResponse: response}
svc := &CaptainAssistantService{responseRepo: &mockResponseRepo{}, llmProvider: provider, promptBuilder: NewSystemPromptBuilder()}
_, err := svc.generatePlaygroundLLMResponse(context.Background(), assistant, history)
require.ErrorIs(t, err, ErrCaptainKnowledgeRetrieval)
assert.Nil(t, provider.lastChatRequest)
})
}
t.Run("FAQ search failure stops chat", func(t *testing.T) {
provider := &mockLLMProvider{embeddingResponse: embedding}
svc := &CaptainAssistantService{
responseRepo: &mockResponseRepo{searchByEmbeddingError: errors.New("pgvector unavailable")},
llmProvider: provider,
promptBuilder: NewSystemPromptBuilder(),
}
_, err := svc.generatePlaygroundLLMResponse(context.Background(), assistant, history)
require.ErrorIs(t, err, ErrCaptainKnowledgeRetrieval)
assert.Nil(t, provider.lastChatRequest)
})
}