github.com/haalcala/mattermost-server-change-repo@v0.0.0-20210713015153-16753fbeee5f/store/searchtest/file_info_layer.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See LICENSE.txt for license information.
     3  
     4  package searchtest
     5  
     6  import (
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/stretchr/testify/require"
    11  
    12  	"github.com/mattermost/mattermost-server/v5/model"
    13  	"github.com/mattermost/mattermost-server/v5/store"
    14  )
    15  
    16  var searchFileInfoStoreTests = []searchTest{
    17  	{
    18  		Name: "Should be able to search posts including results from DMs",
    19  		Fn:   testFileInfoSearchFileInfosIncludingDMs,
    20  		Tags: []string{EngineAll},
    21  	},
    22  	{
    23  		Name: "Should be able to search posts using pagination",
    24  		Fn:   testFileInfoSearchFileInfosWithPagination,
    25  		Tags: []string{EngineElasticSearch, EngineBleve},
    26  	},
    27  	{
    28  		Name: "Should be able to search for exact phrases in quotes",
    29  		Fn:   testFileInfoSearchExactPhraseInQuotes,
    30  		Tags: []string{EnginePostgres, EngineMySql, EngineElasticSearch},
    31  	},
    32  	{
    33  		Name: "Should be able to search for email addresses with or without quotes",
    34  		Fn:   testFileInfoSearchEmailAddresses,
    35  		Tags: []string{EngineElasticSearch},
    36  	},
    37  	{
    38  		Name: "Should be able to search when markdown underscores are applied",
    39  		Fn:   testFileInfoSearchMarkdownUnderscores,
    40  		Tags: []string{EnginePostgres, EngineElasticSearch},
    41  	},
    42  	{
    43  		Name: "Should be able to search for non-latin words",
    44  		Fn:   testFileInfoSearchNonLatinWords,
    45  		Tags: []string{EngineElasticSearch},
    46  	},
    47  	{
    48  		Name: "Should be able to search for alternative spellings of words",
    49  		Fn:   testFileInfoSearchAlternativeSpellings,
    50  		Tags: []string{EngineElasticSearch},
    51  	},
    52  	{
    53  		Name: "Should be able to search for alternative spellings of words with and without accents",
    54  		Fn:   testFileInfoSearchAlternativeSpellingsAccents,
    55  		Tags: []string{EngineElasticSearch},
    56  	},
    57  	{
    58  		Name: "Should be able to search or exclude messages written by a specific user",
    59  		Fn:   testFileInfoSearchOrExcludeFileInfosBySpecificUser,
    60  		Tags: []string{EngineAll},
    61  	},
    62  	{
    63  		Name: "Should be able to search or exclude messages written in a specific channel",
    64  		Fn:   testFileInfoSearchOrExcludeFileInfosInChannel,
    65  		Tags: []string{EngineAll},
    66  	},
    67  	{
    68  		Name: "Should be able to search or exclude messages written in a DM or GM",
    69  		Fn:   testFileInfoSearchOrExcludeFileInfosInDMGM,
    70  		Tags: []string{EngineAll},
    71  	},
    72  	{
    73  		Name: "Should be able to search or exclude files by extensions",
    74  		Fn:   testFileInfoSearchOrExcludeByExtensions,
    75  		Tags: []string{EngineAll},
    76  	},
    77  	{
    78  		Name: "Should be able to filter messages written after a specific date",
    79  		Fn:   testFileInfoFilterFilesAfterSpecificDate,
    80  		Tags: []string{EngineAll},
    81  	},
    82  	{
    83  		Name: "Should be able to filter messages written before a specific date",
    84  		Fn:   testFileInfoFilterFilesBeforeSpecificDate,
    85  		Tags: []string{EngineAll},
    86  	},
    87  	{
    88  		Name: "Should be able to filter messages written on a specific date",
    89  		Fn:   testFileInfoFilterFilesInSpecificDate,
    90  		Tags: []string{EngineAll},
    91  	},
    92  	{
    93  		Name: "Should be able to exclude messages that contain a serch term",
    94  		Fn:   testFileInfoFilterFilesWithATerm,
    95  		Tags: []string{EngineMySql, EnginePostgres},
    96  	},
    97  	{
    98  		Name: "Should be able to search using boolean operators",
    99  		Fn:   testFileInfoSearchUsingBooleanOperators,
   100  		Tags: []string{EngineMySql, EnginePostgres, EngineElasticSearch},
   101  	},
   102  	{
   103  		Name: "Should be able to search with combined filters",
   104  		Fn:   testFileInfoSearchUsingCombinedFilters,
   105  		Tags: []string{EngineAll},
   106  	},
   107  	{
   108  		Name: "Should be able to ignore stop words",
   109  		Fn:   testFileInfoSearchIgnoringStopWords,
   110  		Tags: []string{EngineMySql, EngineElasticSearch},
   111  	},
   112  	{
   113  		Name: "Should support search stemming",
   114  		Fn:   testFileInfoSupportStemming,
   115  		Tags: []string{EnginePostgres, EngineElasticSearch},
   116  	},
   117  	{
   118  		Name: "Should support search with wildcards",
   119  		Fn:   testFileInfoSupportWildcards,
   120  		Tags: []string{EngineAll},
   121  	},
   122  	{
   123  		Name: "Should not support search with preceding wildcards",
   124  		Fn:   testFileInfoNotSupportPrecedingWildcards,
   125  		Tags: []string{EngineAll},
   126  	},
   127  	{
   128  		Name: "Should discard a wildcard if it's not placed immediately by text",
   129  		Fn:   testFileInfoSearchDiscardWildcardAlone,
   130  		Tags: []string{EngineAll},
   131  	},
   132  	{
   133  		Name: "Should support terms with dash",
   134  		Fn:   testFileInfoSupportTermsWithDash,
   135  		Tags: []string{EngineAll},
   136  		Skip: true,
   137  	},
   138  	{
   139  		Name: "Should support terms with underscore",
   140  		Fn:   testFileInfoSupportTermsWithUnderscore,
   141  		Tags: []string{EngineMySql, EngineElasticSearch},
   142  	},
   143  	{
   144  		Name: "Should be able to search in deleted/archived channels",
   145  		Fn:   testFileInfoSearchInDeletedOrArchivedChannels,
   146  		Tags: []string{EngineMySql, EnginePostgres},
   147  	},
   148  	{
   149  		Name:        "Should be able to search terms with dashes",
   150  		Fn:          testFileInfoSearchTermsWithDashes,
   151  		Tags:        []string{EngineAll},
   152  		Skip:        true,
   153  		SkipMessage: "Not working",
   154  	},
   155  	{
   156  		Name: "Should be able to search terms with dots",
   157  		Fn:   testFileInfoSearchTermsWithDots,
   158  		Tags: []string{EnginePostgres, EngineElasticSearch},
   159  	},
   160  	{
   161  		Name: "Should be able to search terms with underscores",
   162  		Fn:   testFileInfoSearchTermsWithUnderscores,
   163  		Tags: []string{EngineMySql, EngineElasticSearch},
   164  	},
   165  	{
   166  		Name: "Should be able to combine stemming and wildcards",
   167  		Fn:   testFileInfoSupportStemmingAndWildcards,
   168  		Tags: []string{EngineElasticSearch},
   169  	},
   170  	{
   171  		Name: "Should support wildcard outside quotes",
   172  		Fn:   testFileInfoSupportWildcardOutsideQuotes,
   173  		Tags: []string{EngineElasticSearch},
   174  	},
   175  	{
   176  		Name: "Should not support slash as character separator",
   177  		Fn:   testFileInfoSlashShouldNotBeCharSeparator,
   178  		Tags: []string{EngineMySql, EngineElasticSearch},
   179  	},
   180  	{
   181  		Name: "Should be able to search emails without quoting them",
   182  		Fn:   testFileInfoSearchEmailsWithoutQuotes,
   183  		Tags: []string{EngineElasticSearch},
   184  	},
   185  }
   186  
   187  func TestSearchFileInfoStore(t *testing.T, s store.Store, testEngine *SearchTestEngine) {
   188  	th := &SearchTestHelper{
   189  		Store: s,
   190  	}
   191  	err := th.SetupBasicFixtures()
   192  	require.NoError(t, err)
   193  	defer th.CleanFixtures()
   194  
   195  	runTestSearch(t, testEngine, searchFileInfoStoreTests, th)
   196  }
   197  
   198  func testFileInfoSearchFileInfosIncludingDMs(t *testing.T, th *SearchTestHelper) {
   199  	direct, err := th.createDirectChannel(th.Team.Id, "direct", "direct", []*model.User{th.User, th.User2})
   200  	require.NoError(t, err)
   201  	defer th.deleteChannel(direct)
   202  
   203  	post, err := th.createPost(th.User.Id, direct.Id, "dm test", "", model.POST_DEFAULT, 0, false)
   204  	require.NoError(t, err)
   205  	defer th.deleteUserPosts(th.User.Id)
   206  
   207  	post2, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "dm test", "", model.POST_DEFAULT, 0, false)
   208  	require.NoError(t, err)
   209  
   210  	p1, err := th.createFileInfo(th.User.Id, post.Id, "dm test filename", "dm contenttest filename", "jpg", "image/jpeg", 0, 0)
   211  	require.NoError(t, err)
   212  	_, err = th.createFileInfo(th.User.Id, post.Id, "dm other filename", "dm other filename", "jpg", "image/jpeg", 0, 0)
   213  	require.NoError(t, err)
   214  	p2, err := th.createFileInfo(th.User.Id, post2.Id, "channel test filename", "channel contenttest filename", "jpg", "image/jpeg", 0, 0)
   215  	require.NoError(t, err)
   216  	defer th.deleteUserFileInfos(th.User.Id)
   217  
   218  	t.Run("by-name", func(t *testing.T) {
   219  		params := &model.SearchParams{Terms: "test"}
   220  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   221  		require.NoError(t, err)
   222  
   223  		require.Len(t, results.FileInfos, 2)
   224  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
   225  		th.checkFileInfoInSearchResults(t, p2.Id, results.FileInfos)
   226  	})
   227  
   228  	t.Run("by-content", func(t *testing.T) {
   229  		params := &model.SearchParams{Terms: "contenttest"}
   230  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   231  		require.NoError(t, err)
   232  
   233  		require.Len(t, results.FileInfos, 2)
   234  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
   235  		th.checkFileInfoInSearchResults(t, p2.Id, results.FileInfos)
   236  	})
   237  }
   238  
   239  func testFileInfoSearchFileInfosWithPagination(t *testing.T, th *SearchTestHelper) {
   240  	direct, err := th.createDirectChannel(th.Team.Id, "direct", "direct", []*model.User{th.User, th.User2})
   241  	require.NoError(t, err)
   242  	defer th.deleteChannel(direct)
   243  
   244  	post, err := th.createPost(th.User.Id, direct.Id, "dm test", "", model.POST_DEFAULT, 10000, false)
   245  	require.NoError(t, err)
   246  	defer th.deleteUserPosts(th.User.Id)
   247  
   248  	post2, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "dm test", "", model.POST_DEFAULT, 20000, false)
   249  	require.NoError(t, err)
   250  
   251  	p1, err := th.createFileInfo(th.User.Id, post.Id, "dm test filename", "dm contenttest filename", "jpg", "image/jpeg", 10000, 0)
   252  	require.NoError(t, err)
   253  	_, err = th.createFileInfo(th.User.Id, post.Id, "dm other filename", "dm other filename", "jpg", "image/jpeg", 20000, 0)
   254  	require.NoError(t, err)
   255  	p2, err := th.createFileInfo(th.User.Id, post2.Id, "channel test filename", "channel contenttest filename", "jpg", "image/jpeg", 0, 0)
   256  	require.NoError(t, err)
   257  	defer th.deleteUserFileInfos(th.User.Id)
   258  
   259  	t.Run("by-name", func(t *testing.T) {
   260  		params := &model.SearchParams{Terms: "test"}
   261  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 1)
   262  		require.NoError(t, err)
   263  
   264  		require.Len(t, results.FileInfos, 1)
   265  		th.checkFileInfoInSearchResults(t, p2.Id, results.FileInfos)
   266  
   267  		results, err = th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 1, 1)
   268  		require.NoError(t, err)
   269  
   270  		require.Len(t, results.FileInfos, 1)
   271  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
   272  	})
   273  
   274  	t.Run("by-content", func(t *testing.T) {
   275  		params := &model.SearchParams{Terms: "contenttest"}
   276  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 1)
   277  		require.NoError(t, err)
   278  
   279  		require.Len(t, results.FileInfos, 1)
   280  		th.checkFileInfoInSearchResults(t, p2.Id, results.FileInfos)
   281  
   282  		results, err = th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 1, 1)
   283  		require.NoError(t, err)
   284  
   285  		require.Len(t, results.FileInfos, 1)
   286  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
   287  	})
   288  }
   289  
   290  func testFileInfoSearchExactPhraseInQuotes(t *testing.T, th *SearchTestHelper) {
   291  	post, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "testmessage", "", model.POST_DEFAULT, 0, false)
   292  	require.NoError(t, err)
   293  	defer th.deleteUserPosts(th.User.Id)
   294  
   295  	p1, err := th.createFileInfo(th.User.Id, post.Id, "channel test 1 2 3 filename", "channel content test 1 2 3 filename", "jpg", "image/jpeg", 0, 0)
   296  	require.NoError(t, err)
   297  	_, err = th.createFileInfo(th.User.Id, post.Id, "channel test 123 filename", "channel content test 123 filename", "jpg", "image/jpeg", 0, 0)
   298  	require.NoError(t, err)
   299  	defer th.deleteUserFileInfos(th.User.Id)
   300  
   301  	t.Run("by-name", func(t *testing.T) {
   302  		params := &model.SearchParams{Terms: "\"channel test 1 2 3\""}
   303  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   304  		require.NoError(t, err)
   305  
   306  		require.Len(t, results.FileInfos, 1)
   307  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
   308  	})
   309  
   310  	t.Run("by-content", func(t *testing.T) {
   311  		params := &model.SearchParams{Terms: "\"channel content test 1 2 3\""}
   312  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   313  		require.NoError(t, err)
   314  
   315  		require.Len(t, results.FileInfos, 1)
   316  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
   317  	})
   318  }
   319  
   320  func testFileInfoSearchEmailAddresses(t *testing.T, th *SearchTestHelper) {
   321  	post, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "testmessage", "", model.POST_DEFAULT, 0, false)
   322  	require.NoError(t, err)
   323  	defer th.deleteUserPosts(th.User.Id)
   324  
   325  	p1, err := th.createFileInfo(th.User.Id, post.Id, "test email test@test.com", "test email test@content.com", "jpg", "image/jpeg", 0, 0)
   326  	require.NoError(t, err)
   327  	_, err = th.createFileInfo(th.User.Id, post.Id, "test email test2@test.com", "test email test2@content.com", "jpg", "image/jpeg", 0, 0)
   328  	require.NoError(t, err)
   329  	defer th.deleteUserFileInfos(th.User.Id)
   330  
   331  	t.Run("by-name", func(t *testing.T) {
   332  		t.Run("Should search email addresses enclosed by quotes", func(t *testing.T) {
   333  			params := &model.SearchParams{Terms: "\"test@test.com\""}
   334  			results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   335  			require.NoError(t, err)
   336  
   337  			require.Len(t, results.FileInfos, 1)
   338  			th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
   339  		})
   340  
   341  		t.Run("Should search email addresses without quotes", func(t *testing.T) {
   342  			params := &model.SearchParams{Terms: "test@test.com"}
   343  			results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   344  			require.NoError(t, err)
   345  
   346  			require.Len(t, results.FileInfos, 1)
   347  			th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
   348  		})
   349  	})
   350  	t.Run("by-content", func(t *testing.T) {
   351  		t.Run("Should search email addresses enclosed by quotes", func(t *testing.T) {
   352  			params := &model.SearchParams{Terms: "\"test@content.com\""}
   353  			results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   354  			require.NoError(t, err)
   355  
   356  			require.Len(t, results.FileInfos, 1)
   357  			th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
   358  		})
   359  
   360  		t.Run("Should search email addresses without quotes", func(t *testing.T) {
   361  			params := &model.SearchParams{Terms: "test@content.com"}
   362  			results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   363  			require.NoError(t, err)
   364  
   365  			require.Len(t, results.FileInfos, 1)
   366  			th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
   367  		})
   368  	})
   369  }
   370  
   371  func testFileInfoSearchMarkdownUnderscores(t *testing.T, th *SearchTestHelper) {
   372  	post, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "testmessage", "", model.POST_DEFAULT, 0, false)
   373  	require.NoError(t, err)
   374  	defer th.deleteUserPosts(th.User.Id)
   375  
   376  	p1, err := th.createFileInfo(th.User.Id, post.Id, "_start middle end_ _another_", "_start middle end_ _another_", "jpg", "image/jpeg", 0, 0)
   377  	require.NoError(t, err)
   378  	defer th.deleteUserFileInfos(th.User.Id)
   379  
   380  	t.Run("Should search the start inside the markdown underscore", func(t *testing.T) {
   381  		params := &model.SearchParams{Terms: "start"}
   382  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   383  		require.NoError(t, err)
   384  
   385  		require.Len(t, results.FileInfos, 1)
   386  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
   387  	})
   388  
   389  	t.Run("Should search a word in the middle of the markdown underscore", func(t *testing.T) {
   390  		params := &model.SearchParams{Terms: "middle"}
   391  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   392  		require.NoError(t, err)
   393  
   394  		require.Len(t, results.FileInfos, 1)
   395  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
   396  	})
   397  
   398  	t.Run("Should search in the end of the markdown underscore", func(t *testing.T) {
   399  		params := &model.SearchParams{Terms: "end"}
   400  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   401  		require.NoError(t, err)
   402  
   403  		require.Len(t, results.FileInfos, 1)
   404  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
   405  	})
   406  
   407  	t.Run("Should search inside markdown underscore", func(t *testing.T) {
   408  		params := &model.SearchParams{Terms: "another"}
   409  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   410  		require.NoError(t, err)
   411  
   412  		require.Len(t, results.FileInfos, 1)
   413  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
   414  	})
   415  }
   416  
   417  func testFileInfoSearchNonLatinWords(t *testing.T, th *SearchTestHelper) {
   418  	post, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "testmessage", "", model.POST_DEFAULT, 0, false)
   419  	require.NoError(t, err)
   420  	defer th.deleteUserPosts(th.User.Id)
   421  
   422  	t.Run("Should be able to search chinese words", func(t *testing.T) {
   423  		p1, err := th.createFileInfo(th.User.Id, post.Id, "你好", "你好", "jpg", "image/jpeg", 0, 0)
   424  		require.NoError(t, err)
   425  		p2, err := th.createFileInfo(th.User.Id, post.Id, "你", "你", "jpg", "image/jpeg", 0, 0)
   426  		require.NoError(t, err)
   427  		defer th.deleteUserFileInfos(th.User.Id)
   428  
   429  		t.Run("Should search one word", func(t *testing.T) {
   430  			params := &model.SearchParams{Terms: "你"}
   431  			results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   432  			require.NoError(t, err)
   433  
   434  			require.Len(t, results.FileInfos, 1)
   435  			th.checkFileInfoInSearchResults(t, p2.Id, results.FileInfos)
   436  		})
   437  		t.Run("Should search two words", func(t *testing.T) {
   438  			params := &model.SearchParams{Terms: "你好"}
   439  			results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   440  			require.NoError(t, err)
   441  
   442  			require.Len(t, results.FileInfos, 1)
   443  			th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
   444  		})
   445  		t.Run("Should search with wildcard", func(t *testing.T) {
   446  			params := &model.SearchParams{Terms: "你*"}
   447  			results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   448  			require.NoError(t, err)
   449  
   450  			require.Len(t, results.FileInfos, 2)
   451  			th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
   452  			th.checkFileInfoInSearchResults(t, p2.Id, results.FileInfos)
   453  		})
   454  	})
   455  	t.Run("Should be able to search cyrillic words", func(t *testing.T) {
   456  		p1, err := th.createFileInfo(th.User.Id, post.Id, "слово test", "слово test", "jpg", "image/jpeg", 0, 0)
   457  		require.NoError(t, err)
   458  		defer th.deleteUserFileInfos(th.User.Id)
   459  
   460  		t.Run("Should search one word", func(t *testing.T) {
   461  			params := &model.SearchParams{Terms: "слово"}
   462  			results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   463  			require.NoError(t, err)
   464  
   465  			require.Len(t, results.FileInfos, 1)
   466  			th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
   467  		})
   468  		t.Run("Should search using wildcard", func(t *testing.T) {
   469  			params := &model.SearchParams{Terms: "слов*"}
   470  			results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   471  			require.NoError(t, err)
   472  
   473  			require.Len(t, results.FileInfos, 1)
   474  			th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
   475  		})
   476  	})
   477  
   478  	t.Run("Should be able to search japanese words", func(t *testing.T) {
   479  		p1, err := th.createFileInfo(th.User.Id, post.Id, "本", "本", "jpg", "image/jpeg", 0, 0)
   480  		require.NoError(t, err)
   481  		p2, err := th.createFileInfo(th.User.Id, post.Id, "本木", "本木", "jpg", "image/jpeg", 0, 0)
   482  		require.NoError(t, err)
   483  		defer th.deleteUserFileInfos(th.User.Id)
   484  
   485  		t.Run("Should search one word", func(t *testing.T) {
   486  			params := &model.SearchParams{Terms: "本"}
   487  			results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   488  			require.NoError(t, err)
   489  
   490  			require.Len(t, results.FileInfos, 2)
   491  			th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
   492  			th.checkFileInfoInSearchResults(t, p2.Id, results.FileInfos)
   493  		})
   494  		t.Run("Should search two words", func(t *testing.T) {
   495  			params := &model.SearchParams{Terms: "本木"}
   496  			results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   497  			require.NoError(t, err)
   498  
   499  			require.Len(t, results.FileInfos, 1)
   500  			th.checkFileInfoInSearchResults(t, p2.Id, results.FileInfos)
   501  		})
   502  		t.Run("Should search with wildcard", func(t *testing.T) {
   503  			params := &model.SearchParams{Terms: "本*"}
   504  			results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   505  			require.NoError(t, err)
   506  
   507  			require.Len(t, results.FileInfos, 2)
   508  			th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
   509  			th.checkFileInfoInSearchResults(t, p2.Id, results.FileInfos)
   510  		})
   511  	})
   512  
   513  	t.Run("Should be able to search korean words", func(t *testing.T) {
   514  		p1, err := th.createFileInfo(th.User.Id, post.Id, "불", "불", "jpg", "image/jpeg", 0, 0)
   515  		require.NoError(t, err)
   516  		p2, err := th.createFileInfo(th.User.Id, post.Id, "불다", "불다", "jpg", "image/jpeg", 0, 0)
   517  		require.NoError(t, err)
   518  		defer th.deleteUserFileInfos(th.User.Id)
   519  
   520  		t.Run("Should search one word", func(t *testing.T) {
   521  			params := &model.SearchParams{Terms: "불"}
   522  			results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   523  			require.NoError(t, err)
   524  
   525  			require.Len(t, results.FileInfos, 1)
   526  			th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
   527  		})
   528  		t.Run("Should search two words", func(t *testing.T) {
   529  			params := &model.SearchParams{Terms: "불다"}
   530  			results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   531  			require.NoError(t, err)
   532  
   533  			require.Len(t, results.FileInfos, 1)
   534  			th.checkFileInfoInSearchResults(t, p2.Id, results.FileInfos)
   535  		})
   536  		t.Run("Should search with wildcard", func(t *testing.T) {
   537  			params := &model.SearchParams{Terms: "불*"}
   538  			results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   539  			require.NoError(t, err)
   540  
   541  			require.Len(t, results.FileInfos, 2)
   542  			th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
   543  			th.checkFileInfoInSearchResults(t, p2.Id, results.FileInfos)
   544  		})
   545  	})
   546  }
   547  
   548  func testFileInfoSearchAlternativeSpellings(t *testing.T, th *SearchTestHelper) {
   549  	post, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "testmessage", "", model.POST_DEFAULT, 0, false)
   550  	require.NoError(t, err)
   551  	defer th.deleteUserPosts(th.User.Id)
   552  
   553  	p1, err := th.createFileInfo(th.User.Id, post.Id, "Straße test", "Straße test", "jpg", "image/jpeg", 0, 0)
   554  	require.NoError(t, err)
   555  	p2, err := th.createFileInfo(th.User.Id, post.Id, "Strasse test", "Strasse test", "jpg", "image/jpeg", 0, 0)
   556  	require.NoError(t, err)
   557  	defer th.deleteUserFileInfos(th.User.Id)
   558  
   559  	params := &model.SearchParams{Terms: "Straße"}
   560  	results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   561  	require.NoError(t, err)
   562  
   563  	require.Len(t, results.FileInfos, 2)
   564  	th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
   565  	th.checkFileInfoInSearchResults(t, p2.Id, results.FileInfos)
   566  
   567  	params = &model.SearchParams{Terms: "Strasse"}
   568  	results, err = th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   569  	require.NoError(t, err)
   570  
   571  	require.Len(t, results.FileInfos, 2)
   572  	th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
   573  	th.checkFileInfoInSearchResults(t, p2.Id, results.FileInfos)
   574  }
   575  
   576  func testFileInfoSearchAlternativeSpellingsAccents(t *testing.T, th *SearchTestHelper) {
   577  	post, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "testmessage", "", model.POST_DEFAULT, 0, false)
   578  	require.NoError(t, err)
   579  	defer th.deleteUserPosts(th.User.Id)
   580  
   581  	p1, err := th.createFileInfo(th.User.Id, post.Id, "café", "café", "jpg", "image/jpeg", 0, 0)
   582  	require.NoError(t, err)
   583  	p2, err := th.createFileInfo(th.User.Id, post.Id, "café", "café", "jpg", "image/jpeg", 0, 0)
   584  	require.NoError(t, err)
   585  	defer th.deleteUserFileInfos(th.User.Id)
   586  
   587  	params := &model.SearchParams{Terms: "café"}
   588  	results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   589  	require.NoError(t, err)
   590  
   591  	require.Len(t, results.FileInfos, 2)
   592  	th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
   593  	th.checkFileInfoInSearchResults(t, p2.Id, results.FileInfos)
   594  
   595  	params = &model.SearchParams{Terms: "café"}
   596  	results, err = th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   597  	require.NoError(t, err)
   598  
   599  	require.Len(t, results.FileInfos, 2)
   600  	th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
   601  	th.checkFileInfoInSearchResults(t, p2.Id, results.FileInfos)
   602  
   603  	params = &model.SearchParams{Terms: "cafe"}
   604  	results, err = th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   605  	require.NoError(t, err)
   606  
   607  	require.Len(t, results.FileInfos, 0)
   608  }
   609  
   610  func testFileInfoSearchOrExcludeFileInfosBySpecificUser(t *testing.T, th *SearchTestHelper) {
   611  	post, err := th.createPost(th.User.Id, th.ChannelPrivate.Id, "testmessage", "", model.POST_DEFAULT, 0, false)
   612  	require.NoError(t, err)
   613  	defer th.deleteUserPosts(th.User.Id)
   614  
   615  	p1, err := th.createFileInfo(th.User.Id, post.Id, "test fromuser filename", "test fromuser filename", "jpg", "image/jpeg", 0, 0)
   616  	require.NoError(t, err)
   617  	_, err = th.createFileInfo(th.User2.Id, post.Id, "test fromuser filename", "test fromuser filename", "jpg", "image/jpeg", 0, 0)
   618  	require.NoError(t, err)
   619  	defer th.deleteUserFileInfos(th.User.Id)
   620  	defer th.deleteUserFileInfos(th.User2.Id)
   621  
   622  	params := &model.SearchParams{Terms: "fromuser", FromUsers: []string{th.User.Id}}
   623  	results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   624  	require.NoError(t, err)
   625  
   626  	require.Len(t, results.FileInfos, 1)
   627  	th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
   628  }
   629  
   630  func testFileInfoSearchOrExcludeFileInfosInChannel(t *testing.T, th *SearchTestHelper) {
   631  	post, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "testmessage", "", model.POST_DEFAULT, 0, false)
   632  	require.NoError(t, err)
   633  	defer th.deleteUserPosts(th.User.Id)
   634  	post2, err := th.createPost(th.User.Id, th.ChannelPrivate.Id, "testmessage", "", model.POST_DEFAULT, 0, false)
   635  	require.NoError(t, err)
   636  	defer th.deleteUserPosts(th.User.Id)
   637  
   638  	p1, err := th.createFileInfo(th.User.Id, post.Id, "test fromuser filename", "test fromuser filename", "jpg", "image/jpeg", 0, 0)
   639  	require.NoError(t, err)
   640  	_, err = th.createFileInfo(th.User.Id, post2.Id, "test fromuser filename", "test fromuser filename", "jpg", "image/jpeg", 0, 0)
   641  	require.NoError(t, err)
   642  	defer th.deleteUserFileInfos(th.User.Id)
   643  	defer th.deleteUserFileInfos(th.User2.Id)
   644  
   645  	params := &model.SearchParams{Terms: "fromuser", InChannels: []string{th.ChannelBasic.Id}}
   646  	results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   647  	require.NoError(t, err)
   648  
   649  	require.Len(t, results.FileInfos, 1)
   650  	th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
   651  }
   652  
   653  func testFileInfoSearchOrExcludeFileInfosInDMGM(t *testing.T, th *SearchTestHelper) {
   654  	direct, err := th.createDirectChannel(th.Team.Id, "direct", "direct", []*model.User{th.User, th.User2})
   655  	require.NoError(t, err)
   656  	defer th.deleteChannel(direct)
   657  
   658  	group, err := th.createGroupChannel(th.Team.Id, "test group", []*model.User{th.User, th.User2})
   659  	require.NoError(t, err)
   660  	defer th.deleteChannel(group)
   661  
   662  	post1, err := th.createPost(th.User.Id, direct.Id, "test fromuser", "", model.POST_DEFAULT, 0, false)
   663  	require.NoError(t, err)
   664  	post2, err := th.createPost(th.User2.Id, group.Id, "test fromuser 2", "", model.POST_DEFAULT, 0, false)
   665  	require.NoError(t, err)
   666  	defer th.deleteUserPosts(th.User.Id)
   667  	defer th.deleteUserPosts(th.User2.Id)
   668  
   669  	p1, err := th.createFileInfo(th.User.Id, post1.Id, "test fromuser", "test fromuser", "jpg", "image/jpg", 0, 0)
   670  	require.NoError(t, err)
   671  	p2, err := th.createFileInfo(th.User2.Id, post2.Id, "test fromuser 2", "test fromuser 2", "jpg", "image/jpg", 0, 0)
   672  	require.NoError(t, err)
   673  	defer th.deleteUserFileInfos(th.User.Id)
   674  	defer th.deleteUserFileInfos(th.User2.Id)
   675  
   676  	t.Run("Should be able to search in both DM and GM channels", func(t *testing.T) {
   677  		params := &model.SearchParams{
   678  			Terms:      "fromuser",
   679  			InChannels: []string{direct.Id, group.Id},
   680  		}
   681  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   682  		require.NoError(t, err)
   683  
   684  		require.Len(t, results.FileInfos, 2)
   685  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
   686  		th.checkFileInfoInSearchResults(t, p2.Id, results.FileInfos)
   687  	})
   688  
   689  	t.Run("Should be able to search only in DM channel", func(t *testing.T) {
   690  		params := &model.SearchParams{
   691  			Terms:      "fromuser",
   692  			InChannels: []string{direct.Id},
   693  		}
   694  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   695  		require.NoError(t, err)
   696  
   697  		require.Len(t, results.FileInfos, 1)
   698  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
   699  	})
   700  
   701  	t.Run("Should be able to search only in GM channel", func(t *testing.T) {
   702  		params := &model.SearchParams{
   703  			Terms:      "fromuser",
   704  			InChannels: []string{group.Id},
   705  		}
   706  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   707  		require.NoError(t, err)
   708  
   709  		require.Len(t, results.FileInfos, 1)
   710  		th.checkFileInfoInSearchResults(t, p2.Id, results.FileInfos)
   711  	})
   712  }
   713  
   714  func testFileInfoSearchOrExcludeByExtensions(t *testing.T, th *SearchTestHelper) {
   715  	post, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "testmessage", "", model.POST_DEFAULT, 0, false)
   716  	require.NoError(t, err)
   717  	defer th.deleteUserPosts(th.User.Id)
   718  
   719  	p1, err := th.createFileInfo(th.User.Id, post.Id, "test", "test", "jpg", "image/jpeg", 0, 0)
   720  	require.NoError(t, err)
   721  	p2, err := th.createFileInfo(th.User.Id, post.Id, "test", "test", "png", "image/png", 0, 0)
   722  	require.NoError(t, err)
   723  	p3, err := th.createFileInfo(th.User.Id, post.Id, "test", "test", "bmp", "image/bmp", 0, 0)
   724  	require.NoError(t, err)
   725  	defer th.deleteUserFileInfos(th.User.Id)
   726  
   727  	t.Run("Search by one extension", func(t *testing.T) {
   728  		params := &model.SearchParams{
   729  			Terms:      "test",
   730  			InChannels: []string{th.ChannelBasic.Id},
   731  			Extensions: []string{"jpg"},
   732  		}
   733  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   734  		require.NoError(t, err)
   735  
   736  		require.Len(t, results.FileInfos, 1)
   737  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
   738  	})
   739  
   740  	t.Run("Search by multiple extensions", func(t *testing.T) {
   741  		params := &model.SearchParams{
   742  			Terms:      "test",
   743  			InChannels: []string{th.ChannelBasic.Id},
   744  			Extensions: []string{"jpg", "bmp"},
   745  		}
   746  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   747  		require.NoError(t, err)
   748  
   749  		require.Len(t, results.FileInfos, 2)
   750  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
   751  		th.checkFileInfoInSearchResults(t, p3.Id, results.FileInfos)
   752  	})
   753  
   754  	t.Run("Search excluding one extension", func(t *testing.T) {
   755  		params := &model.SearchParams{
   756  			Terms:              "test",
   757  			InChannels:         []string{th.ChannelBasic.Id},
   758  			ExcludedExtensions: []string{"jpg"},
   759  		}
   760  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   761  		require.NoError(t, err)
   762  
   763  		require.Len(t, results.FileInfos, 2)
   764  		th.checkFileInfoInSearchResults(t, p2.Id, results.FileInfos)
   765  		th.checkFileInfoInSearchResults(t, p3.Id, results.FileInfos)
   766  	})
   767  
   768  	t.Run("Search excluding multiple extensions", func(t *testing.T) {
   769  		params := &model.SearchParams{
   770  			Terms:              "test",
   771  			InChannels:         []string{th.ChannelBasic.Id},
   772  			ExcludedExtensions: []string{"jpg", "bmp"},
   773  		}
   774  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   775  		require.NoError(t, err)
   776  
   777  		require.Len(t, results.FileInfos, 1)
   778  		th.checkFileInfoInSearchResults(t, p2.Id, results.FileInfos)
   779  	})
   780  }
   781  
   782  func testFileInfoFilterFilesInSpecificDate(t *testing.T, th *SearchTestHelper) {
   783  	post1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "testmessage", "", model.POST_DEFAULT, 0, false)
   784  	require.NoError(t, err)
   785  	post2, err := th.createPost(th.User.Id, th.ChannelPrivate.Id, "testmessage", "", model.POST_DEFAULT, 0, false)
   786  	require.NoError(t, err)
   787  	defer th.deleteUserPosts(th.User.Id)
   788  
   789  	creationDate := model.GetMillisForTime(time.Date(2020, 03, 22, 12, 0, 0, 0, time.UTC))
   790  	p1, err := th.createFileInfo(th.User.Id, post1.Id, "test in specific date", "test in specific date", "jpg", "image/jpeg", creationDate, 0)
   791  	require.NoError(t, err)
   792  	creationDate2 := model.GetMillisForTime(time.Date(2020, 03, 23, 0, 0, 0, 0, time.UTC))
   793  	p2, err := th.createFileInfo(th.User.Id, post2.Id, "test in the present", "test in the present", "jpg", "image/jpeg", creationDate2, 0)
   794  	require.NoError(t, err)
   795  	creationDate3 := model.GetMillisForTime(time.Date(2020, 03, 21, 23, 59, 59, 0, time.UTC))
   796  	p3, err := th.createFileInfo(th.User.Id, post1.Id, "test in the present", "test in the present", "jpg", "image/jpeg", creationDate3, 0)
   797  	require.NoError(t, err)
   798  	defer th.deleteUserFileInfos(th.User.Id)
   799  
   800  	t.Run("Should be able to search posts on date", func(t *testing.T) {
   801  		params := &model.SearchParams{
   802  			Terms:  "test",
   803  			OnDate: "2020-03-22",
   804  		}
   805  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   806  		require.NoError(t, err)
   807  
   808  		require.Len(t, results.FileInfos, 1)
   809  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
   810  	})
   811  	t.Run("Should be able to exclude posts on date", func(t *testing.T) {
   812  		params := &model.SearchParams{
   813  			Terms:        "test",
   814  			ExcludedDate: "2020-03-22",
   815  		}
   816  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   817  		require.NoError(t, err)
   818  
   819  		require.Len(t, results.FileInfos, 2)
   820  		th.checkFileInfoInSearchResults(t, p2.Id, results.FileInfos)
   821  		th.checkFileInfoInSearchResults(t, p3.Id, results.FileInfos)
   822  	})
   823  }
   824  
   825  func testFileInfoFilterFilesBeforeSpecificDate(t *testing.T, th *SearchTestHelper) {
   826  	post1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "testmessage", "", model.POST_DEFAULT, 0, false)
   827  	require.NoError(t, err)
   828  	post2, err := th.createPost(th.User.Id, th.ChannelPrivate.Id, "testmessage", "", model.POST_DEFAULT, 0, false)
   829  	require.NoError(t, err)
   830  	defer th.deleteUserPosts(th.User.Id)
   831  
   832  	creationDate := model.GetMillisForTime(time.Date(2020, 03, 01, 12, 0, 0, 0, time.UTC))
   833  	p1, err := th.createFileInfo(th.User.Id, post1.Id, "test in specific date", "test in specific date", "jpg", "image/jpeg", creationDate, 0)
   834  	require.NoError(t, err)
   835  	creationDate2 := model.GetMillisForTime(time.Date(2020, 03, 22, 23, 59, 59, 0, time.UTC))
   836  	p2, err := th.createFileInfo(th.User.Id, post2.Id, "test in specific date 2", "test in specific date 2", "jpg", "image/jpeg", creationDate2, 0)
   837  	require.NoError(t, err)
   838  	creationDate3 := model.GetMillisForTime(time.Date(2020, 03, 26, 16, 55, 0, 0, time.UTC))
   839  	p3, err := th.createFileInfo(th.User.Id, post1.Id, "test in the present", "test in the present", "jpg", "image/jpeg", creationDate3, 0)
   840  	require.NoError(t, err)
   841  	defer th.deleteUserFileInfos(th.User.Id)
   842  
   843  	t.Run("Should be able to search posts before a date", func(t *testing.T) {
   844  		params := &model.SearchParams{
   845  			Terms:      "test",
   846  			BeforeDate: "2020-03-23",
   847  		}
   848  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   849  		require.NoError(t, err)
   850  
   851  		require.Len(t, results.FileInfos, 2)
   852  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
   853  		th.checkFileInfoInSearchResults(t, p2.Id, results.FileInfos)
   854  	})
   855  
   856  	t.Run("Should be able to exclude posts before a date", func(t *testing.T) {
   857  		params := &model.SearchParams{
   858  			Terms:              "test",
   859  			ExcludedBeforeDate: "2020-03-23",
   860  		}
   861  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   862  		require.NoError(t, err)
   863  
   864  		require.Len(t, results.FileInfos, 1)
   865  		th.checkFileInfoInSearchResults(t, p3.Id, results.FileInfos)
   866  	})
   867  }
   868  
   869  func testFileInfoFilterFilesAfterSpecificDate(t *testing.T, th *SearchTestHelper) {
   870  	post1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "testmessage", "", model.POST_DEFAULT, 0, false)
   871  	require.NoError(t, err)
   872  	post2, err := th.createPost(th.User.Id, th.ChannelPrivate.Id, "testmessage", "", model.POST_DEFAULT, 0, false)
   873  	require.NoError(t, err)
   874  	defer th.deleteUserPosts(th.User.Id)
   875  
   876  	creationDate := model.GetMillisForTime(time.Date(2020, 03, 01, 12, 0, 0, 0, time.UTC))
   877  	p1, err := th.createFileInfo(th.User.Id, post1.Id, "test in specific date", "test in specific date", "jpg", "image/jpeg", creationDate, 0)
   878  	require.NoError(t, err)
   879  	creationDate2 := model.GetMillisForTime(time.Date(2020, 03, 22, 23, 59, 59, 0, time.UTC))
   880  	p2, err := th.createFileInfo(th.User.Id, post2.Id, "test in specific date 2", "test in specific date 2", "jpg", "image/jpeg", creationDate2, 0)
   881  	require.NoError(t, err)
   882  	creationDate3 := model.GetMillisForTime(time.Date(2020, 03, 26, 16, 55, 0, 0, time.UTC))
   883  	p3, err := th.createFileInfo(th.User.Id, post1.Id, "test in the present", "test in the present", "jpg", "image/jpeg", creationDate3, 0)
   884  	require.NoError(t, err)
   885  	defer th.deleteUserFileInfos(th.User.Id)
   886  
   887  	t.Run("Should be able to search posts after a date", func(t *testing.T) {
   888  		params := &model.SearchParams{
   889  			Terms:     "test",
   890  			AfterDate: "2020-03-23",
   891  		}
   892  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   893  		require.NoError(t, err)
   894  
   895  		require.Len(t, results.FileInfos, 1)
   896  		th.checkFileInfoInSearchResults(t, p3.Id, results.FileInfos)
   897  	})
   898  
   899  	t.Run("Should be able to exclude posts after a date", func(t *testing.T) {
   900  		params := &model.SearchParams{
   901  			Terms:             "test",
   902  			ExcludedAfterDate: "2020-03-23",
   903  		}
   904  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   905  		require.NoError(t, err)
   906  
   907  		require.Len(t, results.FileInfos, 2)
   908  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
   909  		th.checkFileInfoInSearchResults(t, p2.Id, results.FileInfos)
   910  	})
   911  }
   912  
   913  func testFileInfoFilterFilesWithATerm(t *testing.T, th *SearchTestHelper) {
   914  	post1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "testmessage", "", model.POST_DEFAULT, 0, false)
   915  	require.NoError(t, err)
   916  	post2, err := th.createPost(th.User.Id, th.ChannelPrivate.Id, "testmessage", "", model.POST_DEFAULT, 0, false)
   917  	require.NoError(t, err)
   918  	defer th.deleteUserPosts(th.User.Id)
   919  
   920  	p1, err := th.createFileInfo(th.User.Id, post1.Id, "one two three", "one two three", "jpg", "image/jpeg", 0, 0)
   921  	require.NoError(t, err)
   922  	p2, err := th.createFileInfo(th.User.Id, post2.Id, "one four five six", "one four five six", "jpg", "image/jpeg", 0, 0)
   923  	require.NoError(t, err)
   924  	_, err = th.createFileInfo(th.User.Id, post1.Id, "one seven eight nine", "one seven eight nine", "jpg", "image/jpeg", 0, 0)
   925  	require.NoError(t, err)
   926  	defer th.deleteUserFileInfos(th.User.Id)
   927  
   928  	t.Run("Should exclude terms", func(t *testing.T) {
   929  		params := &model.SearchParams{
   930  			Terms:         "one",
   931  			ExcludedTerms: "five eight",
   932  		}
   933  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   934  		require.NoError(t, err)
   935  
   936  		require.Len(t, results.FileInfos, 1)
   937  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
   938  	})
   939  
   940  	t.Run("Should exclude quoted terms", func(t *testing.T) {
   941  		params := &model.SearchParams{
   942  			Terms:         "one",
   943  			ExcludedTerms: "\"eight nine\"",
   944  		}
   945  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   946  		require.NoError(t, err)
   947  
   948  		require.Len(t, results.FileInfos, 2)
   949  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
   950  		th.checkFileInfoInSearchResults(t, p2.Id, results.FileInfos)
   951  	})
   952  }
   953  
   954  func testFileInfoSearchUsingBooleanOperators(t *testing.T, th *SearchTestHelper) {
   955  	post, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "testmessage", "", model.POST_DEFAULT, 0, false)
   956  	require.NoError(t, err)
   957  	defer th.deleteUserPosts(th.User.Id)
   958  
   959  	p1, err := th.createFileInfo(th.User.Id, post.Id, "one two three message", "one two three message", "jpg", "image/jpeg", 0, 0)
   960  	require.NoError(t, err)
   961  	p2, err := th.createFileInfo(th.User.Id, post.Id, "two messages", "two messages", "jpg", "image/jpeg", 0, 0)
   962  	require.NoError(t, err)
   963  	_, err = th.createFileInfo(th.User.Id, post.Id, "another message", "another message", "jpg", "image/jpeg", 0, 0)
   964  	require.NoError(t, err)
   965  	defer th.deleteUserFileInfos(th.User.Id)
   966  
   967  	t.Run("Should search posts using OR operator", func(t *testing.T) {
   968  		params := &model.SearchParams{
   969  			Terms:   "one two",
   970  			OrTerms: true,
   971  		}
   972  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   973  		require.NoError(t, err)
   974  
   975  		require.Len(t, results.FileInfos, 2)
   976  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
   977  		th.checkFileInfoInSearchResults(t, p2.Id, results.FileInfos)
   978  	})
   979  
   980  	t.Run("Should search posts using AND operator", func(t *testing.T) {
   981  		params := &model.SearchParams{
   982  			Terms:   "one two",
   983  			OrTerms: false,
   984  		}
   985  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   986  		require.NoError(t, err)
   987  
   988  		require.Len(t, results.FileInfos, 1)
   989  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
   990  	})
   991  }
   992  
   993  func testFileInfoSearchUsingCombinedFilters(t *testing.T, th *SearchTestHelper) {
   994  	post1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "testmessage", "", model.POST_DEFAULT, 0, false)
   995  	require.NoError(t, err)
   996  	post2, err := th.createPost(th.User.Id, th.ChannelPrivate.Id, "testmessage", "", model.POST_DEFAULT, 0, false)
   997  	require.NoError(t, err)
   998  	defer th.deleteUserPosts(th.User.Id)
   999  
  1000  	creationDate := model.GetMillisForTime(time.Date(2020, 03, 01, 12, 0, 0, 0, time.UTC))
  1001  	p1, err := th.createFileInfo(th.User.Id, post2.Id, "one two three message", "one two three message", "jpg", "image/jpeg", creationDate, 0)
  1002  	require.NoError(t, err)
  1003  	creationDate2 := model.GetMillisForTime(time.Date(2020, 03, 10, 12, 0, 0, 0, time.UTC))
  1004  	p2, err := th.createFileInfo(th.User2.Id, post2.Id, "two messages", "two messages", "jpg", "image/jpeg", creationDate2, 0)
  1005  	require.NoError(t, err)
  1006  	creationDate3 := model.GetMillisForTime(time.Date(2020, 03, 20, 12, 0, 0, 0, time.UTC))
  1007  	p3, err := th.createFileInfo(th.User.Id, post1.Id, "two another message", "two another message", "jpg", "image/jpeg", creationDate3, 0)
  1008  	require.NoError(t, err)
  1009  	defer th.deleteUserFileInfos(th.User.Id)
  1010  	defer th.deleteUserFileInfos(th.User2.Id)
  1011  
  1012  	t.Run("Should search combining from user and in channel filters", func(t *testing.T) {
  1013  		params := &model.SearchParams{
  1014  			Terms:      "two",
  1015  			FromUsers:  []string{th.User2.Id},
  1016  			InChannels: []string{th.ChannelPrivate.Id},
  1017  		}
  1018  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1019  		require.NoError(t, err)
  1020  
  1021  		require.Len(t, results.FileInfos, 1)
  1022  		th.checkFileInfoInSearchResults(t, p2.Id, results.FileInfos)
  1023  	})
  1024  
  1025  	t.Run("Should search combining excluding users and in channel filters", func(t *testing.T) {
  1026  		params := &model.SearchParams{
  1027  			Terms:         "two",
  1028  			ExcludedUsers: []string{th.User2.Id},
  1029  			InChannels:    []string{th.ChannelPrivate.Id},
  1030  		}
  1031  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1032  		require.NoError(t, err)
  1033  
  1034  		require.Len(t, results.FileInfos, 1)
  1035  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
  1036  	})
  1037  
  1038  	t.Run("Should search combining excluding dates and in channel filters", func(t *testing.T) {
  1039  		params := &model.SearchParams{
  1040  			Terms:              "two",
  1041  			ExcludedBeforeDate: "2020-03-09",
  1042  			ExcludedAfterDate:  "2020-03-11",
  1043  			InChannels:         []string{th.ChannelPrivate.Id},
  1044  		}
  1045  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1046  		require.NoError(t, err)
  1047  
  1048  		require.Len(t, results.FileInfos, 1)
  1049  		th.checkFileInfoInSearchResults(t, p2.Id, results.FileInfos)
  1050  	})
  1051  	t.Run("Should search combining excluding dates and in channel filters", func(t *testing.T) {
  1052  		params := &model.SearchParams{
  1053  			Terms:            "two",
  1054  			AfterDate:        "2020-03-11",
  1055  			ExcludedChannels: []string{th.ChannelPrivate.Id},
  1056  		}
  1057  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1058  		require.NoError(t, err)
  1059  
  1060  		require.Len(t, results.FileInfos, 1)
  1061  		th.checkFileInfoInSearchResults(t, p3.Id, results.FileInfos)
  1062  	})
  1063  }
  1064  
  1065  func testFileInfoSearchIgnoringStopWords(t *testing.T, th *SearchTestHelper) {
  1066  	post, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "testmessage", "", model.POST_DEFAULT, 0, false)
  1067  	require.NoError(t, err)
  1068  	defer th.deleteUserPosts(th.User.Id)
  1069  
  1070  	p1, err := th.createFileInfo(th.User.Id, post.Id, "the search for a bunch of stop words", "the search for a bunch of stop words", "jpg", "image/jpeg", 0, 0)
  1071  	require.NoError(t, err)
  1072  	p2, err := th.createFileInfo(th.User.Id, post.Id, "the objective is to avoid a bunch of stop words", "the objective is to avoid a bunch of stop words", "jpg", "image/jpeg", 0, 0)
  1073  	require.NoError(t, err)
  1074  	p3, err := th.createFileInfo(th.User.Id, post.Id, "in the a on to where you", "in the a on to where you", "jpg", "image/jpeg", 0, 0)
  1075  	require.NoError(t, err)
  1076  	p4, err := th.createFileInfo(th.User.Id, post.Id, "where is the car?", "where is the car?", "jpg", "image/jpeg", 0, 0)
  1077  	require.NoError(t, err)
  1078  	defer th.deleteUserFileInfos(th.User.Id)
  1079  
  1080  	t.Run("Should avoid stop word 'the'", func(t *testing.T) {
  1081  		params := &model.SearchParams{
  1082  			Terms: "the search",
  1083  		}
  1084  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1085  		require.NoError(t, err)
  1086  
  1087  		require.Len(t, results.FileInfos, 1)
  1088  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
  1089  	})
  1090  
  1091  	t.Run("Should avoid stop word 'a'", func(t *testing.T) {
  1092  		params := &model.SearchParams{
  1093  			Terms: "a avoid",
  1094  		}
  1095  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1096  		require.NoError(t, err)
  1097  
  1098  		require.Len(t, results.FileInfos, 1)
  1099  		th.checkFileInfoInSearchResults(t, p2.Id, results.FileInfos)
  1100  	})
  1101  
  1102  	t.Run("Should avoid stop word 'in'", func(t *testing.T) {
  1103  		params := &model.SearchParams{
  1104  			Terms: "in where you",
  1105  		}
  1106  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1107  		require.NoError(t, err)
  1108  
  1109  		require.Len(t, results.FileInfos, 1)
  1110  		th.checkFileInfoInSearchResults(t, p3.Id, results.FileInfos)
  1111  	})
  1112  
  1113  	t.Run("Should avoid stop words 'where', 'is' and 'the'", func(t *testing.T) {
  1114  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{{Terms: "is the car"}}, th.User.Id, th.Team.Id, 0, 20)
  1115  		require.NoError(t, err)
  1116  		require.Len(t, results.FileInfos, 1)
  1117  		th.checkFileInfoInSearchResults(t, p4.Id, results.FileInfos)
  1118  	})
  1119  
  1120  	t.Run("Should remove all terms and return empty list", func(t *testing.T) {
  1121  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{{Terms: "is the"}}, th.User.Id, th.Team.Id, 0, 20)
  1122  		require.NoError(t, err)
  1123  		require.Empty(t, results.FileInfos)
  1124  	})
  1125  }
  1126  
  1127  func testFileInfoSupportStemming(t *testing.T, th *SearchTestHelper) {
  1128  	post, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "testmessage", "", model.POST_DEFAULT, 0, false)
  1129  	require.NoError(t, err)
  1130  	defer th.deleteUserPosts(th.User.Id)
  1131  
  1132  	p1, err := th.createFileInfo(th.User.Id, post.Id, "search post", "search post", "jpg", "image/jpeg", 0, 0)
  1133  	require.NoError(t, err)
  1134  	p2, err := th.createFileInfo(th.User.Id, post.Id, "searching post", "searching post", "jpg", "image/jpeg", 0, 0)
  1135  	require.NoError(t, err)
  1136  	_, err = th.createFileInfo(th.User.Id, post.Id, "another post", "another post", "jpg", "image/jpeg", 0, 0)
  1137  	require.NoError(t, err)
  1138  	defer th.deleteUserFileInfos(th.User.Id)
  1139  
  1140  	params := &model.SearchParams{
  1141  		Terms: "search",
  1142  	}
  1143  	results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1144  	require.NoError(t, err)
  1145  
  1146  	require.Len(t, results.FileInfos, 2)
  1147  	th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
  1148  	th.checkFileInfoInSearchResults(t, p2.Id, results.FileInfos)
  1149  }
  1150  
  1151  func testFileInfoSupportWildcards(t *testing.T, th *SearchTestHelper) {
  1152  	post, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "testmessage", "", model.POST_DEFAULT, 0, false)
  1153  	require.NoError(t, err)
  1154  	defer th.deleteUserPosts(th.User.Id)
  1155  
  1156  	p1, err := th.createFileInfo(th.User.Id, post.Id, "search post", "search post", "jpg", "image/jpeg", 0, 0)
  1157  	require.NoError(t, err)
  1158  	p2, err := th.createFileInfo(th.User.Id, post.Id, "searching", "searching", "jpg", "image/jpeg", 0, 0)
  1159  	require.NoError(t, err)
  1160  	_, err = th.createFileInfo(th.User.Id, post.Id, "another post", "another post", "jpg", "image/jpeg", 0, 0)
  1161  	require.NoError(t, err)
  1162  	defer th.deleteUserFileInfos(th.User.Id)
  1163  
  1164  	t.Run("Simple wildcard-only search", func(t *testing.T) {
  1165  		params := &model.SearchParams{
  1166  			Terms: "search*",
  1167  		}
  1168  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1169  		require.NoError(t, err)
  1170  
  1171  		require.Len(t, results.FileInfos, 2)
  1172  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
  1173  		th.checkFileInfoInSearchResults(t, p2.Id, results.FileInfos)
  1174  	})
  1175  
  1176  	t.Run("Wildcard search with another term placed after", func(t *testing.T) {
  1177  		params := &model.SearchParams{
  1178  			Terms: "sear* post",
  1179  		}
  1180  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1181  		require.NoError(t, err)
  1182  
  1183  		require.Len(t, results.FileInfos, 1)
  1184  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
  1185  	})
  1186  }
  1187  
  1188  func testFileInfoNotSupportPrecedingWildcards(t *testing.T, th *SearchTestHelper) {
  1189  	post, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "testmessage", "", model.POST_DEFAULT, 0, false)
  1190  	require.NoError(t, err)
  1191  	defer th.deleteUserPosts(th.User.Id)
  1192  
  1193  	_, err = th.createFileInfo(th.User.Id, post.Id, "search post", "search post", "jpg", "image/jpeg", 0, 0)
  1194  	require.NoError(t, err)
  1195  	_, err = th.createFileInfo(th.User.Id, post.Id, "searching post", "searching post", "jpg", "image/jpeg", 0, 0)
  1196  	require.NoError(t, err)
  1197  	_, err = th.createFileInfo(th.User.Id, post.Id, "another post", "another post", "jpg", "image/jpeg", 0, 0)
  1198  	require.NoError(t, err)
  1199  	defer th.deleteUserFileInfos(th.User.Id)
  1200  
  1201  	params := &model.SearchParams{
  1202  		Terms: "*earch",
  1203  	}
  1204  	results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1205  	require.NoError(t, err)
  1206  
  1207  	require.Len(t, results.FileInfos, 0)
  1208  }
  1209  
  1210  func testFileInfoSearchDiscardWildcardAlone(t *testing.T, th *SearchTestHelper) {
  1211  	post, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "testmessage", "", model.POST_DEFAULT, 0, false)
  1212  	require.NoError(t, err)
  1213  	defer th.deleteUserPosts(th.User.Id)
  1214  
  1215  	p1, err := th.createFileInfo(th.User.Id, post.Id, "qwerty", "qwerty", "jpg", "image/jpeg", 0, 0)
  1216  	require.NoError(t, err)
  1217  	_, err = th.createFileInfo(th.User.Id, post.Id, "qwertyjkl", "qwertyjkl", "jpg", "image/jpeg", 0, 0)
  1218  	require.NoError(t, err)
  1219  	defer th.deleteUserFileInfos(th.User.Id)
  1220  
  1221  	params := &model.SearchParams{
  1222  		Terms: "qwerty *",
  1223  	}
  1224  	results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1225  	require.NoError(t, err)
  1226  
  1227  	require.Len(t, results.FileInfos, 1)
  1228  	th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
  1229  }
  1230  
  1231  func testFileInfoSupportTermsWithDash(t *testing.T, th *SearchTestHelper) {
  1232  	post, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "testmessage", "", model.POST_DEFAULT, 0, false)
  1233  	require.NoError(t, err)
  1234  	defer th.deleteUserPosts(th.User.Id)
  1235  
  1236  	p1, err := th.createFileInfo(th.User.Id, post.Id, "search term-with-dash", "search term-with-dash", "jpg", "image/jpeg", 0, 0)
  1237  	require.NoError(t, err)
  1238  	_, err = th.createFileInfo(th.User.Id, post.Id, "searching term with dash", "searching term with dash", "jpg", "image/jpeg", 0, 0)
  1239  	require.NoError(t, err)
  1240  	defer th.deleteUserFileInfos(th.User.Id)
  1241  
  1242  	t.Run("Should search terms with dash", func(t *testing.T) {
  1243  		params := &model.SearchParams{
  1244  			Terms: "term-with-dash",
  1245  		}
  1246  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1247  		require.NoError(t, err)
  1248  
  1249  		require.Len(t, results.FileInfos, 1)
  1250  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
  1251  	})
  1252  
  1253  	t.Run("Should search terms with dash using quotes", func(t *testing.T) {
  1254  		params := &model.SearchParams{
  1255  			Terms: "\"term-with-dash\"",
  1256  		}
  1257  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1258  		require.NoError(t, err)
  1259  
  1260  		require.Len(t, results.FileInfos, 1)
  1261  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
  1262  	})
  1263  }
  1264  
  1265  func testFileInfoSupportTermsWithUnderscore(t *testing.T, th *SearchTestHelper) {
  1266  	post, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "testmessage", "", model.POST_DEFAULT, 0, false)
  1267  	require.NoError(t, err)
  1268  	defer th.deleteUserPosts(th.User.Id)
  1269  
  1270  	p1, err := th.createFileInfo(th.User.Id, post.Id, "search term_with_underscore", "search term_with_underscore", "jpg", "image/jpeg", 0, 0)
  1271  	require.NoError(t, err)
  1272  	_, err = th.createFileInfo(th.User.Id, post.Id, "searching term with underscore", "searching term with underscore", "jpg", "image/jpeg", 0, 0)
  1273  	require.NoError(t, err)
  1274  	defer th.deleteUserFileInfos(th.User.Id)
  1275  
  1276  	t.Run("Should search terms with underscore", func(t *testing.T) {
  1277  		params := &model.SearchParams{
  1278  			Terms: "term_with_underscore",
  1279  		}
  1280  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1281  		require.NoError(t, err)
  1282  
  1283  		require.Len(t, results.FileInfos, 1)
  1284  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
  1285  	})
  1286  
  1287  	t.Run("Should search terms with underscore using quotes", func(t *testing.T) {
  1288  		params := &model.SearchParams{
  1289  			Terms: "\"term_with_underscore\"",
  1290  		}
  1291  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1292  		require.NoError(t, err)
  1293  
  1294  		require.Len(t, results.FileInfos, 1)
  1295  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
  1296  	})
  1297  }
  1298  
  1299  func testFileInfoSearchInDeletedOrArchivedChannels(t *testing.T, th *SearchTestHelper) {
  1300  	post1, err := th.createPost(th.User.Id, th.ChannelDeleted.Id, "testmessage", "", model.POST_DEFAULT, 0, false)
  1301  	require.NoError(t, err)
  1302  	defer th.deleteUserPosts(th.User.Id)
  1303  	post2, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "testmessage", "", model.POST_DEFAULT, 0, false)
  1304  	require.NoError(t, err)
  1305  	defer th.deleteUserPosts(th.User.Id)
  1306  	post3, err := th.createPost(th.User.Id, th.ChannelPrivate.Id, "testmessage", "", model.POST_DEFAULT, 0, false)
  1307  	require.NoError(t, err)
  1308  	defer th.deleteUserPosts(th.User.Id)
  1309  
  1310  	p1, err := th.createFileInfo(th.User.Id, post1.Id, "message in deleted channel", "message in deleted channel", "jpg", "image/jpeg", 0, 0)
  1311  	require.NoError(t, err)
  1312  	p2, err := th.createFileInfo(th.User.Id, post2.Id, "message in regular channel", "message in regular channel", "jpg", "image/jpeg", 0, 0)
  1313  	require.NoError(t, err)
  1314  	p3, err := th.createFileInfo(th.User.Id, post3.Id, "message in private channel", "message in private channel", "jpg", "image/jpeg", 0, 0)
  1315  	require.NoError(t, err)
  1316  	defer th.deleteUserFileInfos(th.User.Id)
  1317  
  1318  	t.Run("Doesn't include posts in deleted channels", func(t *testing.T) {
  1319  		params := &model.SearchParams{Terms: "message", IncludeDeletedChannels: false}
  1320  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1321  		require.NoError(t, err)
  1322  
  1323  		require.Len(t, results.FileInfos, 2)
  1324  		th.checkFileInfoInSearchResults(t, p2.Id, results.FileInfos)
  1325  		th.checkFileInfoInSearchResults(t, p3.Id, results.FileInfos)
  1326  	})
  1327  
  1328  	t.Run("Include posts in deleted channels", func(t *testing.T) {
  1329  		params := &model.SearchParams{Terms: "message", IncludeDeletedChannels: true}
  1330  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1331  		require.NoError(t, err)
  1332  
  1333  		require.Len(t, results.FileInfos, 3)
  1334  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
  1335  		th.checkFileInfoInSearchResults(t, p2.Id, results.FileInfos)
  1336  		th.checkFileInfoInSearchResults(t, p3.Id, results.FileInfos)
  1337  	})
  1338  
  1339  	t.Run("Include posts in deleted channels using multiple terms", func(t *testing.T) {
  1340  		params := &model.SearchParams{Terms: "message channel", IncludeDeletedChannels: true}
  1341  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1342  		require.NoError(t, err)
  1343  
  1344  		require.Len(t, results.FileInfos, 3)
  1345  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
  1346  		th.checkFileInfoInSearchResults(t, p2.Id, results.FileInfos)
  1347  		th.checkFileInfoInSearchResults(t, p3.Id, results.FileInfos)
  1348  	})
  1349  
  1350  	t.Run("Include posts in deleted channels using multiple OR terms", func(t *testing.T) {
  1351  		params := &model.SearchParams{
  1352  			Terms:                  "message channel",
  1353  			IncludeDeletedChannels: true,
  1354  			OrTerms:                true,
  1355  		}
  1356  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1357  		require.NoError(t, err)
  1358  
  1359  		require.Len(t, results.FileInfos, 3)
  1360  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
  1361  		th.checkFileInfoInSearchResults(t, p2.Id, results.FileInfos)
  1362  		th.checkFileInfoInSearchResults(t, p3.Id, results.FileInfos)
  1363  	})
  1364  
  1365  	t.Run("All IncludeDeletedChannels params should have same value if multiple SearchParams provided", func(t *testing.T) {
  1366  		params1 := &model.SearchParams{
  1367  			Terms:                  "message channel",
  1368  			IncludeDeletedChannels: true,
  1369  		}
  1370  		params2 := &model.SearchParams{
  1371  			Terms:                  "#hashtag",
  1372  			IncludeDeletedChannels: false,
  1373  		}
  1374  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params1, params2}, th.User.Id, th.Team.Id, 0, 20)
  1375  		require.Nil(t, results)
  1376  		require.Error(t, err)
  1377  	})
  1378  }
  1379  
  1380  func testFileInfoSearchTermsWithDashes(t *testing.T, th *SearchTestHelper) {
  1381  	post, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "testmessage", "", model.POST_DEFAULT, 0, false)
  1382  	require.NoError(t, err)
  1383  	defer th.deleteUserPosts(th.User.Id)
  1384  
  1385  	p1, err := th.createFileInfo(th.User.Id, post.Id, "message with-dash-term", "message with-dash-term", "jpg", "image/jpeg", 0, 0)
  1386  	require.NoError(t, err)
  1387  	p2, err := th.createFileInfo(th.User.Id, post.Id, "message with dash term", "message with dash term", "jpg", "image/jpeg", 0, 0)
  1388  	require.NoError(t, err)
  1389  	defer th.deleteUserFileInfos(th.User.Id)
  1390  
  1391  	t.Run("Search for terms with dash", func(t *testing.T) {
  1392  		params := &model.SearchParams{Terms: "with-dash-term"}
  1393  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1394  		require.NoError(t, err)
  1395  
  1396  		require.Len(t, results.FileInfos, 1)
  1397  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
  1398  	})
  1399  
  1400  	t.Run("Search for terms with quoted dash", func(t *testing.T) {
  1401  		params := &model.SearchParams{Terms: "\"with-dash-term\""}
  1402  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1403  		require.NoError(t, err)
  1404  
  1405  		require.Len(t, results.FileInfos, 1)
  1406  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
  1407  	})
  1408  
  1409  	t.Run("Search for multiple terms with one having dash", func(t *testing.T) {
  1410  		params := &model.SearchParams{Terms: "with-dash-term message"}
  1411  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1412  		require.NoError(t, err)
  1413  
  1414  		require.Len(t, results.FileInfos, 1)
  1415  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
  1416  	})
  1417  
  1418  	t.Run("Search for multiple OR terms with one having dash", func(t *testing.T) {
  1419  		params := &model.SearchParams{Terms: "with-dash-term message", OrTerms: true}
  1420  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1421  		require.NoError(t, err)
  1422  
  1423  		require.Len(t, results.FileInfos, 2)
  1424  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
  1425  		th.checkFileInfoInSearchResults(t, p2.Id, results.FileInfos)
  1426  	})
  1427  }
  1428  
  1429  func testFileInfoSearchTermsWithDots(t *testing.T, th *SearchTestHelper) {
  1430  	post, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "testmessage", "", model.POST_DEFAULT, 0, false)
  1431  	require.NoError(t, err)
  1432  	defer th.deleteUserPosts(th.User.Id)
  1433  
  1434  	p1, err := th.createFileInfo(th.User.Id, post.Id, "message with.dots.term", "message with.dots.term", "jpg", "image/jpeg", 0, 0)
  1435  	require.NoError(t, err)
  1436  	p2, err := th.createFileInfo(th.User.Id, post.Id, "message with dots term", "message with dots term", "jpg", "image/jpeg", 0, 0)
  1437  	require.NoError(t, err)
  1438  	defer th.deleteUserFileInfos(th.User.Id)
  1439  
  1440  	t.Run("Search for terms with dots", func(t *testing.T) {
  1441  		params := &model.SearchParams{Terms: "with.dots.term"}
  1442  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1443  		require.NoError(t, err)
  1444  
  1445  		require.Len(t, results.FileInfos, 1)
  1446  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
  1447  	})
  1448  
  1449  	t.Run("Search for terms with quoted dots", func(t *testing.T) {
  1450  		params := &model.SearchParams{Terms: "\"with.dots.term\""}
  1451  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1452  		require.NoError(t, err)
  1453  
  1454  		require.Len(t, results.FileInfos, 1)
  1455  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
  1456  	})
  1457  
  1458  	t.Run("Search for multiple terms with one having dots", func(t *testing.T) {
  1459  		params := &model.SearchParams{Terms: "with.dots.term message"}
  1460  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1461  		require.NoError(t, err)
  1462  
  1463  		require.Len(t, results.FileInfos, 1)
  1464  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
  1465  	})
  1466  
  1467  	t.Run("Search for multiple OR terms with one having dots", func(t *testing.T) {
  1468  		params := &model.SearchParams{Terms: "with.dots.term message", OrTerms: true}
  1469  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1470  		require.NoError(t, err)
  1471  
  1472  		require.Len(t, results.FileInfos, 2)
  1473  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
  1474  		th.checkFileInfoInSearchResults(t, p2.Id, results.FileInfos)
  1475  	})
  1476  }
  1477  
  1478  func testFileInfoSearchTermsWithUnderscores(t *testing.T, th *SearchTestHelper) {
  1479  	post, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "testmessage", "", model.POST_DEFAULT, 0, false)
  1480  	require.NoError(t, err)
  1481  	defer th.deleteUserPosts(th.User.Id)
  1482  
  1483  	p1, err := th.createFileInfo(th.User.Id, post.Id, "message with_underscores_term", "message with_underscores_term", "jpg", "image/jpeg", 0, 0)
  1484  	require.NoError(t, err)
  1485  	p2, err := th.createFileInfo(th.User.Id, post.Id, "message with underscores term", "message with underscores term", "jpg", "image/jpeg", 0, 0)
  1486  	require.NoError(t, err)
  1487  	defer th.deleteUserFileInfos(th.User.Id)
  1488  
  1489  	t.Run("Search for terms with underscores", func(t *testing.T) {
  1490  		params := &model.SearchParams{Terms: "with_underscores_term"}
  1491  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1492  		require.NoError(t, err)
  1493  
  1494  		require.Len(t, results.FileInfos, 1)
  1495  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
  1496  	})
  1497  
  1498  	t.Run("Search for terms with quoted underscores", func(t *testing.T) {
  1499  		params := &model.SearchParams{Terms: "\"with_underscores_term\""}
  1500  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1501  		require.NoError(t, err)
  1502  
  1503  		require.Len(t, results.FileInfos, 1)
  1504  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
  1505  	})
  1506  
  1507  	t.Run("Search for multiple terms with one having underscores", func(t *testing.T) {
  1508  		params := &model.SearchParams{Terms: "with_underscores_term message"}
  1509  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1510  		require.NoError(t, err)
  1511  
  1512  		require.Len(t, results.FileInfos, 1)
  1513  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
  1514  	})
  1515  
  1516  	t.Run("Search for multiple OR terms with one having underscores", func(t *testing.T) {
  1517  		params := &model.SearchParams{Terms: "with_underscores_term message", OrTerms: true}
  1518  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1519  		require.NoError(t, err)
  1520  
  1521  		require.Len(t, results.FileInfos, 2)
  1522  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
  1523  		th.checkFileInfoInSearchResults(t, p2.Id, results.FileInfos)
  1524  	})
  1525  }
  1526  
  1527  func testFileInfoSupportStemmingAndWildcards(t *testing.T, th *SearchTestHelper) {
  1528  	post1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "testmessage", "", model.POST_DEFAULT, 0, false)
  1529  	require.NoError(t, err)
  1530  	defer th.deleteUserPosts(th.User.Id)
  1531  	post2, err := th.createPost(th.User.Id, th.ChannelPrivate.Id, "testmessage", "", model.POST_DEFAULT, 0, false)
  1532  	require.NoError(t, err)
  1533  
  1534  	defer th.deleteUserPosts(th.User.Id)
  1535  	p1, err := th.createFileInfo(th.User.Id, post1.Id, "approve", "approve", "jpg", "image/jpeg", 0, 0)
  1536  	require.NoError(t, err)
  1537  	p2, err := th.createFileInfo(th.User.Id, post2.Id, "approved", "approved", "jpg", "image/jpeg", 0, 0)
  1538  	require.NoError(t, err)
  1539  	p3, err := th.createFileInfo(th.User.Id, post2.Id, "approvedz", "approvedz", "jpg", "image/jpeg", 0, 0)
  1540  	require.NoError(t, err)
  1541  	defer th.deleteUserFileInfos(th.User.Id)
  1542  
  1543  	t.Run("Should stem appr", func(t *testing.T) {
  1544  		params := &model.SearchParams{Terms: "appr*"}
  1545  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1546  		require.NoError(t, err)
  1547  
  1548  		require.Len(t, results.FileInfos, 3)
  1549  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
  1550  		th.checkFileInfoInSearchResults(t, p2.Id, results.FileInfos)
  1551  		th.checkFileInfoInSearchResults(t, p3.Id, results.FileInfos)
  1552  	})
  1553  
  1554  	t.Run("Should stem approve", func(t *testing.T) {
  1555  		params := &model.SearchParams{Terms: "approve*"}
  1556  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1557  		require.NoError(t, err)
  1558  
  1559  		require.Len(t, results.FileInfos, 1)
  1560  		th.checkFileInfoInSearchResults(t, p3.Id, results.FileInfos)
  1561  	})
  1562  }
  1563  
  1564  func testFileInfoSupportWildcardOutsideQuotes(t *testing.T, th *SearchTestHelper) {
  1565  	post1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "testmessage", "", model.POST_DEFAULT, 0, false)
  1566  	require.NoError(t, err)
  1567  	defer th.deleteUserPosts(th.User.Id)
  1568  	post2, err := th.createPost(th.User.Id, th.ChannelPrivate.Id, "testmessage", "", model.POST_DEFAULT, 0, false)
  1569  	require.NoError(t, err)
  1570  
  1571  	p1, err := th.createFileInfo(th.User.Id, post1.Id, "hello world", "hello world", "jpg", "image/jpeg", 0, 0)
  1572  	require.NoError(t, err)
  1573  	p2, err := th.createFileInfo(th.User.Id, post2.Id, "hell or heaven", "hell or heaven", "jpg", "image/jpeg", 0, 0)
  1574  	require.NoError(t, err)
  1575  	defer th.deleteUserFileInfos(th.User.Id)
  1576  
  1577  	t.Run("Should return results without quotes", func(t *testing.T) {
  1578  		params := &model.SearchParams{Terms: "hell*"}
  1579  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1580  		require.NoError(t, err)
  1581  
  1582  		require.Len(t, results.FileInfos, 2)
  1583  		th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
  1584  		th.checkFileInfoInSearchResults(t, p2.Id, results.FileInfos)
  1585  	})
  1586  
  1587  	t.Run("Should return just one result with quotes", func(t *testing.T) {
  1588  		params := &model.SearchParams{Terms: "\"hell\"*"}
  1589  		results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1590  		require.NoError(t, err)
  1591  
  1592  		require.Len(t, results.FileInfos, 1)
  1593  		th.checkFileInfoInSearchResults(t, p2.Id, results.FileInfos)
  1594  	})
  1595  
  1596  }
  1597  
  1598  func testFileInfoSlashShouldNotBeCharSeparator(t *testing.T, th *SearchTestHelper) {
  1599  	post, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "testmessage", "", model.POST_DEFAULT, 0, false)
  1600  	require.NoError(t, err)
  1601  	defer th.deleteUserPosts(th.User.Id)
  1602  
  1603  	p1, err := th.createFileInfo(th.User.Id, post.Id, "alpha/beta gamma, theta", "alpha/beta gamma, theta", "jpg", "image/jpeg", 0, 0)
  1604  	require.NoError(t, err)
  1605  	defer th.deleteUserFileInfos(th.User.Id)
  1606  
  1607  	params := &model.SearchParams{Terms: "gamma"}
  1608  	results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1609  	require.NoError(t, err)
  1610  
  1611  	require.Len(t, results.FileInfos, 1)
  1612  	th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
  1613  
  1614  	params = &model.SearchParams{Terms: "beta"}
  1615  	results, err = th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1616  	require.NoError(t, err)
  1617  
  1618  	require.Len(t, results.FileInfos, 1)
  1619  	th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
  1620  
  1621  	params = &model.SearchParams{Terms: "alpha"}
  1622  	results, err = th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1623  	require.NoError(t, err)
  1624  
  1625  	require.Len(t, results.FileInfos, 1)
  1626  	th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
  1627  }
  1628  
  1629  func testFileInfoSearchEmailsWithoutQuotes(t *testing.T, th *SearchTestHelper) {
  1630  	post, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "testmessage", "", model.POST_DEFAULT, 0, false)
  1631  	require.NoError(t, err)
  1632  	defer th.deleteUserPosts(th.User.Id)
  1633  
  1634  	p1, err := th.createFileInfo(th.User.Id, post.Id, "message test@test.com", "message test@test.com", "jpg", "image/jpeg", 0, 0)
  1635  	require.NoError(t, err)
  1636  	_, err = th.createFileInfo(th.User.Id, post.Id, "message test2@test.com", "message test2@test.com", "jpg", "image/jpeg", 0, 0)
  1637  	require.NoError(t, err)
  1638  	defer th.deleteUserFileInfos(th.User.Id)
  1639  
  1640  	params := &model.SearchParams{Terms: "test@test.com"}
  1641  	results, err := th.Store.FileInfo().Search([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1642  	require.NoError(t, err)
  1643  
  1644  	require.Len(t, results.FileInfos, 1)
  1645  	th.checkFileInfoInSearchResults(t, p1.Id, results.FileInfos)
  1646  }