Files
upstream-mattermost/server/enterprise/elasticsearch/opensearch/aggregation_job_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

225 lines
8.0 KiB
Go

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.enterprise for license information.
package opensearch
import (
"bytes"
"context"
"encoding/json"
"os"
"testing"
"time"
"github.com/opensearch-project/opensearch-go/v4/opensearchapi"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/public/shared/request"
"github.com/mattermost/mattermost/server/v8/channels/api4"
"github.com/mattermost/mattermost/server/v8/channels/store/storetest/mocks"
"github.com/mattermost/mattermost/server/v8/enterprise/elasticsearch/common"
)
func TestElasticsearchAggregation(t *testing.T) {
if os.Getenv("IS_CI") == "true" {
os.Setenv("MM_ELASTICSEARCHSETTINGS_CONNECTIONURL", "http://opensearch:9201")
os.Setenv("MM_ELASTICSEARCHSETTINGS_BACKEND", "opensearch")
}
defer func() {
if os.Getenv("IS_CI") == "true" {
os.Setenv("MM_ELASTICSEARCHSETTINGS_CONNECTIONURL", "http://elasticsearch:9201")
os.Unsetenv("MM_ELASTICSEARCHSETTINGS_BACKEND")
}
}()
th := api4.SetupEnterpriseWithStoreMock(t)
rctx := request.TestContext(t)
mockUserStore := mocks.UserStore{}
mockUserStore.On("Count", mock.Anything).Return(int64(10), nil)
mockUserStore.On("GetAllProfiles", mock.Anything).Return(nil, nil)
mockPostStore := mocks.PostStore{}
mockPostStore.On("GetMaxPostSize").Return(model.PostMessageMaxBytesV2, nil)
mockSystemStore := mocks.SystemStore{}
mockSystemStore.On("GetByName", "UpgradedFromTE").Return(&model.System{Name: "UpgradedFromTE", Value: "false"}, nil)
mockSystemStore.On("GetByName", "InstallationDate").Return(&model.System{Name: "InstallationDate", Value: "10"}, nil)
mockSystemStore.On("GetByName", "FirstServerRunTimestamp").Return(&model.System{Name: "FirstServerRunTimestamp", Value: "10"}, nil)
mockJobStore := mocks.JobStore{}
mockJobStore.On("Save", mock.AnythingOfType("*model.Job")).Return(&model.Job{}, nil)
mockJobStore.On("UpdateStatus", mock.AnythingOfType("string"), model.JobStatusSuccess).Return(&model.Job{}, nil)
mockJobStore.On("Get", mock.AnythingOfType("*request.Context"), mock.AnythingOfType("string")).Return(&model.Job{
Status: model.JobStatusSuccess,
}, nil)
mockJobStore.On("UpdateStatusOptimistically",
mock.AnythingOfType("string"),
model.JobStatusPending,
model.JobStatusInProgress).
Return(&model.Job{}, nil)
mockJobStore.On("GetAllByType", mock.AnythingOfType("string")).Return([]*model.Job{{
Id: "abcxyz123",
Type: "EnterpriseElasticsearchIndexer",
Status: model.JobStatusCanceled,
}}, nil)
mockStore := th.App.Srv().Platform().Store.(*mocks.Store)
mockStore.On("User").Return(&mockUserStore)
mockStore.On("Post").Return(&mockPostStore)
mockStore.On("System").Return(&mockSystemStore)
mockStore.On("Job").Return(&mockJobStore)
mockStore.On("GetDBSchemaVersion").Return(1, nil)
aggImpl := OpensearchAggregatorInterfaceImpl{Server: th.Server}
// Register search engine
th.App.SearchEngine().RegisterElasticsearchEngine(&OpensearchInterfaceImpl{
Platform: th.Server.Platform(),
})
// Set up the state for the tests.
th.App.UpdateConfig(func(cfg *model.Config) {
if os.Getenv("IS_CI") == "true" {
*cfg.ElasticsearchSettings.ConnectionURL = "http://opensearch:9201"
} else {
*cfg.ElasticsearchSettings.ConnectionURL = "http://localhost:9201"
}
*cfg.ElasticsearchSettings.Backend = model.ElasticsearchSettingsOSBackend
*cfg.ElasticsearchSettings.EnableIndexing = true
*cfg.ElasticsearchSettings.EnableSearching = true
*cfg.ElasticsearchSettings.EnableAutocomplete = true
*cfg.ElasticsearchSettings.LiveIndexingBatchSize = 1
*cfg.ElasticsearchSettings.AggregatePostsAfterDays = 1
*cfg.SqlSettings.DisableDatabaseSearch = true
})
esImpl := th.App.SearchEngine().ElasticsearchEngine
appErr := esImpl.Start(context.Background())
if appErr != nil && appErr.Id != "ent.elasticsearch.start.already_started.app_error" {
require.Fail(t, "failed to start elasticsearch", appErr)
}
require.Nil(t, esImpl.PurgeIndexes(rctx))
post := &model.Post{
Id: model.NewId(),
ChannelId: "channel",
Message: "hi",
}
for i := range indexDeletionBatchSize + 1 {
indexPost(t, th, esImpl.(*OpensearchInterfaceImpl),
post,
time.Now().Add(-time.Duration(4+i)*24*time.Hour))
}
job := &model.Job{
Id: model.NewId(),
Type: model.JobTypeElasticsearchPostAggregation,
Status: model.JobStatusPending,
}
_, err := th.Server.Store().Job().Save(job)
require.NoError(t, err)
worker := aggImpl.MakeWorker().(*OpensearchAggregatorWorker)
worker.client = createTestClient(t, th.Context, th.App.Config(), th.App.FileBackend())
worker.jobServer.Store = mockStore
indexingImpl := OpensearchIndexerInterfaceImpl{
Server: th.App.Srv(),
}
th.Server.Jobs.RegisterJobType(model.JobTypeElasticsearchPostIndexing, indexingImpl.MakeWorker(), nil)
worker.DoJob(job)
// We assert the minimum number of calls to verify that
// batching is working correctly. Because job().Get() will happen
// in each iteration.
numCalls := 0
for _, call := range mockJobStore.Calls {
if call.Method == "Get" {
numCalls++
}
}
assert.GreaterOrEqual(t, numCalls, 8, "Unexpected number of Jobstore.Get calls")
}
func TestElasticsearchAggregationSkipDuringBulkIndexing(t *testing.T) {
th := api4.SetupEnterpriseWithStoreMock(t)
mockUserStore := mocks.UserStore{}
mockUserStore.On("Count", mock.Anything).Return(int64(10), nil)
mockPostStore := mocks.PostStore{}
mockPostStore.On("GetMaxPostSize").Return(model.PostMessageMaxBytesV2, nil)
mockSystemStore := mocks.SystemStore{}
mockSystemStore.On("GetByName", "UpgradedFromTE").Return(&model.System{Name: "UpgradedFromTE", Value: "false"}, nil)
mockSystemStore.On("GetByName", "InstallationDate").Return(&model.System{Name: "InstallationDate", Value: "10"}, nil)
mockSystemStore.On("GetByName", "FirstServerRunTimestamp").Return(&model.System{Name: "FirstServerRunTimestamp", Value: "10"}, nil)
mockJobStore := mocks.JobStore{}
mockStore := th.App.Srv().Platform().Store.(*mocks.Store)
mockStore.On("User").Return(&mockUserStore)
mockStore.On("Post").Return(&mockPostStore)
mockStore.On("System").Return(&mockSystemStore)
mockStore.On("Job").Return(&mockJobStore)
mockStore.On("GetDBSchemaVersion").Return(1, nil)
aggImpl := OpensearchAggregatorInterfaceImpl{Server: th.Server}
aggImpl.Server.Jobs.Store = mockStore
// Register search engine
th.App.SearchEngine().RegisterElasticsearchEngine(&OpensearchInterfaceImpl{
Platform: th.Server.Platform(),
})
// Set up the state for the tests.
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ElasticsearchSettings.EnableIndexing = true
*cfg.ElasticsearchSettings.EnableSearching = true
*cfg.ElasticsearchSettings.EnableAutocomplete = true
*cfg.ElasticsearchSettings.LiveIndexingBatchSize = 1
*cfg.ElasticsearchSettings.AggregatePostsAfterDays = 1
*cfg.SqlSettings.DisableDatabaseSearch = true
})
sched := aggImpl.MakeScheduler()
// Pass pending jobs as true
job, appErr := sched.ScheduleJob(th.Context, th.App.Config(), true, nil)
require.Nil(t, job)
require.Nil(t, appErr)
mockJobStore.AssertNotCalled(t, "GetCountByStatusAndType")
}
func indexPost(t *testing.T, th *api4.TestHelper, esImpl *OpensearchInterfaceImpl, post *model.Post, createTime time.Time) { //nolint:unused
t.Helper()
indexName := common.BuildPostIndexName(*th.Server.Config().ElasticsearchSettings.AggregatePostsAfterDays,
common.IndexBasePosts,
common.IndexBasePosts_MONTH,
createTime.Add(-1*24*time.Hour),
model.GetMillisForTime(createTime),
)
searchPost, err := common.ESPostFromPost(post, "teamID", "O", true)
require.NoError(t, err)
ctx, cancel := context.WithTimeout(context.Background(),
time.Duration(*esImpl.Platform.Config().ElasticsearchSettings.RequestTimeoutSeconds)*time.Second)
defer cancel()
buf, err := json.Marshal(searchPost)
require.NoError(t, err)
_, err = esImpl.client.Index(ctx, opensearchapi.IndexReq{
Index: indexName,
DocumentID: post.Id,
Body: bytes.NewReader(buf),
})
require.NoError(t, err)
}