Restructure the monorepo into clear top-level directories: - backend/: Go module root (cmd, internal, pkg, configs, migrations, docs/swagger, scripts, tests, go.mod, Makefile, .air.toml) - deploy/: Docker (Dockerfile, docker-compose*), quickstart, fluentd - docs/: project documentation + reports/ (moved from repo root) - AGENTS.md: new AI coding-agent guide at repo root Update all references to the new layout: - Dockerfile: COPY backend/go.mod, COPY backend/ (context = repo root) - docker-compose files: context ../.., dockerfile deploy/docker/Dockerfile, env_file ../../.env, volume mounts ../../backend:/app - deploy/quickstart/compose.yaml: dockerfile deploy/docker/Dockerfile - CI: working-directory: backend for go commands, file deploy/docker/Dockerfile, coverage path backend/coverage.out, health_check backend/scripts/ - backend/Makefile: docker target uses -f ../deploy/docker/Dockerfile ../ - README: architecture tree, quickstart, config paths updated Move root stray scripts (rename_models.*, run_m11_tests.sh, verify_build.sh, gorm_bool_main.go) to backend/scripts/legacy/. All moves via git mv to preserve history. Build, vet, SQLite tests, and docker compose config verified.
405 lines
18 KiB
Go
405 lines
18 KiB
Go
package search
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"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) {
|
|
lastActivity := int64(1772884800)
|
|
conv := makeConversation(12, 3, "open", "billing,urgent")
|
|
conv.InboxID = 7
|
|
conv.ContactID = 9
|
|
conv.LastActivityAt = &lastActivity
|
|
displayID := uint(42)
|
|
conv.DisplayID = &displayID
|
|
conv.Contact = &model.Contact{Name: "Ada Lovelace", Email: "ada@example.com", PhoneNumber: "+123", Identifier: "ada-id"}
|
|
conv.Inbox = &model.Inbox{Base: model.Base{ID: 7}, Name: "Website", ChannelID: 11, ChannelType: "web_widget"}
|
|
conv.Assignee = &model.User{Base: model.Base{ID: 8}, Name: "Agent One", Email: "agent@example.com", Role: "administrator"}
|
|
conv.Messages = []model.Message{{Base: model.Base{ID: 15, CreatedAt: time.Unix(1772884700, 0)}, AccountID: 3, InboxID: 7, ConversationID: 12, Content: "hello", MessageType: "incoming"}}
|
|
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)
|
|
assert.Equal(t, lastActivity, doc.LastActivityAtTS)
|
|
assert.Equal(t, "Conversation #42", doc.Title)
|
|
assert.Equal(t, "42 Ada Lovelace ada@example.com +123 ada-id", doc.Content)
|
|
assert.Equal(t, "Ada Lovelace", doc.Data["contact"].(map[string]interface{})["name"])
|
|
assert.Equal(t, "Website", doc.Data["inbox"].(map[string]interface{})["name"])
|
|
assert.Equal(t, "Agent One", doc.Data["agent"].(map[string]interface{})["available_name"])
|
|
assert.Equal(t, uint(15), doc.Data["message"].(map[string]interface{})["id"])
|
|
}
|
|
|
|
func TestMessageDocumentCarriesChatwootPayloadData(t *testing.T) {
|
|
displayID := uint(42)
|
|
msg := model.Message{
|
|
Base: model.Base{ID: 15, CreatedAt: time.Unix(1772884700, 0)},
|
|
AccountID: 3,
|
|
InboxID: 7,
|
|
ConversationID: 12,
|
|
Content: "hello with file",
|
|
MessageType: "incoming",
|
|
ContentType: "text",
|
|
Status: "sent",
|
|
Conversation: &model.Conversation{Base: model.Base{ID: 12}, DisplayID: &displayID},
|
|
Attachments: []model.Attachment{{
|
|
Base: model.Base{ID: 99},
|
|
AccountID: 3,
|
|
MessageID: 15,
|
|
FileType: "audio",
|
|
FileName: "note.mp3",
|
|
FileURL: "https://files.example/note.mp3",
|
|
Metadata: `{"transcribed_text":"hello transcript"}`,
|
|
}},
|
|
}
|
|
|
|
doc := MessageDocument(msg)
|
|
|
|
message := doc.Data["message"].(map[string]interface{})
|
|
assert.Equal(t, displayID, message["conversation_id"])
|
|
attachments := message["attachments"].([]map[string]interface{})
|
|
require.Len(t, attachments, 1)
|
|
assert.Equal(t, "audio", attachments[0]["file_type"])
|
|
assert.Equal(t, "hello transcript", attachments[0]["transcribed_text"])
|
|
}
|
|
|
|
func TestArticleDocumentCarriesSearchPartialData(t *testing.T) {
|
|
categoryID := uint(9)
|
|
article := model.Article{
|
|
Base: model.Base{ID: 21, UpdatedAt: time.Unix(1772884800, 0)},
|
|
AccountID: 3,
|
|
PortalID: 5,
|
|
CategoryID: &categoryID,
|
|
Title: "Billing FAQ",
|
|
Slug: "billing-faq",
|
|
Locale: "en",
|
|
Content: "billing content",
|
|
Status: "published",
|
|
Portal: model.Portal{Base: model.Base{ID: 5}, Slug: "help-center"},
|
|
Category: &model.Category{Base: model.Base{ID: categoryID}, Name: "Billing"},
|
|
}
|
|
|
|
doc := ArticleDocument(article)
|
|
|
|
payload := doc.Data["article"].(map[string]interface{})
|
|
assert.Equal(t, "help-center", payload["portal_slug"])
|
|
assert.Equal(t, "Billing", payload["category_name"])
|
|
assert.Equal(t, int64(1772884800), payload["updated_at"])
|
|
}
|
|
|
|
func TestContactDocumentSetsResolvedScopeFields(t *testing.T) {
|
|
doc := ContactDocument(model.Contact{
|
|
Base: model.Base{ID: 5},
|
|
AccountID: 2,
|
|
Name: "Grace Hopper",
|
|
Email: "grace@example.com",
|
|
ContactType: "lead",
|
|
})
|
|
|
|
assert.Equal(t, ResultTypeContact, doc.Type)
|
|
assert.Equal(t, "lead", doc.ContactType)
|
|
assert.Equal(t, "lead", doc.ContactSource)
|
|
assert.True(t, doc.ContactHasDetails)
|
|
|
|
labelled := ContactDocument(model.Contact{Base: model.Base{ID: 7}, AccountID: 2, Name: "Labelled", Labels: []string{"vip", "trial"}})
|
|
assert.Equal(t, []string{"vip", "trial"}, labelled.Labels)
|
|
|
|
anonymous := ContactDocument(model.Contact{Base: model.Base{ID: 6}, AccountID: 2, Name: "Anonymous"})
|
|
assert.False(t, anonymous.ContactHasDetails)
|
|
}
|
|
|
|
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_SearchSendsResolvedContactFilter(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":[],"estimatedTotalHits":0}`), nil
|
|
})
|
|
|
|
engine := NewMeiliSearchEngine(EngineConfig{Host: "http://meili.test", IndexPrefix: "gochat_"})
|
|
engine.client.SetTransport(transport)
|
|
filter := &SearchFilter{Page: 1, PerPage: 10, Types: []SearchResultType{ResultTypeContact}, ContactResolvedScope: true}
|
|
_, err := engine.Search(context.Background(), 42, "ada", filter)
|
|
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "account_id = 42 AND contact_has_details = true", requestBody["filter"])
|
|
}
|
|
|
|
func TestMeiliSearchEngine_SearchSendsCRMV2ResolvedContactFilter(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":[],"estimatedTotalHits":0}`), nil
|
|
})
|
|
|
|
engine := NewMeiliSearchEngine(EngineConfig{Host: "http://meili.test", IndexPrefix: "gochat_"})
|
|
engine.client.SetTransport(transport)
|
|
filter := &SearchFilter{Page: 1, PerPage: 10, Types: []SearchResultType{ResultTypeContact}, ContactResolvedScope: true, ContactCRMV2: true}
|
|
_, err := engine.Search(context.Background(), 42, "ada", filter)
|
|
|
|
require.NoError(t, err)
|
|
assert.Equal(t, `account_id = 42 AND contact_type = "lead"`, requestBody["filter"])
|
|
}
|
|
|
|
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}, AdvancedSearchEnabled: true, 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\" OR sender_type = \"Contact\") AND sender_id = 77", requestBody["filter"])
|
|
}
|
|
|
|
func TestMeiliSearchEngine_SearchSendsAgentSenderAliases(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}, AdvancedSearchEnabled: true, SenderType: "agent", SenderID: &senderID}
|
|
_, err := engine.Search(context.Background(), 42, "hello", filter)
|
|
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "account_id = 42 AND (sender_type = \"agent\" OR sender_type = \"user\" OR sender_type = \"User\") AND sender_id = 77", requestBody["filter"])
|
|
}
|
|
|
|
func TestMeiliSearchEngine_SearchSkipsAdvancedFiltersWhenFeatureDisabled(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)
|
|
inboxID := uint(9)
|
|
dateFrom := time.Date(2026, 3, 7, 12, 0, 0, 0, time.UTC)
|
|
filter := &SearchFilter{Page: 1, PerPage: 10, Types: []SearchResultType{ResultTypeMessage}, SenderType: "contact", SenderID: &senderID, InboxID: &inboxID, DateFrom: &dateFrom}
|
|
_, err := engine.Search(context.Background(), 42, "hello", filter)
|
|
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "account_id = 42", requestBody["filter"])
|
|
}
|
|
|
|
func TestMeiliSearchEngine_SearchSendsInboxAccessFilter(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)
|
|
filter := &SearchFilter{Page: 1, PerPage: 10, Types: []SearchResultType{ResultTypeMessage}, EnforceInboxAccess: true, AccessibleInboxIDs: []uint{3, 5}}
|
|
_, err := engine.Search(context.Background(), 42, "hello", filter)
|
|
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "account_id = 42 AND (inbox_id = 3 OR inbox_id = 5)", requestBody["filter"])
|
|
}
|
|
|
|
func TestMeiliSearchEngine_SearchSendsMessageRecentBaseFilter(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)
|
|
cutoff := time.Date(2026, 3, 7, 12, 0, 0, 0, time.UTC)
|
|
filter := &SearchFilter{Page: 1, PerPage: 10, Types: []SearchResultType{ResultTypeMessage}, MessageCreatedAfter: &cutoff}
|
|
_, err := engine.Search(context.Background(), 42, "hello", filter)
|
|
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "account_id = 42 AND created_at_ts >= 1772884800", requestBody["filter"])
|
|
}
|
|
|
|
func TestMeiliSearchEngine_SearchUsesReferenceTimeFilterFields(t *testing.T) {
|
|
seen := map[string]string{}
|
|
transport := roundTripFunc(func(r *http.Request) (*http.Response, error) {
|
|
var requestBody map[string]interface{}
|
|
require.NoError(t, json.NewDecoder(r.Body).Decode(&requestBody))
|
|
seen[r.URL.Path] = requestBody["filter"].(string)
|
|
return jsonResponse(http.StatusOK, `{"hits":[],"estimatedTotalHits":0}`), nil
|
|
})
|
|
|
|
engine := NewMeiliSearchEngine(EngineConfig{Host: "http://meili.test", IndexPrefix: "gochat_"})
|
|
engine.client.SetTransport(transport)
|
|
from := time.Date(2026, 3, 7, 12, 0, 0, 0, time.UTC)
|
|
filter := &SearchFilter{Page: 1, PerPage: 10, Types: []SearchResultType{ResultTypeConversation, ResultTypeContact, ResultTypeArticle, ResultTypeMessage}, AdvancedSearchEnabled: true, DateFrom: &from}
|
|
_, err := engine.Search(context.Background(), 42, "hello", filter)
|
|
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "account_id = 42 AND last_activity_at_ts >= 1772884800", seen["/indexes/gochat_conversations/search"])
|
|
assert.Equal(t, "account_id = 42 AND last_activity_at_ts >= 1772884800", seen["/indexes/gochat_contacts/search"])
|
|
assert.Equal(t, "account_id = 42 AND updated_at_ts >= 1772884800", seen["/indexes/gochat_articles/search"])
|
|
assert.Equal(t, "account_id = 42 AND created_at_ts >= 1772884800", seen["/indexes/gochat_messages/search"])
|
|
}
|
|
|
|
func TestMeiliSearchEngine_SearchIgnoresInaccessibleInboxParam(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)
|
|
inboxID := uint(9)
|
|
filter := &SearchFilter{Page: 1, PerPage: 10, Types: []SearchResultType{ResultTypeMessage}, AdvancedSearchEnabled: true, InboxID: &inboxID, EnforceInboxAccess: true, AccessibleInboxIDs: []uint{3, 5}}
|
|
_, err := engine.Search(context.Background(), 42, "hello", filter)
|
|
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "account_id = 42 AND (inbox_id = 3 OR inbox_id = 5)", requestBody["filter"])
|
|
}
|
|
|
|
func TestMeiliSearchEngine_SearchAppliesExplicitInboxOnlyToMessages(t *testing.T) {
|
|
seen := map[string]string{}
|
|
transport := roundTripFunc(func(r *http.Request) (*http.Response, error) {
|
|
var requestBody map[string]interface{}
|
|
require.NoError(t, json.NewDecoder(r.Body).Decode(&requestBody))
|
|
seen[r.URL.Path] = requestBody["filter"].(string)
|
|
return jsonResponse(http.StatusOK, `{"hits":[],"estimatedTotalHits":0}`), nil
|
|
})
|
|
|
|
engine := NewMeiliSearchEngine(EngineConfig{Host: "http://meili.test", IndexPrefix: "gochat_"})
|
|
engine.client.SetTransport(transport)
|
|
inboxID := uint(9)
|
|
filter := &SearchFilter{Page: 1, PerPage: 10, Types: []SearchResultType{ResultTypeConversation, ResultTypeContact, ResultTypeArticle, ResultTypeMessage}, AdvancedSearchEnabled: true, InboxID: &inboxID}
|
|
_, err := engine.Search(context.Background(), 42, "hello", filter)
|
|
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "account_id = 42", seen["/indexes/gochat_conversations/search"])
|
|
assert.Equal(t, "account_id = 42", seen["/indexes/gochat_contacts/search"])
|
|
assert.Equal(t, "account_id = 42", seen["/indexes/gochat_articles/search"])
|
|
assert.Equal(t, "account_id = 42 AND inbox_id = 9", seen["/indexes/gochat_messages/search"])
|
|
}
|
|
|
|
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)
|
|
}
|