Files
gochat/backend/internal/search/coverage3_test.go
T

474 lines
15 KiB
Go

package search
import (
"context"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/gochat/gochat/internal/model"
)
// --- ParseSearchMode ---
func TestParseSearchMode_Trigram_Cov3(t *testing.T) {
assert.Equal(t, SearchModeTrigram, ParseSearchMode("trigram"))
assert.Equal(t, SearchModeTrigram, ParseSearchMode("TRIGRAM"))
}
func TestParseSearchMode_Default_Cov3(t *testing.T) {
assert.Equal(t, SearchModeILike, ParseSearchMode(""))
assert.Equal(t, SearchModeILike, ParseSearchMode("unknown"))
}
// --- SearchEngineDB ---
func TestSearchEngineDB_Search_Cov3(t *testing.T) {
repo := &mockSearchRepo{
conversations: []model.Conversation{
{Base: model.Base{ID: 1}, AccountID: 1, Status: "open"},
},
convCount: 1,
}
engine := NewSearchEngineDB(repo)
resp, err := engine.Search(context.Background(), 1, "test", &SearchFilter{
Page: 1,
PerPage: 25,
SortBy: "created_at",
SortOrder: "desc",
})
require.NoError(t, err)
assert.NotNil(t, resp)
assert.NotEmpty(t, resp.Results)
}
func TestSearchEngineDB_IndexDocument_Cov3(t *testing.T) {
engine := NewSearchEngineDB(&mockSearchRepo{})
err := engine.IndexDocument(context.Background(), SearchDocument{ID: 1, Type: ResultTypeConversation})
require.NoError(t, err)
}
func TestSearchEngineDB_IndexBatch_Cov3(t *testing.T) {
engine := NewSearchEngineDB(&mockSearchRepo{})
err := engine.IndexBatch(context.Background(), []SearchDocument{{ID: 1}, {ID: 2}})
require.NoError(t, err)
}
func TestSearchEngineDB_DeleteDocument_Cov3(t *testing.T) {
engine := NewSearchEngineDB(&mockSearchRepo{})
err := engine.DeleteDocument(context.Background(), ResultTypeConversation, 1, 1)
require.NoError(t, err)
}
func TestSearchEngineDB_Bootstrap_Cov3(t *testing.T) {
engine := NewSearchEngineDB(&mockSearchRepo{})
err := engine.Bootstrap(context.Background())
require.NoError(t, err)
}
func TestSearchEngineDB_Close_Cov3(t *testing.T) {
engine := NewSearchEngineDB(&mockSearchRepo{})
err := engine.Close()
require.NoError(t, err)
}
// --- CompanyDocument ---
func TestCompanyDocument_Cov3(t *testing.T) {
company := model.Company{
ID: 1,
AccountID: 1,
Name: "Acme Corp",
Description: "A company",
Domain: "acme.com",
WebsiteURL: "https://acme.com",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
doc := CompanyDocument(company)
assert.Equal(t, "Acme Corp", doc.Title)
assert.Equal(t, ResultTypeCompany, doc.Type)
assert.Contains(t, doc.Content, "A company")
assert.Contains(t, doc.Content, "acme.com")
assert.NotEmpty(t, doc.UID)
}
// --- messageConversationDisplayID ---
func TestMessageConversationDisplayID_WithDisplayID_Cov3(t *testing.T) {
displayID := uint(200)
conv := &model.Conversation{Base: model.Base{ID: 1}, DisplayID: &displayID}
assert.Equal(t, uint(200), messageConversationDisplayID(conv))
}
func TestMessageConversationDisplayID_NilConversation_Cov3(t *testing.T) {
assert.Equal(t, uint(0), messageConversationDisplayID(nil))
}
func TestMessageConversationDisplayID_NoDisplayID_Cov3(t *testing.T) {
conv := &model.Conversation{Base: model.Base{ID: 5}}
assert.Equal(t, uint(5), messageConversationDisplayID(conv))
}
func TestMessageConversationDisplayID_ZeroDisplayID_Cov3(t *testing.T) {
displayID := uint(0)
conv := &model.Conversation{Base: model.Base{ID: 5}, DisplayID: &displayID}
assert.Equal(t, uint(5), messageConversationDisplayID(conv))
}
// --- jsonObject ---
func TestJsonObject_Empty_Cov3(t *testing.T) {
result := jsonObject(nil)
assert.Empty(t, result)
}
func TestJsonObject_NullString_Cov3(t *testing.T) {
result := jsonObject([]byte("null"))
assert.Empty(t, result)
}
func TestJsonObject_Valid_Cov3(t *testing.T) {
result := jsonObject([]byte(`{"key":"value"}`))
assert.Equal(t, "value", result["key"])
}
func TestJsonObject_Invalid_Cov3(t *testing.T) {
result := jsonObject([]byte(`{invalid`))
assert.Empty(t, result)
}
// --- metadataValue ---
func TestMetadataValue_Empty_Cov3(t *testing.T) {
assert.Equal(t, "", metadataValue("", "key"))
}
func TestMetadataValue_InvalidJSON_Cov3(t *testing.T) {
assert.Equal(t, "", metadataValue("{invalid", "key"))
}
func TestMetadataValue_StringValue_Cov3(t *testing.T) {
assert.Equal(t, "hello", metadataValue(`{"key":"hello"}`, "key"))
}
func TestMetadataValue_NonStringValue_Cov3(t *testing.T) {
assert.Equal(t, "", metadataValue(`{"key":123}`, "key"))
}
func TestMetadataValue_MissingKey_Cov3(t *testing.T) {
assert.Equal(t, "", metadataValue(`{"other":"value"}`, "key"))
}
// --- firstNonEmpty ---
func TestFirstNonEmpty_Cov3(t *testing.T) {
assert.Equal(t, "first", firstNonEmpty("first", "second"))
assert.Equal(t, "second", firstNonEmpty("", "second"))
assert.Equal(t, "third", firstNonEmpty(" ", "", "third"))
assert.Equal(t, "", firstNonEmpty("", " ", ""))
}
// --- conversationSearchTitle ---
func TestConversationSearchTitle_WithDisplayID_Cov3(t *testing.T) {
displayID := uint(200)
conv := model.Conversation{Base: model.Base{ID: 1}, DisplayID: &displayID}
title := conversationSearchTitle(conv)
assert.Contains(t, title, "200")
}
func TestConversationSearchTitle_WithoutDisplayID_Cov3(t *testing.T) {
conv := model.Conversation{Base: model.Base{ID: 42}}
title := conversationSearchTitle(conv)
assert.Contains(t, title, "42")
}
// --- SearchService additional tests ---
func TestNewSearchServiceWithEngine_Cov3(t *testing.T) {
engine := NewSearchEngineDB(&mockSearchRepo{})
svc := NewSearchServiceWithEngine(engine, &mockSearchRepo{})
assert.NotNil(t, svc)
assert.NotNil(t, svc.engine)
}
func TestSearchService_SearchResolvedContacts_Cov3(t *testing.T) {
repo := &mockSearchRepo{
contacts: []model.Contact{
{Base: model.Base{ID: 1}, AccountID: 1, Name: "Alice"},
},
contactCount: 1,
}
svc := NewSearchService(repo)
results, total, err := svc.SearchResolvedContacts(context.Background(), 1, "alice", &SearchFilter{
Page: 1, PerPage: 25, SortBy: "created_at", SortOrder: "desc",
})
require.NoError(t, err)
assert.Equal(t, int64(1), total)
assert.Len(t, results, 1)
}
func TestSearchService_SearchCompanies_Cov3(t *testing.T) {
repo := &mockSearchRepo{
companies: []model.Company{
{ID: 1, AccountID: 1, Name: "Acme"},
},
companyCount: 1,
}
svc := NewSearchService(repo)
results, total, err := svc.SearchCompanies(context.Background(), 1, "acme", &SearchFilter{
Page: 1, PerPage: 25,
})
require.NoError(t, err)
assert.Equal(t, int64(1), total)
assert.Len(t, results, 1)
}
func TestSearchService_SearchCompanies_Error_Cov3(t *testing.T) {
repo := &mockSearchRepo{
companyErr: context.DeadlineExceeded,
}
svc := NewSearchService(repo)
_, _, err := svc.SearchCompanies(context.Background(), 1, "acme", &SearchFilter{
Page: 1, PerPage: 25,
})
assert.Error(t, err)
}
func TestSearchService_SearchCompanies_WithEngine_Cov3(t *testing.T) {
engine := NewSearchEngineDB(&mockSearchRepo{
companies: []model.Company{
{ID: 1, AccountID: 1, Name: "Acme"},
},
companyCount: 1,
})
svc := NewSearchServiceWithEngine(engine, &mockSearchRepo{})
results, total, err := svc.SearchCompanies(context.Background(), 1, "acme", &SearchFilter{
Page: 1, PerPage: 25,
})
require.NoError(t, err)
assert.Equal(t, int64(1), total)
assert.Len(t, results, 1)
}
func TestSearchService_SearchArticles_WithEngine_Cov3(t *testing.T) {
engine := NewSearchEngineDB(&mockSearchRepo{
articles: []model.Article{
{Base: model.Base{ID: 1}, AccountID: 1, Title: "How to"},
},
articleCount: 1,
})
svc := NewSearchServiceWithEngine(engine, &mockSearchRepo{})
results, total, err := svc.SearchArticles(context.Background(), 1, "how", &SearchFilter{
Page: 1, PerPage: 25,
})
require.NoError(t, err)
assert.Equal(t, int64(1), total)
assert.Len(t, results, 1)
}
func TestSearchService_GlobalSearch_WithEngine_Cov3(t *testing.T) {
engine := NewSearchEngineDB(&mockSearchRepo{
conversations: []model.Conversation{
{Base: model.Base{ID: 1}, AccountID: 1},
},
convCount: 1,
})
svc := NewSearchServiceWithEngine(engine, &mockSearchRepo{})
resp, err := svc.GlobalSearch(context.Background(), 1, "test", &SearchFilter{
Page: 1, PerPage: 25, SortBy: "created_at", SortOrder: "desc",
})
require.NoError(t, err)
assert.NotNil(t, resp)
}
func TestSearchService_GlobalSearch_WithEngine_Error_Cov3(t *testing.T) {
engine := &mockEngine_Cov3{err: context.DeadlineExceeded}
svc := NewSearchServiceWithEngine(engine, &mockSearchRepo{})
_, err := svc.GlobalSearch(context.Background(), 1, "test", &SearchFilter{
Page: 1, PerPage: 25, SortBy: "created_at", SortOrder: "desc",
})
assert.Error(t, err)
}
func TestSearchService_IndexDocument_NoEngine_Cov3(t *testing.T) {
svc := NewSearchService(&mockSearchRepo{})
err := svc.IndexDocument(context.Background(), SearchDocument{})
require.NoError(t, err)
}
func TestSearchService_IndexDocument_WithEngine_Cov3(t *testing.T) {
engine := NewSearchEngineDB(&mockSearchRepo{})
svc := NewSearchServiceWithEngine(engine, &mockSearchRepo{})
err := svc.IndexDocument(context.Background(), SearchDocument{ID: 1})
require.NoError(t, err)
}
func TestSearchService_IndexConversation_Cov3(t *testing.T) {
engine := NewSearchEngineDB(&mockSearchRepo{})
svc := NewSearchServiceWithEngine(engine, &mockSearchRepo{})
err := svc.IndexConversation(context.Background(), &model.Conversation{Base: model.Base{ID: 1}})
require.NoError(t, err)
}
func TestSearchService_IndexConversation_Nil_Cov3(t *testing.T) {
svc := NewSearchService(&mockSearchRepo{})
err := svc.IndexConversation(context.Background(), nil)
require.NoError(t, err)
}
func TestSearchService_DeleteConversation_Cov3(t *testing.T) {
engine := NewSearchEngineDB(&mockSearchRepo{})
svc := NewSearchServiceWithEngine(engine, &mockSearchRepo{})
err := svc.DeleteConversation(context.Background(), 1, 1)
require.NoError(t, err)
}
func TestSearchService_IndexMessage_Cov3(t *testing.T) {
engine := NewSearchEngineDB(&mockSearchRepo{})
svc := NewSearchServiceWithEngine(engine, &mockSearchRepo{})
err := svc.IndexMessage(context.Background(), &model.Message{Base: model.Base{ID: 1}})
require.NoError(t, err)
}
func TestSearchService_IndexMessage_Nil_Cov3(t *testing.T) {
svc := NewSearchService(&mockSearchRepo{})
err := svc.IndexMessage(context.Background(), nil)
require.NoError(t, err)
}
func TestSearchService_DeleteMessage_Cov3(t *testing.T) {
engine := NewSearchEngineDB(&mockSearchRepo{})
svc := NewSearchServiceWithEngine(engine, &mockSearchRepo{})
err := svc.DeleteMessage(context.Background(), 1, 1)
require.NoError(t, err)
}
func TestSearchService_IndexContact_Cov3(t *testing.T) {
engine := NewSearchEngineDB(&mockSearchRepo{})
svc := NewSearchServiceWithEngine(engine, &mockSearchRepo{})
err := svc.IndexContact(context.Background(), &model.Contact{Base: model.Base{ID: 1}})
require.NoError(t, err)
}
func TestSearchService_IndexContact_Nil_Cov3(t *testing.T) {
svc := NewSearchService(&mockSearchRepo{})
err := svc.IndexContact(context.Background(), nil)
require.NoError(t, err)
}
func TestSearchService_DeleteContact_Cov3(t *testing.T) {
engine := NewSearchEngineDB(&mockSearchRepo{})
svc := NewSearchServiceWithEngine(engine, &mockSearchRepo{})
err := svc.DeleteContact(context.Background(), 1, 1)
require.NoError(t, err)
}
func TestSearchService_IndexCompany_Cov3(t *testing.T) {
engine := NewSearchEngineDB(&mockSearchRepo{})
svc := NewSearchServiceWithEngine(engine, &mockSearchRepo{})
err := svc.IndexCompany(context.Background(), &model.Company{ID: 1})
require.NoError(t, err)
}
// --- cloneSearchFilter ---
func TestCloneSearchFilter_Nil_Cov3(t *testing.T) {
clone := cloneSearchFilter(nil)
assert.NotNil(t, clone)
assert.Equal(t, 1, clone.Page)
}
func TestCloneSearchFilter_Full_Cov3(t *testing.T) {
original := &SearchFilter{
Page: 2,
PerPage: 50,
SortBy: "updated_at",
Types: []SearchResultType{ResultTypeConversation},
Status: []string{"open"},
Labels: []string{"vip"},
AccessibleInboxIDs: []uint{1, 2},
}
clone := cloneSearchFilter(original)
assert.Equal(t, original.Page, clone.Page)
assert.Equal(t, original.PerPage, clone.PerPage)
assert.Equal(t, original.Types, clone.Types)
assert.Equal(t, original.Status, clone.Status)
assert.Equal(t, original.Labels, clone.Labels)
assert.Equal(t, original.AccessibleInboxIDs, clone.AccessibleInboxIDs)
// Verify deep copy
clone.Types[0] = ResultTypeMessage
assert.Equal(t, ResultTypeConversation, original.Types[0])
}
// --- withSearchAccess ---
func TestSearchService_WithSearchAccess_NoUser_Cov3(t *testing.T) {
svc := NewSearchService(&mockSearchRepo{})
filter := &SearchFilter{Page: 1, PerPage: 25}
result := svc.withSearchAccess(context.Background(), 1, filter)
assert.False(t, result.EnforceInboxAccess)
}
func TestSearchService_WithSearchAccess_ZeroUser_Cov3(t *testing.T) {
svc := NewSearchService(&mockSearchRepo{})
userID := uint(0)
filter := &SearchFilter{Page: 1, PerPage: 25, CurrentUserID: &userID}
result := svc.withSearchAccess(context.Background(), 1, filter)
assert.False(t, result.EnforceInboxAccess)
}
func TestSearchService_WithSearchAccess_WithUser_Cov3(t *testing.T) {
repo := &mockSearchRepo{
access: SearchAccess{SkipInboxFiltering: true, InboxIDs: []uint{1, 2}},
}
svc := NewSearchService(repo)
userID := uint(5)
filter := &SearchFilter{Page: 1, PerPage: 25, CurrentUserID: &userID}
result := svc.withSearchAccess(context.Background(), 1, filter)
assert.True(t, result.EnforceInboxAccess == false) // SkipInboxFiltering=true → EnforceInboxAccess=false
assert.Equal(t, []uint{1, 2}, result.AccessibleInboxIDs)
}
func TestSearchService_WithSearchAccess_AccessError_Cov3(t *testing.T) {
repo := &mockSearchRepo{
accessErr: context.DeadlineExceeded,
}
svc := NewSearchService(repo)
userID := uint(5)
filter := &SearchFilter{Page: 1, PerPage: 25, CurrentUserID: &userID}
result := svc.withSearchAccess(context.Background(), 1, filter)
assert.True(t, result.EnforceInboxAccess)
assert.Nil(t, result.AccessibleInboxIDs)
}
func TestSearchService_WithSearchAccess_NoResolver_Cov3(t *testing.T) {
svc := &SearchService{searchRepo: &mockSearchRepo{}}
userID := uint(5)
filter := &SearchFilter{Page: 1, PerPage: 25, CurrentUserID: &userID}
result := svc.withSearchAccess(context.Background(), 1, filter)
assert.False(t, result.EnforceInboxAccess)
}
// --- mockEngine for testing ---
type mockEngine_Cov3 struct {
resp *SearchResponse
err error
}
func (m *mockEngine_Cov3) Search(ctx context.Context, accountID uint, query string, filter *SearchFilter) (*SearchResponse, error) {
return m.resp, m.err
}
func (m *mockEngine_Cov3) IndexDocument(ctx context.Context, doc SearchDocument) error { return nil }
func (m *mockEngine_Cov3) IndexBatch(ctx context.Context, docs []SearchDocument) error { return nil }
func (m *mockEngine_Cov3) DeleteDocument(ctx context.Context, docType SearchResultType, accountID uint, id uint) error {
return nil
}
func (m *mockEngine_Cov3) Bootstrap(ctx context.Context) error { return nil }
func (m *mockEngine_Cov3) Close() error { return nil }