142 lines
5.1 KiB
Go
142 lines
5.1 KiB
Go
package search
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/gochat/gochat/internal/model"
|
|
)
|
|
|
|
type roundTripFunc func(*http.Request) (*http.Response, error)
|
|
|
|
func (f roundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
|
|
return f(req)
|
|
}
|
|
|
|
func jsonResponse(status int, body string) *http.Response {
|
|
return &http.Response{
|
|
StatusCode: status,
|
|
Header: http.Header{"Content-Type": []string{"application/json"}},
|
|
Body: io.NopCloser(strings.NewReader(body)),
|
|
}
|
|
}
|
|
|
|
func TestNewSearchEngine_DefaultsToMeilisearch(t *testing.T) {
|
|
engine, err := NewSearchEngine(EngineConfig{}, nil)
|
|
require.NoError(t, err)
|
|
_, ok := engine.(*MeiliSearchEngine)
|
|
assert.True(t, ok)
|
|
}
|
|
|
|
func TestNewSearchEngine_DBFallbackRequiresRepo(t *testing.T) {
|
|
engine, err := NewSearchEngine(EngineConfig{Engine: EngineDB}, nil)
|
|
assert.Error(t, err)
|
|
assert.Nil(t, engine)
|
|
}
|
|
|
|
func TestDocumentBuildersSetStableUIDAndType(t *testing.T) {
|
|
conv := makeConversation(12, 3, "open", "billing,urgent")
|
|
conv.InboxID = 7
|
|
conv.ContactID = 9
|
|
doc := ConversationDocument(conv)
|
|
|
|
assert.Equal(t, "3:conversation:12", doc.UID)
|
|
assert.Equal(t, ResultTypeConversation, doc.Type)
|
|
assert.Equal(t, uint(3), doc.AccountID)
|
|
assert.Equal(t, []string{"billing", "urgent"}, doc.Labels)
|
|
}
|
|
|
|
func TestMeiliSearchEngine_SearchSendsScopedFilter(t *testing.T) {
|
|
var requestBody map[string]interface{}
|
|
transport := roundTripFunc(func(r *http.Request) (*http.Response, error) {
|
|
require.Equal(t, "/indexes/gochat_contacts/search", r.URL.Path)
|
|
require.NoError(t, json.NewDecoder(r.Body).Decode(&requestBody))
|
|
return jsonResponse(http.StatusOK, `{
|
|
"hits":[{"uid":"42:contact:9","id":9,"type":"contact","account_id":42,"snippet":"Ada Lovelace","_rankingScore":0.98}],
|
|
"estimatedTotalHits":1
|
|
}`), nil
|
|
})
|
|
|
|
engine := NewMeiliSearchEngine(EngineConfig{Host: "http://meili.test", IndexPrefix: "gochat_"})
|
|
engine.client.SetTransport(transport)
|
|
filter := &SearchFilter{Page: 1, PerPage: 10, Types: []SearchResultType{ResultTypeContact}}
|
|
resp, err := engine.Search(context.Background(), 42, "ada", filter)
|
|
|
|
require.NoError(t, err)
|
|
assert.Equal(t, int64(1), resp.TotalCount)
|
|
assert.Len(t, resp.Results, 1)
|
|
assert.Equal(t, uint(9), resp.Results[0].ID)
|
|
assert.Equal(t, "account_id = 42", requestBody["filter"])
|
|
assert.Equal(t, "ada", requestBody["q"])
|
|
}
|
|
|
|
func TestMeiliSearchEngine_SearchSendsMessageSenderIDFilter(t *testing.T) {
|
|
var requestBody map[string]interface{}
|
|
transport := roundTripFunc(func(r *http.Request) (*http.Response, error) {
|
|
require.Equal(t, "/indexes/gochat_messages/search", r.URL.Path)
|
|
require.NoError(t, json.NewDecoder(r.Body).Decode(&requestBody))
|
|
return jsonResponse(http.StatusOK, `{"hits":[],"estimatedTotalHits":0}`), nil
|
|
})
|
|
|
|
engine := NewMeiliSearchEngine(EngineConfig{Host: "http://meili.test", IndexPrefix: "gochat_"})
|
|
engine.client.SetTransport(transport)
|
|
senderID := uint(77)
|
|
filter := &SearchFilter{Page: 1, PerPage: 10, Types: []SearchResultType{ResultTypeMessage}, SenderType: "contact", SenderID: &senderID}
|
|
_, err := engine.Search(context.Background(), 42, "hello", filter)
|
|
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "account_id = 42 AND sender_type = \"contact\" AND sender_id = 77", requestBody["filter"])
|
|
}
|
|
|
|
func TestMeiliSearchEngine_IndexAndDeleteDocument(t *testing.T) {
|
|
seen := []string{}
|
|
transport := roundTripFunc(func(r *http.Request) (*http.Response, error) {
|
|
seen = append(seen, r.Method+" "+r.URL.Path)
|
|
return jsonResponse(http.StatusAccepted, `{"taskUid":1}`), nil
|
|
})
|
|
|
|
engine := NewMeiliSearchEngine(EngineConfig{Host: "http://meili.test", IndexPrefix: "gochat_"})
|
|
engine.client.SetTransport(transport)
|
|
doc := ContactDocument(model.Contact{Base: model.Base{ID: 5}, AccountID: 2, Name: "Grace"})
|
|
require.NoError(t, engine.IndexDocument(context.Background(), doc))
|
|
require.NoError(t, engine.DeleteDocument(context.Background(), ResultTypeContact, 2, 5))
|
|
|
|
assert.Equal(t, []string{
|
|
"POST /indexes/gochat_contacts/documents",
|
|
"DELETE /indexes/gochat_contacts/documents/2:contact:5",
|
|
}, seen)
|
|
}
|
|
|
|
func TestMeiliSearchEngine_BootstrapCreatesMissingIndexesAndSettings(t *testing.T) {
|
|
created := 0
|
|
settings := 0
|
|
transport := roundTripFunc(func(r *http.Request) (*http.Response, error) {
|
|
switch {
|
|
case r.Method == http.MethodGet && strings.HasPrefix(r.URL.Path, "/indexes/"):
|
|
return jsonResponse(http.StatusNotFound, `{"message":"not found"}`), nil
|
|
case r.Method == http.MethodPost && r.URL.Path == "/indexes":
|
|
created++
|
|
return jsonResponse(http.StatusAccepted, `{"taskUid":1}`), nil
|
|
case r.Method == http.MethodPatch && strings.HasSuffix(r.URL.Path, "/settings"):
|
|
settings++
|
|
return jsonResponse(http.StatusAccepted, `{"taskUid":2}`), nil
|
|
default:
|
|
t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path)
|
|
return nil, nil
|
|
}
|
|
})
|
|
|
|
engine := NewMeiliSearchEngine(EngineConfig{Host: "http://meili.test", IndexPrefix: "gochat_"})
|
|
engine.client.SetTransport(transport)
|
|
require.NoError(t, engine.Bootstrap(context.Background()))
|
|
assert.Equal(t, len(searchableTypes(nil)), created)
|
|
assert.Equal(t, len(searchableTypes(nil)), settings)
|
|
}
|