Files
upstream-mattermost/server/enterprise/elasticsearch/common/common_test.go
upstream-mirror 240db4e2a7
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 2s
Server CI Nightly Race / Build Environment (push) Failing after 1s
snapshot of https://github.com/mattermost/mattermost.git (master, 2026-09-16; blobs > 131072B omitted)
2026-09-16 00:29:55 +05:00

664 lines
20 KiB
Go

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.enterprise for license information.
package common
import (
"encoding/json"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/mattermost/mattermost/server/public/model"
)
func TestElasticsearchBuildPostIndexName(t *testing.T) {
now := time.Date(2017, 8, 14, 15, 16, 17, 123, time.Local)
sixDaysAgo := time.Date(2017, 8, 9, 12, 11, 10, 987, time.Local)
sevenDaysAgo := time.Date(2017, 8, 8, 11, 10, 9, 876, time.Local)
eightDaysAgo := time.Date(2017, 8, 7, 6, 5, 4, 321, time.Local)
sixMillis := sixDaysAgo.UnixNano() / int64(time.Millisecond)
sevenMillis := sevenDaysAgo.UnixNano() / int64(time.Millisecond)
eightMillis := eightDaysAgo.UnixNano() / int64(time.Millisecond)
aggregationCutoff := 7 // Aggregate monthly after 7 days.
sixName := BuildPostIndexName(aggregationCutoff, IndexBasePosts, IndexBasePosts_MONTH, now, sixMillis)
sevenName := BuildPostIndexName(aggregationCutoff, IndexBasePosts, IndexBasePosts_MONTH, now, sevenMillis)
eightName := BuildPostIndexName(aggregationCutoff, IndexBasePosts, IndexBasePosts_MONTH, now, eightMillis)
assert.Equal(t, sixName, "posts_2017_08_09")
assert.Equal(t, sevenName, "posts_2017_08_08")
assert.Equal(t, eightName, "postsmonth_2017_08")
}
func TestHasAnalysisPlugin(t *testing.T) {
installed := []string{"analysis-icu", "analysis-kuromoji", "opensearch-analysis-nori"}
testCases := []struct {
Name string
Plugins []string
Lookup string
Expected bool
}{
{"bundled name", installed, "analysis-kuromoji", true},
{"opensearch prefixed name", installed, "analysis-nori", true},
{"not installed", installed, "analysis-stempel", false},
{"no plugins reported", nil, "analysis-nori", false},
{"unrelated prefix is not accepted", []string{"vendor-analysis-nori"}, "analysis-nori", false},
{"elasticsearch prefix is not accepted", []string{"elasticsearch-analysis-nori"}, "analysis-nori", false},
{"a longer name is not a match", []string{"analysis-noris"}, "analysis-nori", false},
}
for _, tc := range testCases {
t.Run(tc.Name, func(t *testing.T) {
assert.Equal(t, tc.Expected, HasAnalysisPlugin(tc.Plugins, tc.Lookup))
})
}
}
func TestESPostFromPostForIndexing(t *testing.T) {
t.Run("any form with text only", func(t *testing.T) {
post := model.PostForIndexing{
TeamId: model.NewId(),
ParentCreateAt: nil,
Post: model.Post{
Id: model.NewId(),
ChannelId: model.NewId(),
UserId: model.NewId(),
CreateAt: model.GetMillis(),
Message: "message",
Type: "",
Hashtags: "",
Props: map[string]any{
model.PostPropsAttachments: []any{
map[string]any{
"text": "text 1",
},
},
},
},
}
espost := ESPostFromPostForIndexing(&post, true)
assert.Equal(t, post.Id, espost.Id)
assert.Equal(t, post.TeamId, espost.TeamId)
assert.Equal(t, post.ChannelId, espost.ChannelId)
assert.Equal(t, post.UserId, espost.UserId)
assert.Equal(t, post.CreateAt, espost.CreateAt)
assert.Equal(t, post.Message, espost.Message)
assert.Equal(t, "default", espost.Type)
assert.Empty(t, espost.Hashtags)
assert.Equal(t, "text 1", espost.Attachments)
})
t.Run("MessageAttachment form with text only", func(t *testing.T) {
post := model.PostForIndexing{
TeamId: model.NewId(),
ParentCreateAt: nil,
Post: model.Post{
Id: model.NewId(),
ChannelId: model.NewId(),
UserId: model.NewId(),
CreateAt: model.GetMillis(),
Message: "message",
Type: "slack_attachment",
Hashtags: "#buh #boh",
Props: map[string]any{
model.PostPropsAttachments: []*model.MessageAttachment{
{
Text: "text 2",
},
},
},
},
}
espost := ESPostFromPostForIndexing(&post, true)
assert.Equal(t, post.Id, espost.Id)
assert.Equal(t, post.TeamId, espost.TeamId)
assert.Equal(t, post.ChannelId, espost.ChannelId)
assert.Equal(t, post.UserId, espost.UserId)
assert.Equal(t, post.CreateAt, espost.CreateAt)
assert.Equal(t, post.Message, espost.Message)
assert.Equal(t, "slack_attachment", espost.Type)
assert.Len(t, espost.Hashtags, 2)
assert.Equal(t, "text 2", espost.Attachments)
})
t.Run("any form indexes title, pretext, and fields", func(t *testing.T) {
post := model.PostForIndexing{
TeamId: model.NewId(),
Post: model.Post{
Id: model.NewId(),
ChannelId: model.NewId(),
UserId: model.NewId(),
CreateAt: model.GetMillis(),
Message: "",
Type: "slack_attachment",
Props: map[string]any{
model.PostPropsAttachments: []any{
map[string]any{
"title": "Build Failed",
"pretext": "CI notification",
"fallback": "Build Failed on main",
"text": "Details here",
"fields": []any{
map[string]any{
"title": "Branch",
"value": "main",
},
map[string]any{
"title": "Status",
"value": "failed",
},
},
},
},
},
},
}
espost := ESPostFromPostForIndexing(&post, true)
assert.Contains(t, espost.Attachments, "Details here")
assert.Contains(t, espost.Attachments, "Build Failed")
assert.Contains(t, espost.Attachments, "CI notification")
assert.NotContains(t, espost.Attachments, "Build Failed on main")
assert.Contains(t, espost.Attachments, "Branch")
assert.Contains(t, espost.Attachments, "main")
assert.Contains(t, espost.Attachments, "Status")
assert.Contains(t, espost.Attachments, "failed")
})
t.Run("MessageAttachment form indexes title, pretext, and fields", func(t *testing.T) {
post := model.PostForIndexing{
TeamId: model.NewId(),
Post: model.Post{
Id: model.NewId(),
ChannelId: model.NewId(),
UserId: model.NewId(),
CreateAt: model.GetMillis(),
Message: "",
Type: "slack_attachment",
Props: map[string]any{
model.PostPropsAttachments: []*model.MessageAttachment{
{
Title: "Build Failed",
Pretext: "CI notification",
Fallback: "Build Failed on main",
Text: "Details here",
Fields: []*model.MessageAttachmentField{
{Title: "Branch", Value: "main"},
{Title: "Status", Value: "failed"},
},
},
},
},
},
}
espost := ESPostFromPostForIndexing(&post, true)
assert.Contains(t, espost.Attachments, "Details here")
assert.Contains(t, espost.Attachments, "Build Failed")
assert.Contains(t, espost.Attachments, "CI notification")
assert.NotContains(t, espost.Attachments, "Build Failed on main")
assert.Contains(t, espost.Attachments, "Branch")
assert.Contains(t, espost.Attachments, "main")
assert.Contains(t, espost.Attachments, "Status")
assert.Contains(t, espost.Attachments, "failed")
})
t.Run("empty fields are excluded", func(t *testing.T) {
post := model.PostForIndexing{
TeamId: model.NewId(),
Post: model.Post{
Id: model.NewId(),
ChannelId: model.NewId(),
UserId: model.NewId(),
CreateAt: model.GetMillis(),
Message: "",
Type: "slack_attachment",
Props: map[string]any{
model.PostPropsAttachments: []*model.MessageAttachment{
{
Title: "Only Title",
},
},
},
},
}
espost := ESPostFromPostForIndexing(&post, true)
assert.Equal(t, "Only Title", espost.Attachments)
})
t.Run("nil fields and attachments are handled", func(t *testing.T) {
post := model.PostForIndexing{
TeamId: model.NewId(),
Post: model.Post{
Id: model.NewId(),
ChannelId: model.NewId(),
UserId: model.NewId(),
CreateAt: model.GetMillis(),
Message: "",
Type: "slack_attachment",
Props: map[string]any{
model.PostPropsAttachments: []*model.MessageAttachment{
nil,
{
Text: "valid",
Fields: []*model.MessageAttachmentField{
nil,
{Title: "field title", Value: "field value"},
},
},
},
},
},
}
espost := ESPostFromPostForIndexing(&post, true)
assert.Contains(t, espost.Attachments, "valid")
assert.Contains(t, espost.Attachments, "field title")
assert.Contains(t, espost.Attachments, "field value")
})
t.Run("non-string field values are indexed", func(t *testing.T) {
post := model.PostForIndexing{
TeamId: model.NewId(),
Post: model.Post{
Id: model.NewId(),
ChannelId: model.NewId(),
UserId: model.NewId(),
CreateAt: model.GetMillis(),
Message: "",
Type: "slack_attachment",
Props: map[string]any{
model.PostPropsAttachments: []any{
map[string]any{
"text": "metrics",
"fields": []any{
map[string]any{
"title": "Count",
"value": 42,
},
map[string]any{
"title": "Rate",
"value": 99.5,
},
},
},
},
},
},
}
espost := ESPostFromPostForIndexing(&post, true)
assert.Contains(t, espost.Attachments, "metrics")
assert.Contains(t, espost.Attachments, "Count")
assert.Contains(t, espost.Attachments, "42")
assert.Contains(t, espost.Attachments, "Rate")
assert.Contains(t, espost.Attachments, "99.5")
})
t.Run("non-string field values in MessageAttachment form are indexed", func(t *testing.T) {
post := model.PostForIndexing{
TeamId: model.NewId(),
Post: model.Post{
Id: model.NewId(),
ChannelId: model.NewId(),
UserId: model.NewId(),
CreateAt: model.GetMillis(),
Message: "",
Type: "slack_attachment",
Props: map[string]any{
model.PostPropsAttachments: []*model.MessageAttachment{
{
Text: "metrics",
Fields: []*model.MessageAttachmentField{
{Title: "Count", Value: 42},
{Title: "Rate", Value: 99.5},
},
},
},
},
},
}
espost := ESPostFromPostForIndexing(&post, true)
assert.Contains(t, espost.Attachments, "metrics")
assert.Contains(t, espost.Attachments, "Count")
assert.Contains(t, espost.Attachments, "42")
assert.Contains(t, espost.Attachments, "Rate")
assert.Contains(t, espost.Attachments, "99.5")
})
t.Run("any form indexes footer and author_name", func(t *testing.T) {
post := model.PostForIndexing{
TeamId: model.NewId(),
Post: model.Post{
Id: model.NewId(),
ChannelId: model.NewId(),
UserId: model.NewId(),
CreateAt: model.GetMillis(),
Message: "",
Type: "slack_attachment",
Props: map[string]any{
model.PostPropsAttachments: []any{
map[string]any{
"text": "body text",
"footer": "Opportunity #OPP-000035341 • United States",
"author_name": "Salesforce",
},
},
},
},
}
espost := ESPostFromPostForIndexing(&post, true)
assert.Contains(t, espost.Attachments, "body text")
assert.Contains(t, espost.Attachments, "Opportunity #OPP-000035341 • United States")
assert.Contains(t, espost.Attachments, "Salesforce")
})
t.Run("MessageAttachment form indexes footer and author_name", func(t *testing.T) {
post := model.PostForIndexing{
TeamId: model.NewId(),
Post: model.Post{
Id: model.NewId(),
ChannelId: model.NewId(),
UserId: model.NewId(),
CreateAt: model.GetMillis(),
Message: "",
Type: "slack_attachment",
Props: map[string]any{
model.PostPropsAttachments: []*model.MessageAttachment{
{
Text: "body text",
Footer: "Opportunity #OPP-000035341 • United States",
AuthorName: "Salesforce",
},
},
},
},
}
espost := ESPostFromPostForIndexing(&post, true)
assert.Contains(t, espost.Attachments, "body text")
assert.Contains(t, espost.Attachments, "Opportunity #OPP-000035341 • United States")
assert.Contains(t, espost.Attachments, "Salesforce")
})
t.Run("interactive mm_blocks blocks and cards are indexed in attachments field", func(t *testing.T) {
post := model.PostForIndexing{
TeamId: model.NewId(),
Post: model.Post{
Id: model.NewId(),
ChannelId: model.NewId(),
UserId: model.NewId(),
CreateAt: model.GetMillis(),
Message: "root",
Type: model.PostTypeDefault,
Props: map[string]any{
model.PostPropsMmBlocks: []any{
map[string]any{"type": "text", "text": "mm-line"},
},
model.PostPropsBlockKitBlocks: []any{
map[string]any{
"type": "section",
"text": map[string]any{
"type": "mrkdwn",
"text": "block kit-line",
},
},
},
model.PostPropsAdaptiveCards: []any{
map[string]any{
"type": "AdaptiveCard",
"body": []any{
map[string]any{"type": "TextBlock", "text": "card-line"},
},
},
},
},
},
}
espost := ESPostFromPostForIndexing(&post, true)
assert.Equal(t, "root", espost.Message)
assert.Contains(t, espost.Attachments, "mm-line")
assert.Contains(t, espost.Attachments, "block kit-line")
assert.Contains(t, espost.Attachments, "card-line")
})
t.Run("interactive content omitted from attachments when mmBlocksEnabled is false", func(t *testing.T) {
post := model.PostForIndexing{
TeamId: model.NewId(),
Post: model.Post{
Id: model.NewId(),
ChannelId: model.NewId(),
UserId: model.NewId(),
CreateAt: model.GetMillis(),
Message: "root",
Type: model.PostTypeDefault,
Props: map[string]any{
model.PostPropsMmBlocks: []any{
map[string]any{"type": "text", "text": "mm-line"},
},
model.PostPropsBlockKitBlocks: []any{
map[string]any{
"type": "section",
"text": map[string]any{
"type": "mrkdwn",
"text": "block kit-line",
},
},
},
model.PostPropsAdaptiveCards: []any{
map[string]any{
"type": "AdaptiveCard",
"body": []any{
map[string]any{"type": "TextBlock", "text": "card-line"},
},
},
},
},
},
}
espost := ESPostFromPostForIndexing(&post, false)
assert.Equal(t, "root", espost.Message)
assert.NotContains(t, espost.Attachments, "mm-line")
assert.NotContains(t, espost.Attachments, "block kit-line")
assert.NotContains(t, espost.Attachments, "card-line")
})
t.Run("multiple attachments are combined", func(t *testing.T) {
post := model.PostForIndexing{
TeamId: model.NewId(),
Post: model.Post{
Id: model.NewId(),
ChannelId: model.NewId(),
UserId: model.NewId(),
CreateAt: model.GetMillis(),
Message: "",
Type: "slack_attachment",
Props: map[string]any{
model.PostPropsAttachments: []any{
map[string]any{
"title": "First",
"text": "one",
},
map[string]any{
"title": "Second",
"text": "two",
},
},
},
},
}
espost := ESPostFromPostForIndexing(&post, true)
assert.Contains(t, espost.Attachments, "First")
assert.Contains(t, espost.Attachments, "one")
assert.Contains(t, espost.Attachments, "Second")
assert.Contains(t, espost.Attachments, "two")
})
}
// TestESPostFromPost_CreatePostJSONRoundTrip simulates the exact flow that happens
// in production: App.CreatePost converts []*MessageAttachment to []any via JSON
// marshal/unmarshal (Post.Attachments in post.go), then the search layer calls
// ESPostFromPost which does ShallowCopy → ESPostFromPostForIndexing.
func TestESPostFromPost_CreatePostJSONRoundTrip(t *testing.T) {
// Step 1: Start with typed MessageAttachment (as a plugin/webhook would create)
original := &model.Post{
Id: model.NewId(),
ChannelId: model.NewId(),
UserId: model.NewId(),
CreateAt: model.GetMillis(),
Message: "#closedwon #renewal",
Type: "slack_attachment",
}
original.AddProp(model.PostPropsAttachments, []*model.MessageAttachment{
{
Title: "Account: Acme Corp / Widget Industries, LLC",
Text: "Renewal ARR: $49,140.00",
Pretext: "#closedwon #renewal",
Footer: "Opportunity #OPP-000035341 • United States",
AuthorName: "CRM Bot",
Fields: []*model.MessageAttachmentField{
{Title: "Sales Rep", Value: "Jane Smith"},
{Title: "Account Manager", Value: "John Doe"},
{Title: "Opportunity", Value: "Acme Corp / Widget Industries, LLC - Enterprise - 350 Seats - '26 Renewal"},
{Title: "Seats", Value: 350.0},
},
},
})
// Step 2: Simulate CreatePost JSON round-trip (post.go:305-315)
if attachments, ok := original.GetProp(model.PostPropsAttachments).([]*model.MessageAttachment); ok {
jsonAttachments, err := json.Marshal(attachments)
require.NoError(t, err)
attachmentsInterface := []any{}
err = json.Unmarshal(jsonAttachments, &attachmentsInterface)
require.NoError(t, err)
original.AddProp(model.PostPropsAttachments, attachmentsInterface)
} else {
require.Fail(t, "expected []*MessageAttachment type assertion to succeed")
}
// Step 3: Simulate ESPostFromPost (what the search layer calls)
teamId := model.NewId()
esPost, err := ESPostFromPost(original, teamId, "O", true)
require.NoError(t, err)
// Step 4: Verify all attachment fields are indexed
assert.Contains(t, esPost.Attachments, "Account: Acme Corp / Widget Industries, LLC", "title should be indexed")
assert.Contains(t, esPost.Attachments, "Renewal ARR: $49,140.00", "text should be indexed")
assert.Contains(t, esPost.Attachments, "#closedwon #renewal", "pretext should be indexed")
// Field titles and values
assert.Contains(t, esPost.Attachments, "Sales Rep", "field title should be indexed")
assert.Contains(t, esPost.Attachments, "Jane Smith", "field value should be indexed")
assert.Contains(t, esPost.Attachments, "Account Manager", "field title should be indexed")
assert.Contains(t, esPost.Attachments, "John Doe", "field value should be indexed")
assert.Contains(t, esPost.Attachments, "Opportunity", "field title should be indexed")
assert.Contains(t, esPost.Attachments, "Acme Corp / Widget Industries, LLC - Enterprise - 350 Seats - '26 Renewal", "field value should be indexed")
assert.Contains(t, esPost.Attachments, "350", "numeric field value should be indexed")
// Footer and AuthorName
assert.Contains(t, esPost.Attachments, "Opportunity #OPP-000035341 • United States", "footer should be indexed")
assert.Contains(t, esPost.Attachments, "CRM Bot", "author_name should be indexed")
t.Logf("Attachments field content: %s", esPost.Attachments)
}
// TestESPostFromPost_APIJSONUnmarshal simulates a post created via the REST API
// where Props are deserialized from JSON (attachments arrive as []any directly).
func TestESPostFromPost_APIJSONUnmarshal(t *testing.T) {
// Simulate what happens when a bot POSTs JSON to /api/v4/posts
postJSON := `{
"channel_id": "` + model.NewId() + `",
"message": "",
"props": {
"attachments": [{
"title": "Account: Acme Corp",
"text": "Renewal ARR: $49,140.00",
"footer": "Opportunity #OPP-000035341",
"author_name": "CRM Bot",
"fields": [
{"title": "Sales Rep", "value": "Jane Smith"},
{"title": "Seats", "value": 350}
]
}]
}
}`
var post model.Post
err := json.Unmarshal([]byte(postJSON), &post)
require.NoError(t, err)
post.Id = model.NewId()
post.UserId = model.NewId()
post.CreateAt = model.GetMillis()
// The CreatePost conversion at post.go:305-315 would NOT trigger here
// because the type is already []any, not []*MessageAttachment
_, typeOk := post.GetProp(model.PostPropsAttachments).([]*model.MessageAttachment)
assert.False(t, typeOk, "API-created post should have []any, not []*MessageAttachment")
esPost, err := ESPostFromPost(&post, model.NewId(), "O", true)
require.NoError(t, err)
assert.Contains(t, esPost.Attachments, "Account: Acme Corp", "title should be indexed")
assert.Contains(t, esPost.Attachments, "Renewal ARR: $49,140.00", "text should be indexed")
assert.Contains(t, esPost.Attachments, "Sales Rep", "field title should be indexed")
assert.Contains(t, esPost.Attachments, "Jane Smith", "field value should be indexed")
assert.Contains(t, esPost.Attachments, "350", "numeric field value should be indexed")
assert.Contains(t, esPost.Attachments, "Opportunity #OPP-000035341", "footer should be indexed")
assert.Contains(t, esPost.Attachments, "CRM Bot", "author_name should be indexed")
t.Logf("Attachments field content: %s", esPost.Attachments)
}
func TestGetMatchesForHit(t *testing.T) {
snippets := map[string][]string{
"message": {
"<em>Apples</em> and oranges and <em>apple</em> and orange",
"Johnny <em>Appleseed</em>",
"That doesn't <em>apply</em> to me, and it doesn't <em>apply</em> to you.",
},
"hashtags": {
"This is an <em>#hashtag</em>",
},
}
expected := []string{
"Apples",
"apple",
"Appleseed",
"apply",
"#hashtag",
}
actual, err := GetMatchesForHit(snippets)
require.NoError(t, err)
require.ElementsMatch(t, expected, actual)
}