github.com/masterhung0112/hk_server/v5@v5.0.0-20220302090640-ec71aef15e1c/api4/file_test.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See LICENSE.txt for license information.
     3  
     4  package api4
     5  
     6  import (
     7  	"bytes"
     8  	"crypto/rand"
     9  	"fmt"
    10  	"io"
    11  	"io/ioutil"
    12  	"mime/multipart"
    13  	"net/http"
    14  	"net/textproto"
    15  	"net/url"
    16  	"os"
    17  	"path/filepath"
    18  	"strings"
    19  	"testing"
    20  	"time"
    21  
    22  	"github.com/stretchr/testify/assert"
    23  	"github.com/stretchr/testify/require"
    24  
    25  	"github.com/masterhung0112/hk_server/v5/app"
    26  	"github.com/masterhung0112/hk_server/v5/model"
    27  	"github.com/masterhung0112/hk_server/v5/utils/fileutils"
    28  	"github.com/masterhung0112/hk_server/v5/utils/testutils"
    29  )
    30  
    31  var testDir = ""
    32  
    33  func init() {
    34  	testDir, _ = fileutils.FindDir("tests")
    35  }
    36  
    37  // File Section
    38  var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"")
    39  
    40  func escapeQuotes(s string) string {
    41  	return quoteEscaper.Replace(s)
    42  }
    43  
    44  func randomBytes(t *testing.T, n int) []byte {
    45  	bb := make([]byte, n)
    46  	_, err := rand.Read(bb)
    47  	require.NoError(t, err)
    48  	return bb
    49  }
    50  
    51  func fileBytes(t *testing.T, path string) []byte {
    52  	path = filepath.Join(testDir, path)
    53  	f, err := os.Open(path)
    54  	require.NoError(t, err)
    55  	defer f.Close()
    56  	bb, err := ioutil.ReadAll(f)
    57  	require.NoError(t, err)
    58  	return bb
    59  }
    60  
    61  func testDoUploadFileRequest(t testing.TB, c *model.Client4, url string, blob []byte, contentType string,
    62  	contentLength int64) (*model.FileUploadResponse, *model.Response) {
    63  	req, err := http.NewRequest("POST", c.ApiUrl+c.GetFilesRoute()+url, bytes.NewReader(blob))
    64  	require.NoError(t, err)
    65  
    66  	if contentLength != 0 {
    67  		req.ContentLength = contentLength
    68  	}
    69  	req.Header.Set("Content-Type", contentType)
    70  	if c.AuthToken != "" {
    71  		req.Header.Set(model.HEADER_AUTH, c.AuthType+" "+c.AuthToken)
    72  	}
    73  
    74  	resp, err := c.HttpClient.Do(req)
    75  	require.NoError(t, err)
    76  	require.NotNil(t, resp)
    77  	defer closeBody(resp)
    78  
    79  	if resp.StatusCode >= 300 {
    80  		return nil, model.BuildErrorResponse(resp, model.AppErrorFromJson(resp.Body))
    81  	}
    82  
    83  	return model.FileUploadResponseFromJson(resp.Body), model.BuildResponse(resp)
    84  }
    85  
    86  func testUploadFilesPost(
    87  	t testing.TB,
    88  	c *model.Client4,
    89  	channelId string,
    90  	names []string,
    91  	blobs [][]byte,
    92  	clientIds []string,
    93  	useChunked bool,
    94  ) (*model.FileUploadResponse, *model.Response) {
    95  
    96  	// Do not check len(clientIds), leave it entirely to the user to
    97  	// provide. The server will error out if it does not match the number
    98  	// of files, but it's not critical here.
    99  	require.NotEmpty(t, names)
   100  	require.NotEmpty(t, blobs)
   101  	require.Equal(t, len(names), len(blobs))
   102  
   103  	fileUploadResponse := &model.FileUploadResponse{}
   104  	for i, blob := range blobs {
   105  		var cl int64
   106  		if useChunked {
   107  			cl = -1
   108  		} else {
   109  			cl = int64(len(blob))
   110  		}
   111  		ct := http.DetectContentType(blob)
   112  
   113  		postURL := fmt.Sprintf("?channel_id=%v", url.QueryEscape(channelId)) +
   114  			fmt.Sprintf("&filename=%v", url.QueryEscape(names[i]))
   115  		if len(clientIds) > i {
   116  			postURL += fmt.Sprintf("&client_id=%v", url.QueryEscape(clientIds[i]))
   117  		}
   118  
   119  		fur, resp := testDoUploadFileRequest(t, c, postURL, blob, ct, cl)
   120  		if resp.Error != nil {
   121  			return nil, resp
   122  		}
   123  
   124  		fileUploadResponse.FileInfos = append(fileUploadResponse.FileInfos, fur.FileInfos[0])
   125  		if len(clientIds) > 0 {
   126  			if len(fur.ClientIds) > 0 {
   127  				fileUploadResponse.ClientIds = append(fileUploadResponse.ClientIds, fur.ClientIds[0])
   128  			} else {
   129  				fileUploadResponse.ClientIds = append(fileUploadResponse.ClientIds, "")
   130  			}
   131  		}
   132  	}
   133  
   134  	return fileUploadResponse, nil
   135  }
   136  
   137  func testUploadFilesMultipart(
   138  	t testing.TB,
   139  	c *model.Client4,
   140  	channelId string,
   141  	names []string,
   142  	blobs [][]byte,
   143  	clientIds []string,
   144  ) (
   145  	fileUploadResponse *model.FileUploadResponse,
   146  	response *model.Response,
   147  ) {
   148  	// Do not check len(clientIds), leave it entirely to the user to
   149  	// provide. The server will error out if it does not match the number
   150  	// of files, but it's not critical here.
   151  	require.NotEmpty(t, names)
   152  	require.NotEmpty(t, blobs)
   153  	require.Equal(t, len(names), len(blobs))
   154  
   155  	mwBody := &bytes.Buffer{}
   156  	mw := multipart.NewWriter(mwBody)
   157  
   158  	err := mw.WriteField("channel_id", channelId)
   159  	require.NoError(t, err)
   160  
   161  	for i, blob := range blobs {
   162  		ct := http.DetectContentType(blob)
   163  		if len(clientIds) > i {
   164  			err = mw.WriteField("client_ids", clientIds[i])
   165  			require.NoError(t, err)
   166  		}
   167  
   168  		h := textproto.MIMEHeader{}
   169  		h.Set("Content-Disposition",
   170  			fmt.Sprintf(`form-data; name="files"; filename="%s"`, escapeQuotes(names[i])))
   171  		h.Set("Content-Type", ct)
   172  
   173  		// If we error here, writing to mw, the deferred handler
   174  		part, err := mw.CreatePart(h)
   175  		require.NoError(t, err)
   176  
   177  		_, err = io.Copy(part, bytes.NewReader(blob))
   178  		require.NoError(t, err)
   179  	}
   180  
   181  	require.NoError(t, mw.Close())
   182  	return testDoUploadFileRequest(t, c, "", mwBody.Bytes(), mw.FormDataContentType(), -1)
   183  }
   184  
   185  func TestUploadFiles(t *testing.T) {
   186  	th := Setup(t).InitBasic()
   187  	defer th.TearDown()
   188  	if *th.App.Config().FileSettings.DriverName == "" {
   189  		t.Skip("skipping because no file driver is enabled")
   190  	}
   191  
   192  	channel := th.BasicChannel
   193  	date := time.Now().Format("20060102")
   194  
   195  	// Get better error messages
   196  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableDeveloper = true })
   197  
   198  	tests := []struct {
   199  		title     string
   200  		client    *model.Client4
   201  		blobs     [][]byte
   202  		names     []string
   203  		clientIds []string
   204  
   205  		skipSuccessValidation       bool
   206  		skipPayloadValidation       bool
   207  		skipSimplePost              bool
   208  		skipMultipart               bool
   209  		channelId                   string
   210  		useChunkedInSimplePost      bool
   211  		expectedCreatorId           string
   212  		expectedPayloadNames        []string
   213  		expectImage                 bool
   214  		expectedImageWidths         []int
   215  		expectedImageHeights        []int
   216  		expectedImageThumbnailNames []string
   217  		expectedImagePreviewNames   []string
   218  		expectedImageHasPreview     []bool
   219  		expectedImageMiniPreview    []bool
   220  		setupConfig                 func(a *app.App) func(a *app.App)
   221  		checkResponse               func(t *testing.T, resp *model.Response)
   222  	}{
   223  		// Upload a bunch of files, mixed images and non-images
   224  		{
   225  			title:                    "Happy",
   226  			names:                    []string{"test.png", "testgif.gif", "testplugin.tar.gz", "test-search.md", "test.tiff"},
   227  			expectedCreatorId:        th.BasicUser.Id,
   228  			expectedImageMiniPreview: []bool{true, true, false, false, true},
   229  		},
   230  		// Upload a bunch of files, with clientIds
   231  		{
   232  			title:                    "Happy client_ids",
   233  			names:                    []string{"test.png", "testgif.gif", "testplugin.tar.gz", "test-search.md", "test.tiff"},
   234  			clientIds:                []string{"1", "2", "3", "4", "5"},
   235  			expectedImageMiniPreview: []bool{true, true, false, false, true},
   236  			expectedCreatorId:        th.BasicUser.Id,
   237  		},
   238  		// Upload a bunch of images. testgif.gif is an animated GIF,
   239  		// so it does not have HasPreviewImage set.
   240  		{
   241  			title:                    "Happy images",
   242  			names:                    []string{"test.png", "testgif.gif"},
   243  			expectImage:              true,
   244  			expectedCreatorId:        th.BasicUser.Id,
   245  			expectedImageWidths:      []int{408, 118},
   246  			expectedImageHeights:     []int{336, 118},
   247  			expectedImageHasPreview:  []bool{true, false},
   248  			expectedImageMiniPreview: []bool{true, true},
   249  		},
   250  		{
   251  			title:                    "Happy invalid image",
   252  			names:                    []string{"testgif.gif"},
   253  			blobs:                    [][]byte{fileBytes(t, "test-search.md")},
   254  			skipPayloadValidation:    true,
   255  			expectedCreatorId:        th.BasicUser.Id,
   256  			expectedImageMiniPreview: []bool{false},
   257  		},
   258  		// Simple POST, chunked encoding
   259  		{
   260  			title:                    "Happy image chunked post",
   261  			skipMultipart:            true,
   262  			useChunkedInSimplePost:   true,
   263  			names:                    []string{"test.png"},
   264  			expectImage:              true,
   265  			expectedImageWidths:      []int{408},
   266  			expectedImageHeights:     []int{336},
   267  			expectedImageHasPreview:  []bool{true},
   268  			expectedCreatorId:        th.BasicUser.Id,
   269  			expectedImageMiniPreview: []bool{true},
   270  		},
   271  		// Image thumbnail and preview: size and orientation. Note that
   272  		// the expected image dimensions remain the same regardless of the
   273  		// orientation - what we save in FileInfo is used by the
   274  		// clients to size UI elements, so the dimensions are "actual".
   275  		{
   276  			title:                       "Happy image thumbnail/preview 1",
   277  			names:                       []string{"orientation_test_1.jpeg"},
   278  			expectedImageThumbnailNames: []string{"orientation_test_1_expected_thumb.jpeg"},
   279  			expectedImagePreviewNames:   []string{"orientation_test_1_expected_preview.jpeg"},
   280  			expectImage:                 true,
   281  			expectedImageWidths:         []int{2860},
   282  			expectedImageHeights:        []int{1578},
   283  			expectedImageHasPreview:     []bool{true},
   284  			expectedCreatorId:           th.BasicUser.Id,
   285  			expectedImageMiniPreview:    []bool{true},
   286  		},
   287  		{
   288  			title:                       "Happy image thumbnail/preview 2",
   289  			names:                       []string{"orientation_test_2.jpeg"},
   290  			expectedImageThumbnailNames: []string{"orientation_test_2_expected_thumb.jpeg"},
   291  			expectedImagePreviewNames:   []string{"orientation_test_2_expected_preview.jpeg"},
   292  			expectImage:                 true,
   293  			expectedImageWidths:         []int{2860},
   294  			expectedImageHeights:        []int{1578},
   295  			expectedImageHasPreview:     []bool{true},
   296  			expectedImageMiniPreview:    []bool{true},
   297  			expectedCreatorId:           th.BasicUser.Id,
   298  		},
   299  		{
   300  			title:                       "Happy image thumbnail/preview 3",
   301  			names:                       []string{"orientation_test_3.jpeg"},
   302  			expectedImageThumbnailNames: []string{"orientation_test_3_expected_thumb.jpeg"},
   303  			expectedImagePreviewNames:   []string{"orientation_test_3_expected_preview.jpeg"},
   304  			expectImage:                 true,
   305  			expectedImageWidths:         []int{2860},
   306  			expectedImageHeights:        []int{1578},
   307  			expectedImageHasPreview:     []bool{true},
   308  			expectedImageMiniPreview:    []bool{true},
   309  			expectedCreatorId:           th.BasicUser.Id,
   310  		},
   311  		{
   312  			title:                       "Happy image thumbnail/preview 4",
   313  			names:                       []string{"orientation_test_4.jpeg"},
   314  			expectedImageThumbnailNames: []string{"orientation_test_4_expected_thumb.jpeg"},
   315  			expectedImagePreviewNames:   []string{"orientation_test_4_expected_preview.jpeg"},
   316  			expectImage:                 true,
   317  			expectedImageWidths:         []int{2860},
   318  			expectedImageHeights:        []int{1578},
   319  			expectedImageHasPreview:     []bool{true},
   320  			expectedImageMiniPreview:    []bool{true},
   321  			expectedCreatorId:           th.BasicUser.Id,
   322  		},
   323  		{
   324  			title:                       "Happy image thumbnail/preview 5",
   325  			names:                       []string{"orientation_test_5.jpeg"},
   326  			expectedImageThumbnailNames: []string{"orientation_test_5_expected_thumb.jpeg"},
   327  			expectedImagePreviewNames:   []string{"orientation_test_5_expected_preview.jpeg"},
   328  			expectImage:                 true,
   329  			expectedImageWidths:         []int{2860},
   330  			expectedImageHeights:        []int{1578},
   331  			expectedImageHasPreview:     []bool{true},
   332  			expectedImageMiniPreview:    []bool{true},
   333  			expectedCreatorId:           th.BasicUser.Id,
   334  		},
   335  		{
   336  			title:                       "Happy image thumbnail/preview 6",
   337  			names:                       []string{"orientation_test_6.jpeg"},
   338  			expectedImageThumbnailNames: []string{"orientation_test_6_expected_thumb.jpeg"},
   339  			expectedImagePreviewNames:   []string{"orientation_test_6_expected_preview.jpeg"},
   340  			expectImage:                 true,
   341  			expectedImageWidths:         []int{2860},
   342  			expectedImageHeights:        []int{1578},
   343  			expectedImageHasPreview:     []bool{true},
   344  			expectedImageMiniPreview:    []bool{true},
   345  			expectedCreatorId:           th.BasicUser.Id,
   346  		},
   347  		{
   348  			title:                       "Happy image thumbnail/preview 7",
   349  			names:                       []string{"orientation_test_7.jpeg"},
   350  			expectedImageThumbnailNames: []string{"orientation_test_7_expected_thumb.jpeg"},
   351  			expectedImagePreviewNames:   []string{"orientation_test_7_expected_preview.jpeg"},
   352  			expectImage:                 true,
   353  			expectedImageWidths:         []int{2860},
   354  			expectedImageHeights:        []int{1578},
   355  			expectedImageHasPreview:     []bool{true},
   356  			expectedImageMiniPreview:    []bool{true},
   357  			expectedCreatorId:           th.BasicUser.Id,
   358  		},
   359  		{
   360  			title:                       "Happy image thumbnail/preview 8",
   361  			names:                       []string{"orientation_test_8.jpeg"},
   362  			expectedImageThumbnailNames: []string{"orientation_test_8_expected_thumb.jpeg"},
   363  			expectedImagePreviewNames:   []string{"orientation_test_8_expected_preview.jpeg"},
   364  			expectImage:                 true,
   365  			expectedImageWidths:         []int{2860},
   366  			expectedImageHeights:        []int{1578},
   367  			expectedImageHasPreview:     []bool{true},
   368  			expectedImageMiniPreview:    []bool{true},
   369  			expectedCreatorId:           th.BasicUser.Id,
   370  		},
   371  		// TIFF preview test
   372  		{
   373  			title:                       "Happy image thumbnail/preview 9",
   374  			names:                       []string{"test.tiff"},
   375  			expectedImageThumbnailNames: []string{"test_expected_tiff_thumb.jpeg"},
   376  			expectedImagePreviewNames:   []string{"test_expected_tiff_preview.jpeg"},
   377  			expectImage:                 true,
   378  			expectedImageWidths:         []int{701},
   379  			expectedImageHeights:        []int{701},
   380  			expectedImageHasPreview:     []bool{true},
   381  			expectedImageMiniPreview:    []bool{true},
   382  			expectedCreatorId:           th.BasicUser.Id,
   383  		},
   384  		// Extremely wide image test
   385  		{
   386  			title:                       "Happy image thumbnail/preview 10",
   387  			names:                       []string{"10000x1.png"},
   388  			expectedImageThumbnailNames: []string{"10000x1_expected_thumb.jpeg"},
   389  			expectedImagePreviewNames:   []string{"10000x1_expected_preview.jpeg"},
   390  			expectImage:                 true,
   391  			expectedImageWidths:         []int{10000},
   392  			expectedImageHeights:        []int{1},
   393  			expectedImageHasPreview:     []bool{true},
   394  			expectedCreatorId:           th.BasicUser.Id,
   395  		},
   396  		// Extremely high image test
   397  		{
   398  			title:                       "Happy image thumbnail/preview 11",
   399  			names:                       []string{"1x10000.png"},
   400  			expectedImageThumbnailNames: []string{"1x10000_expected_thumb.jpeg"},
   401  			expectedImagePreviewNames:   []string{"1x10000_expected_preview.jpeg"},
   402  			expectImage:                 true,
   403  			expectedImageWidths:         []int{1},
   404  			expectedImageHeights:        []int{10000},
   405  			expectedImageHasPreview:     []bool{true},
   406  			expectedCreatorId:           th.BasicUser.Id,
   407  		},
   408  		// animated GIF
   409  		{
   410  			title:                       "Happy image thumbnail/preview 12",
   411  			names:                       []string{"testgif.gif"},
   412  			expectedImageThumbnailNames: []string{"testgif_expected_thumbnail.jpg"},
   413  			expectedImagePreviewNames:   []string{"testgif_expected_preview.jpg"},
   414  			expectImage:                 true,
   415  			expectedImageWidths:         []int{118},
   416  			expectedImageHeights:        []int{118},
   417  			expectedImageHasPreview:     []bool{false},
   418  			expectedCreatorId:           th.BasicUser.Id,
   419  		},
   420  		{
   421  			title:                    "Happy admin",
   422  			client:                   th.SystemAdminClient,
   423  			names:                    []string{"test.png"},
   424  			expectedImageMiniPreview: []bool{true},
   425  			expectedCreatorId:        th.SystemAdminUser.Id,
   426  		},
   427  		{
   428  			title:                  "Happy stream",
   429  			useChunkedInSimplePost: true,
   430  			skipPayloadValidation:  true,
   431  			names:                  []string{"1Mb-stream"},
   432  			blobs:                  [][]byte{randomBytes(t, 1024*1024)},
   433  			setupConfig: func(a *app.App) func(a *app.App) {
   434  				maxFileSize := *a.Config().FileSettings.MaxFileSize
   435  				a.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.MaxFileSize = 1024 * 1024 })
   436  				return func(a *app.App) {
   437  					a.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.MaxFileSize = maxFileSize })
   438  				}
   439  			},
   440  			expectedImageMiniPreview: []bool{false},
   441  			expectedCreatorId:        th.BasicUser.Id,
   442  		},
   443  		// Error cases
   444  		{
   445  			title:                 "Error channel_id does not exist",
   446  			channelId:             model.NewId(),
   447  			names:                 []string{"test.png"},
   448  			skipSuccessValidation: true,
   449  			checkResponse:         CheckForbiddenStatus,
   450  		},
   451  		{
   452  			// on simple post this uploads the last file
   453  			// successfully, without a ClientId
   454  			title:                    "Error too few client_ids",
   455  			skipSimplePost:           true,
   456  			names:                    []string{"test.png", "testplugin.tar.gz", "test-search.md"},
   457  			clientIds:                []string{"1", "4"},
   458  			expectedImageMiniPreview: []bool{true, false, false},
   459  			skipSuccessValidation:    true,
   460  			checkResponse:            CheckBadRequestStatus,
   461  		},
   462  		{
   463  			title:                    "Error invalid channel_id",
   464  			channelId:                "../../junk",
   465  			names:                    []string{"test.png"},
   466  			expectedImageMiniPreview: []bool{true},
   467  			skipSuccessValidation:    true,
   468  			checkResponse:            CheckBadRequestStatus,
   469  		},
   470  		{
   471  			title:                 "Error admin channel_id does not exist",
   472  			client:                th.SystemAdminClient,
   473  			channelId:             model.NewId(),
   474  			names:                 []string{"test.png"},
   475  			skipSuccessValidation: true,
   476  			checkResponse:         CheckForbiddenStatus,
   477  		},
   478  		{
   479  			title:                 "Error admin invalid channel_id",
   480  			client:                th.SystemAdminClient,
   481  			channelId:             "../../junk",
   482  			names:                 []string{"test.png"},
   483  			skipSuccessValidation: true,
   484  			checkResponse:         CheckBadRequestStatus,
   485  		},
   486  		{
   487  			title:                 "Error admin disabled uploads",
   488  			client:                th.SystemAdminClient,
   489  			names:                 []string{"test.png"},
   490  			skipSuccessValidation: true,
   491  			checkResponse:         CheckNotImplementedStatus,
   492  			setupConfig: func(a *app.App) func(a *app.App) {
   493  				enableFileAttachments := *a.Config().FileSettings.EnableFileAttachments
   494  				a.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.EnableFileAttachments = false })
   495  				return func(a *app.App) {
   496  					a.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.EnableFileAttachments = enableFileAttachments })
   497  				}
   498  			},
   499  		},
   500  		{
   501  			title:                 "Error file too large",
   502  			names:                 []string{"test.png"},
   503  			skipSuccessValidation: true,
   504  			checkResponse:         CheckRequestEntityTooLargeStatus,
   505  			setupConfig: func(a *app.App) func(a *app.App) {
   506  				maxFileSize := *a.Config().FileSettings.MaxFileSize
   507  				a.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.MaxFileSize = 279590 })
   508  				return func(a *app.App) {
   509  					a.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.MaxFileSize = maxFileSize })
   510  				}
   511  			},
   512  		},
   513  		// File too large (chunked, simple POST only, multipart would've been redundant with above)
   514  		{
   515  			title:                    "File too large chunked",
   516  			useChunkedInSimplePost:   true,
   517  			skipMultipart:            true,
   518  			names:                    []string{"test.png"},
   519  			skipSuccessValidation:    true,
   520  			checkResponse:            CheckRequestEntityTooLargeStatus,
   521  			expectedImageMiniPreview: []bool{false},
   522  			setupConfig: func(a *app.App) func(a *app.App) {
   523  				maxFileSize := *a.Config().FileSettings.MaxFileSize
   524  				a.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.MaxFileSize = 279590 })
   525  				return func(a *app.App) {
   526  					a.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.MaxFileSize = maxFileSize })
   527  				}
   528  			},
   529  		},
   530  		{
   531  			title:                 "Error stream too large",
   532  			skipPayloadValidation: true,
   533  			names:                 []string{"1Mb-stream"},
   534  			blobs:                 [][]byte{randomBytes(t, 1024*1024)},
   535  			skipSuccessValidation: true,
   536  			checkResponse:         CheckRequestEntityTooLargeStatus,
   537  			setupConfig: func(a *app.App) func(a *app.App) {
   538  				maxFileSize := *a.Config().FileSettings.MaxFileSize
   539  				a.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.MaxFileSize = 10 * 1024 })
   540  				return func(a *app.App) {
   541  					a.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.MaxFileSize = maxFileSize })
   542  				}
   543  			},
   544  		},
   545  	}
   546  
   547  	for _, useMultipart := range []bool{true, false} {
   548  		for _, tc := range tests {
   549  			if tc.skipMultipart && useMultipart || tc.skipSimplePost && !useMultipart {
   550  				continue
   551  			}
   552  
   553  			title := ""
   554  			if useMultipart {
   555  				title = "multipart "
   556  			} else {
   557  				title = "simple "
   558  			}
   559  			if tc.title != "" {
   560  				title += tc.title + " "
   561  			}
   562  			title += fmt.Sprintf("%v", tc.names)
   563  
   564  			t.Run(title, func(t *testing.T) {
   565  				// Apply any necessary config changes
   566  				if tc.setupConfig != nil {
   567  					restoreConfig := tc.setupConfig(th.App)
   568  					if restoreConfig != nil {
   569  						defer restoreConfig(th.App)
   570  					}
   571  				}
   572  
   573  				// Set the default values
   574  				client := th.Client
   575  				if tc.client != nil {
   576  					client = tc.client
   577  				}
   578  				channelId := channel.Id
   579  				if tc.channelId != "" {
   580  					channelId = tc.channelId
   581  				}
   582  
   583  				blobs := tc.blobs
   584  				if len(blobs) == 0 {
   585  					for _, name := range tc.names {
   586  						blobs = append(blobs, fileBytes(t, name))
   587  					}
   588  				}
   589  
   590  				var fileResp *model.FileUploadResponse
   591  				var resp *model.Response
   592  				if useMultipart {
   593  					fileResp, resp = testUploadFilesMultipart(t, client, channelId, tc.names, blobs, tc.clientIds)
   594  				} else {
   595  					fileResp, resp = testUploadFilesPost(t, client, channelId, tc.names, blobs, tc.clientIds, tc.useChunkedInSimplePost)
   596  				}
   597  
   598  				if tc.checkResponse != nil {
   599  					tc.checkResponse(t, resp)
   600  				} else {
   601  					if resp != nil {
   602  						require.Nil(t, resp.Error)
   603  					}
   604  				}
   605  				if tc.skipSuccessValidation {
   606  					return
   607  				}
   608  
   609  				require.NotNil(t, fileResp, "Nil fileResp")
   610  				require.NotEqual(t, 0, len(fileResp.FileInfos), "Empty FileInfos")
   611  				require.Equal(t, len(tc.names), len(fileResp.FileInfos), "Mismatched actual or expected FileInfos")
   612  
   613  				for i, ri := range fileResp.FileInfos {
   614  					// The returned file info from the upload call will be missing some fields that will be stored in the database
   615  					assert.Equal(t, ri.CreatorId, tc.expectedCreatorId, "File should be assigned to user")
   616  					assert.Equal(t, ri.PostId, "", "File shouldn't have a post Id")
   617  					assert.Equal(t, ri.Path, "", "File path should not be set on returned info")
   618  					assert.Equal(t, ri.ThumbnailPath, "", "File thumbnail path should not be set on returned info")
   619  					assert.Equal(t, ri.PreviewPath, "", "File preview path should not be set on returned info")
   620  					if len(tc.expectedImageMiniPreview) == len(fileResp.FileInfos) {
   621  						assert.Equal(t, ri.MiniPreview != nil, tc.expectedImageMiniPreview[i], "File: %s mini preview state unexpected", tc.names[i])
   622  					}
   623  					if len(tc.clientIds) > i {
   624  						assert.True(t, len(fileResp.ClientIds) == len(tc.clientIds),
   625  							fmt.Sprintf("Wrong number of clientIds returned, expected %v, got %v", len(tc.clientIds), len(fileResp.ClientIds)))
   626  						assert.Equal(t, fileResp.ClientIds[i], tc.clientIds[i],
   627  							fmt.Sprintf("Wrong clientId returned, expected %v, got %v", tc.clientIds[i], fileResp.ClientIds[i]))
   628  					}
   629  
   630  					dbInfo, err := th.App.Srv().Store.FileInfo().Get(ri.Id)
   631  					require.NoError(t, err)
   632  					assert.Equal(t, dbInfo.Id, ri.Id, "File id from response should match one stored in database")
   633  					assert.Equal(t, dbInfo.CreatorId, tc.expectedCreatorId, "F ile should be assigned to user")
   634  					assert.Equal(t, dbInfo.PostId, "", "File shouldn't have a post")
   635  					assert.NotEqual(t, dbInfo.Path, "", "File path should be set in database")
   636  					_, fname := filepath.Split(dbInfo.Path)
   637  					ext := filepath.Ext(fname)
   638  					name := fname[:len(fname)-len(ext)]
   639  					expectedDir := fmt.Sprintf("%v/teams/%v/channels/%v/users/%s/%s", date, FileTeamId, channel.Id, ri.CreatorId, ri.Id)
   640  					expectedPath := fmt.Sprintf("%s/%s", expectedDir, fname)
   641  					assert.Equal(t, dbInfo.Path, expectedPath,
   642  						fmt.Sprintf("File %v saved to:%q, expected:%q", dbInfo.Name, dbInfo.Path, expectedPath))
   643  
   644  					if tc.expectImage {
   645  						expectedThumbnailPath := fmt.Sprintf("%s/%s_thumb.jpg", expectedDir, name)
   646  						expectedPreviewPath := fmt.Sprintf("%s/%s_preview.jpg", expectedDir, name)
   647  						assert.Equal(t, dbInfo.ThumbnailPath, expectedThumbnailPath,
   648  							fmt.Sprintf("Thumbnail for %v saved to:%q, expected:%q", dbInfo.Name, dbInfo.ThumbnailPath, expectedThumbnailPath))
   649  						assert.Equal(t, dbInfo.PreviewPath, expectedPreviewPath,
   650  							fmt.Sprintf("Preview for %v saved to:%q, expected:%q", dbInfo.Name, dbInfo.PreviewPath, expectedPreviewPath))
   651  
   652  						assert.True(t,
   653  							dbInfo.HasPreviewImage == tc.expectedImageHasPreview[i],
   654  							fmt.Sprintf("Image: HasPreviewImage should be set for %s", dbInfo.Name))
   655  						assert.True(t,
   656  							dbInfo.Width == tc.expectedImageWidths[i] && dbInfo.Height == tc.expectedImageHeights[i],
   657  							fmt.Sprintf("Image dimensions: expected %dwx%dh, got %vwx%dh",
   658  								tc.expectedImageWidths[i], tc.expectedImageHeights[i],
   659  								dbInfo.Width, dbInfo.Height))
   660  					}
   661  
   662  					if !tc.skipPayloadValidation {
   663  						compare := func(get func(string) ([]byte, *model.Response), name string) {
   664  							data, resp := get(ri.Id)
   665  							require.NotNil(t, resp)
   666  							require.Nil(t, resp.Error)
   667  
   668  							expected, err := ioutil.ReadFile(filepath.Join(testDir, name))
   669  							require.NoError(t, err)
   670  							if !bytes.Equal(data, expected) {
   671  								tf, err := ioutil.TempFile("", fmt.Sprintf("test_%v_*_%s", i, name))
   672  								defer tf.Close()
   673  								require.NoError(t, err)
   674  								_, err = io.Copy(tf, bytes.NewReader(data))
   675  								require.NoError(t, err)
   676  								t.Errorf("Actual data mismatched %s, written to %q - expected %d bytes, got %d.", name, tf.Name(), len(expected), len(data))
   677  							}
   678  						}
   679  						if len(tc.expectedPayloadNames) == 0 {
   680  							tc.expectedPayloadNames = tc.names
   681  						}
   682  
   683  						compare(client.GetFile, tc.expectedPayloadNames[i])
   684  						if len(tc.expectedImageThumbnailNames) > i {
   685  							compare(client.GetFileThumbnail, tc.expectedImageThumbnailNames[i])
   686  						}
   687  						if len(tc.expectedImageThumbnailNames) > i {
   688  							compare(client.GetFilePreview, tc.expectedImagePreviewNames[i])
   689  						}
   690  					}
   691  
   692  					th.cleanupTestFile(dbInfo)
   693  				}
   694  			})
   695  
   696  		}
   697  	}
   698  }
   699  
   700  func TestGetFile(t *testing.T) {
   701  	th := Setup(t).InitBasic()
   702  	defer th.TearDown()
   703  	Client := th.Client
   704  	channel := th.BasicChannel
   705  
   706  	if *th.App.Config().FileSettings.DriverName == "" {
   707  		t.Skip("skipping because no file driver is enabled")
   708  	}
   709  
   710  	sent, err := testutils.ReadTestFile("test.png")
   711  	require.NoError(t, err)
   712  
   713  	fileResp, resp := Client.UploadFile(sent, channel.Id, "test.png")
   714  	CheckNoError(t, resp)
   715  
   716  	fileId := fileResp.FileInfos[0].Id
   717  
   718  	data, resp := Client.GetFile(fileId)
   719  	CheckNoError(t, resp)
   720  	require.NotEqual(t, 0, len(data), "should not be empty")
   721  
   722  	for i := range data {
   723  		require.Equal(t, sent[i], data[i], "received file didn't match sent one")
   724  	}
   725  
   726  	_, resp = Client.GetFile("junk")
   727  	CheckBadRequestStatus(t, resp)
   728  
   729  	_, resp = Client.GetFile(model.NewId())
   730  	CheckNotFoundStatus(t, resp)
   731  
   732  	Client.Logout()
   733  	_, resp = Client.GetFile(fileId)
   734  	CheckUnauthorizedStatus(t, resp)
   735  
   736  	_, resp = th.SystemAdminClient.GetFile(fileId)
   737  	CheckNoError(t, resp)
   738  }
   739  
   740  func TestGetFileHeaders(t *testing.T) {
   741  	th := Setup(t).InitBasic()
   742  	defer th.TearDown()
   743  
   744  	Client := th.Client
   745  	channel := th.BasicChannel
   746  
   747  	if *th.App.Config().FileSettings.DriverName == "" {
   748  		t.Skip("skipping because no file driver is enabled")
   749  	}
   750  
   751  	testHeaders := func(data []byte, filename string, expectedContentType string, getInline bool) func(*testing.T) {
   752  		return func(t *testing.T) {
   753  			fileResp, resp := Client.UploadFile(data, channel.Id, filename)
   754  			CheckNoError(t, resp)
   755  
   756  			fileId := fileResp.FileInfos[0].Id
   757  
   758  			_, resp = Client.GetFile(fileId)
   759  			CheckNoError(t, resp)
   760  
   761  			CheckStartsWith(t, resp.Header.Get("Content-Type"), expectedContentType, "returned incorrect Content-Type")
   762  
   763  			if getInline {
   764  				CheckStartsWith(t, resp.Header.Get("Content-Disposition"), "inline", "returned incorrect Content-Disposition")
   765  			} else {
   766  				CheckStartsWith(t, resp.Header.Get("Content-Disposition"), "attachment", "returned incorrect Content-Disposition")
   767  			}
   768  
   769  			_, resp = Client.DownloadFile(fileId, true)
   770  			CheckNoError(t, resp)
   771  
   772  			CheckStartsWith(t, resp.Header.Get("Content-Type"), expectedContentType, "returned incorrect Content-Type")
   773  			CheckStartsWith(t, resp.Header.Get("Content-Disposition"), "attachment", "returned incorrect Content-Disposition")
   774  		}
   775  	}
   776  
   777  	data := []byte("ABC")
   778  
   779  	t.Run("png", testHeaders(data, "test.png", "image/png", true))
   780  	t.Run("gif", testHeaders(data, "test.gif", "image/gif", true))
   781  	t.Run("mp4", testHeaders(data, "test.mp4", "video/mp4", true))
   782  	t.Run("mp3", testHeaders(data, "test.mp3", "audio/mpeg", true))
   783  	t.Run("pdf", testHeaders(data, "test.pdf", "application/pdf", false))
   784  	t.Run("txt", testHeaders(data, "test.txt", "text/plain", false))
   785  	t.Run("html", testHeaders(data, "test.html", "text/plain", false))
   786  	t.Run("js", testHeaders(data, "test.js", "text/plain", false))
   787  	t.Run("go", testHeaders(data, "test.go", "application/octet-stream", false))
   788  	t.Run("zip", testHeaders(data, "test.zip", "application/zip", false))
   789  	// Not every platform can recognize these
   790  	//t.Run("exe", testHeaders(data, "test.exe", "application/x-ms", false))
   791  	t.Run("no extension", testHeaders(data, "test", "application/octet-stream", false))
   792  	t.Run("no extension 2", testHeaders([]byte("<html></html>"), "test", "application/octet-stream", false))
   793  }
   794  
   795  func TestGetFileThumbnail(t *testing.T) {
   796  	th := Setup(t).InitBasic()
   797  	defer th.TearDown()
   798  	Client := th.Client
   799  	channel := th.BasicChannel
   800  
   801  	if *th.App.Config().FileSettings.DriverName == "" {
   802  		t.Skip("skipping because no file driver is enabled")
   803  	}
   804  
   805  	sent, err := testutils.ReadTestFile("test.png")
   806  	require.NoError(t, err)
   807  
   808  	fileResp, resp := Client.UploadFile(sent, channel.Id, "test.png")
   809  	CheckNoError(t, resp)
   810  
   811  	fileId := fileResp.FileInfos[0].Id
   812  
   813  	data, resp := Client.GetFileThumbnail(fileId)
   814  	CheckNoError(t, resp)
   815  	require.NotEqual(t, 0, len(data), "should not be empty")
   816  
   817  	_, resp = Client.GetFileThumbnail("junk")
   818  	CheckBadRequestStatus(t, resp)
   819  
   820  	_, resp = Client.GetFileThumbnail(model.NewId())
   821  	CheckNotFoundStatus(t, resp)
   822  
   823  	Client.Logout()
   824  	_, resp = Client.GetFileThumbnail(fileId)
   825  	CheckUnauthorizedStatus(t, resp)
   826  
   827  	otherUser := th.CreateUser()
   828  	Client.Login(otherUser.Email, otherUser.Password)
   829  	_, resp = Client.GetFileThumbnail(fileId)
   830  	CheckForbiddenStatus(t, resp)
   831  
   832  	Client.Logout()
   833  	_, resp = th.SystemAdminClient.GetFileThumbnail(fileId)
   834  	CheckNoError(t, resp)
   835  }
   836  
   837  func TestGetFileLink(t *testing.T) {
   838  	th := Setup(t).InitBasic()
   839  	defer th.TearDown()
   840  	Client := th.Client
   841  	channel := th.BasicChannel
   842  
   843  	if *th.App.Config().FileSettings.DriverName == "" {
   844  		t.Skip("skipping because no file driver is enabled")
   845  	}
   846  
   847  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.EnablePublicLink = true })
   848  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.PublicLinkSalt = model.NewRandomString(32) })
   849  
   850  	data, err := testutils.ReadTestFile("test.png")
   851  	require.NoError(t, err)
   852  
   853  	fileResp, uploadResp := Client.UploadFile(data, channel.Id, "test.png")
   854  	CheckNoError(t, uploadResp)
   855  
   856  	fileId := fileResp.FileInfos[0].Id
   857  
   858  	_, resp := Client.GetFileLink(fileId)
   859  	CheckBadRequestStatus(t, resp)
   860  
   861  	// Hacky way to assign file to a post (usually would be done by CreatePost call)
   862  	err = th.App.Srv().Store.FileInfo().AttachToPost(fileId, th.BasicPost.Id, th.BasicUser.Id)
   863  	require.NoError(t, err)
   864  
   865  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.EnablePublicLink = false })
   866  	_, resp = Client.GetFileLink(fileId)
   867  	CheckNotImplementedStatus(t, resp)
   868  
   869  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.EnablePublicLink = true })
   870  	link, resp := Client.GetFileLink(fileId)
   871  	CheckNoError(t, resp)
   872  	require.NotEqual(t, "", link, "should've received public link")
   873  
   874  	_, resp = Client.GetFileLink("junk")
   875  	CheckBadRequestStatus(t, resp)
   876  
   877  	_, resp = Client.GetFileLink(model.NewId())
   878  	CheckNotFoundStatus(t, resp)
   879  
   880  	Client.Logout()
   881  	_, resp = Client.GetFileLink(fileId)
   882  	CheckUnauthorizedStatus(t, resp)
   883  
   884  	otherUser := th.CreateUser()
   885  	Client.Login(otherUser.Email, otherUser.Password)
   886  	_, resp = Client.GetFileLink(fileId)
   887  	CheckForbiddenStatus(t, resp)
   888  
   889  	Client.Logout()
   890  	_, resp = th.SystemAdminClient.GetFileLink(fileId)
   891  	CheckNoError(t, resp)
   892  
   893  	fileInfo, err := th.App.Srv().Store.FileInfo().Get(fileId)
   894  	require.NoError(t, err)
   895  	th.cleanupTestFile(fileInfo)
   896  }
   897  
   898  func TestGetFilePreview(t *testing.T) {
   899  	th := Setup(t).InitBasic()
   900  	defer th.TearDown()
   901  	Client := th.Client
   902  	channel := th.BasicChannel
   903  
   904  	if *th.App.Config().FileSettings.DriverName == "" {
   905  		t.Skip("skipping because no file driver is enabled")
   906  	}
   907  
   908  	sent, err := testutils.ReadTestFile("test.png")
   909  	require.NoError(t, err)
   910  
   911  	fileResp, resp := Client.UploadFile(sent, channel.Id, "test.png")
   912  	CheckNoError(t, resp)
   913  	fileId := fileResp.FileInfos[0].Id
   914  
   915  	data, resp := Client.GetFilePreview(fileId)
   916  	CheckNoError(t, resp)
   917  	require.NotEqual(t, 0, len(data), "should not be empty")
   918  
   919  	_, resp = Client.GetFilePreview("junk")
   920  	CheckBadRequestStatus(t, resp)
   921  
   922  	_, resp = Client.GetFilePreview(model.NewId())
   923  	CheckNotFoundStatus(t, resp)
   924  
   925  	Client.Logout()
   926  	_, resp = Client.GetFilePreview(fileId)
   927  	CheckUnauthorizedStatus(t, resp)
   928  
   929  	otherUser := th.CreateUser()
   930  	Client.Login(otherUser.Email, otherUser.Password)
   931  	_, resp = Client.GetFilePreview(fileId)
   932  	CheckForbiddenStatus(t, resp)
   933  
   934  	Client.Logout()
   935  	_, resp = th.SystemAdminClient.GetFilePreview(fileId)
   936  	CheckNoError(t, resp)
   937  }
   938  
   939  func TestGetFileInfo(t *testing.T) {
   940  	th := Setup(t).InitBasic()
   941  	defer th.TearDown()
   942  	Client := th.Client
   943  	user := th.BasicUser
   944  	channel := th.BasicChannel
   945  
   946  	if *th.App.Config().FileSettings.DriverName == "" {
   947  		t.Skip("skipping because no file driver is enabled")
   948  	}
   949  
   950  	sent, err := testutils.ReadTestFile("test.png")
   951  	require.NoError(t, err)
   952  
   953  	fileResp, resp := Client.UploadFile(sent, channel.Id, "test.png")
   954  	CheckNoError(t, resp)
   955  	fileId := fileResp.FileInfos[0].Id
   956  
   957  	info, resp := Client.GetFileInfo(fileId)
   958  	CheckNoError(t, resp)
   959  
   960  	require.NoError(t, err)
   961  	require.Equal(t, fileId, info.Id, "got incorrect file")
   962  	require.Equal(t, user.Id, info.CreatorId, "file should be assigned to user")
   963  	require.Equal(t, "", info.PostId, "file shouldn't have a post")
   964  	require.Equal(t, "", info.Path, "file path shouldn't have been returned to client")
   965  	require.Equal(t, "", info.ThumbnailPath, "file thumbnail path shouldn't have been returned to client")
   966  	require.Equal(t, "", info.PreviewPath, "file preview path shouldn't have been returned to client")
   967  	require.Equal(t, "image/png", info.MimeType, "mime type should've been image/png")
   968  
   969  	_, resp = Client.GetFileInfo("junk")
   970  	CheckBadRequestStatus(t, resp)
   971  
   972  	_, resp = Client.GetFileInfo(model.NewId())
   973  	CheckNotFoundStatus(t, resp)
   974  
   975  	Client.Logout()
   976  	_, resp = Client.GetFileInfo(fileId)
   977  	CheckUnauthorizedStatus(t, resp)
   978  
   979  	otherUser := th.CreateUser()
   980  	Client.Login(otherUser.Email, otherUser.Password)
   981  	_, resp = Client.GetFileInfo(fileId)
   982  	CheckForbiddenStatus(t, resp)
   983  
   984  	Client.Logout()
   985  	_, resp = th.SystemAdminClient.GetFileInfo(fileId)
   986  	CheckNoError(t, resp)
   987  }
   988  
   989  func TestGetPublicFile(t *testing.T) {
   990  	th := Setup(t).InitBasic()
   991  	defer th.TearDown()
   992  	Client := th.Client
   993  	channel := th.BasicChannel
   994  
   995  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.EnablePublicLink = true })
   996  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.PublicLinkSalt = model.NewRandomString(32) })
   997  
   998  	data, err := testutils.ReadTestFile("test.png")
   999  	require.NoError(t, err)
  1000  
  1001  	fileResp, httpResp := Client.UploadFile(data, channel.Id, "test.png")
  1002  	CheckNoError(t, httpResp)
  1003  
  1004  	fileId := fileResp.FileInfos[0].Id
  1005  
  1006  	// Hacky way to assign file to a post (usually would be done by CreatePost call)
  1007  	err = th.App.Srv().Store.FileInfo().AttachToPost(fileId, th.BasicPost.Id, th.BasicUser.Id)
  1008  	require.NoError(t, err)
  1009  
  1010  	info, err := th.App.Srv().Store.FileInfo().Get(fileId)
  1011  	require.NoError(t, err)
  1012  	link := th.App.GeneratePublicLink(Client.Url, info)
  1013  
  1014  	resp, err := http.Get(link)
  1015  	require.NoError(t, err)
  1016  	require.Equal(t, http.StatusOK, resp.StatusCode, "failed to get image with public link")
  1017  
  1018  	resp, err = http.Get(link[:strings.LastIndex(link, "?")])
  1019  	require.NoError(t, err)
  1020  	require.Equal(t, http.StatusBadRequest, resp.StatusCode, "should've failed to get image with public link without hash", resp.Status)
  1021  
  1022  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.EnablePublicLink = false })
  1023  
  1024  	resp, err = http.Get(link)
  1025  	require.NoError(t, err)
  1026  	require.Equal(t, http.StatusNotImplemented, resp.StatusCode, "should've failed to get image with disabled public link")
  1027  
  1028  	// test after the salt has changed
  1029  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.EnablePublicLink = true })
  1030  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.PublicLinkSalt = model.NewRandomString(32) })
  1031  
  1032  	resp, err = http.Get(link)
  1033  	require.NoError(t, err)
  1034  	require.Equal(t, http.StatusBadRequest, resp.StatusCode, "should've failed to get image with public link after salt changed")
  1035  
  1036  	resp, err = http.Get(link)
  1037  	require.NoError(t, err)
  1038  	require.Equal(t, http.StatusBadRequest, resp.StatusCode, "should've failed to get image with public link after salt changed")
  1039  
  1040  	fileInfo, err := th.App.Srv().Store.FileInfo().Get(fileId)
  1041  	require.NoError(t, err)
  1042  	require.NoError(t, th.cleanupTestFile(fileInfo))
  1043  
  1044  	th.cleanupTestFile(info)
  1045  	link = th.App.GeneratePublicLink(Client.Url, info)
  1046  	resp, err = http.Get(link)
  1047  	require.NoError(t, err)
  1048  	require.Equal(t, http.StatusNotFound, resp.StatusCode, "should've failed to get file after it is deleted")
  1049  }
  1050  
  1051  func TestSearchFiles(t *testing.T) {
  1052  	th := Setup(t).InitBasic()
  1053  	defer th.TearDown()
  1054  	experimentalViewArchivedChannels := *th.App.Config().TeamSettings.ExperimentalViewArchivedChannels
  1055  	defer func() {
  1056  		th.App.UpdateConfig(func(cfg *model.Config) {
  1057  			cfg.TeamSettings.ExperimentalViewArchivedChannels = &experimentalViewArchivedChannels
  1058  		})
  1059  	}()
  1060  	th.App.UpdateConfig(func(cfg *model.Config) {
  1061  		*cfg.TeamSettings.ExperimentalViewArchivedChannels = true
  1062  	})
  1063  	data, err := testutils.ReadTestFile("test.png")
  1064  	require.NoError(t, err)
  1065  
  1066  	th.LoginBasic()
  1067  	Client := th.Client
  1068  
  1069  	filename := "search for fileInfo1"
  1070  	fileInfo1, appErr := th.App.UploadFile(th.Context, data, th.BasicChannel.Id, filename)
  1071  	require.Nil(t, appErr)
  1072  	err = th.App.Srv().Store.FileInfo().AttachToPost(fileInfo1.Id, th.BasicPost.Id, th.BasicUser.Id)
  1073  	require.NoError(t, err)
  1074  
  1075  	filename = "search for fileInfo2"
  1076  	fileInfo2, appErr := th.App.UploadFile(th.Context, data, th.BasicChannel.Id, filename)
  1077  	require.Nil(t, appErr)
  1078  	err = th.App.Srv().Store.FileInfo().AttachToPost(fileInfo2.Id, th.BasicPost.Id, th.BasicUser.Id)
  1079  	require.NoError(t, err)
  1080  
  1081  	filename = "tagged search for fileInfo3"
  1082  	fileInfo3, appErr := th.App.UploadFile(th.Context, data, th.BasicChannel.Id, filename)
  1083  	require.Nil(t, appErr)
  1084  	err = th.App.Srv().Store.FileInfo().AttachToPost(fileInfo3.Id, th.BasicPost.Id, th.BasicUser.Id)
  1085  	require.NoError(t, err)
  1086  
  1087  	filename = "tagged for fileInfo4"
  1088  	fileInfo4, appErr := th.App.UploadFile(th.Context, data, th.BasicChannel.Id, filename)
  1089  	require.Nil(t, appErr)
  1090  	err = th.App.Srv().Store.FileInfo().AttachToPost(fileInfo4.Id, th.BasicPost.Id, th.BasicUser.Id)
  1091  	require.NoError(t, err)
  1092  
  1093  	archivedChannel := th.CreatePublicChannel()
  1094  	fileInfo5, appErr := th.App.UploadFile(th.Context, data, archivedChannel.Id, "tagged for fileInfo3")
  1095  	require.Nil(t, appErr)
  1096  	post := &model.Post{ChannelId: archivedChannel.Id, Message: model.NewId() + "a"}
  1097  	rpost, resp := Client.CreatePost(post)
  1098  	CheckNoError(t, resp)
  1099  	err = th.App.Srv().Store.FileInfo().AttachToPost(fileInfo5.Id, rpost.Id, th.BasicUser.Id)
  1100  	require.NoError(t, err)
  1101  	th.Client.DeleteChannel(archivedChannel.Id)
  1102  
  1103  	terms := "search"
  1104  	isOrSearch := false
  1105  	timezoneOffset := 5
  1106  	searchParams := model.SearchParameter{
  1107  		Terms:          &terms,
  1108  		IsOrSearch:     &isOrSearch,
  1109  		TimeZoneOffset: &timezoneOffset,
  1110  	}
  1111  	fileInfos, resp := Client.SearchFilesWithParams(th.BasicTeam.Id, &searchParams)
  1112  	CheckNoError(t, resp)
  1113  	require.Len(t, fileInfos.Order, 3, "wrong search")
  1114  
  1115  	terms = "search"
  1116  	page := 0
  1117  	perPage := 2
  1118  	searchParams = model.SearchParameter{
  1119  		Terms:          &terms,
  1120  		IsOrSearch:     &isOrSearch,
  1121  		TimeZoneOffset: &timezoneOffset,
  1122  		Page:           &page,
  1123  		PerPage:        &perPage,
  1124  	}
  1125  	fileInfos2, resp := Client.SearchFilesWithParams(th.BasicTeam.Id, &searchParams)
  1126  	CheckNoError(t, resp)
  1127  	// We don't support paging for DB search yet, modify this when we do.
  1128  	require.Len(t, fileInfos2.Order, 3, "Wrong number of fileInfos")
  1129  	assert.Equal(t, fileInfos.Order[0], fileInfos2.Order[0])
  1130  	assert.Equal(t, fileInfos.Order[1], fileInfos2.Order[1])
  1131  
  1132  	page = 1
  1133  	searchParams = model.SearchParameter{
  1134  		Terms:          &terms,
  1135  		IsOrSearch:     &isOrSearch,
  1136  		TimeZoneOffset: &timezoneOffset,
  1137  		Page:           &page,
  1138  		PerPage:        &perPage,
  1139  	}
  1140  	fileInfos2, resp = Client.SearchFilesWithParams(th.BasicTeam.Id, &searchParams)
  1141  	CheckNoError(t, resp)
  1142  	// We don't support paging for DB search yet, modify this when we do.
  1143  	require.Empty(t, fileInfos2.Order, "Wrong number of fileInfos")
  1144  
  1145  	fileInfos, resp = Client.SearchFiles(th.BasicTeam.Id, "search", false)
  1146  	CheckNoError(t, resp)
  1147  	require.Len(t, fileInfos.Order, 3, "wrong search")
  1148  
  1149  	fileInfos, resp = Client.SearchFiles(th.BasicTeam.Id, "fileInfo2", false)
  1150  	CheckNoError(t, resp)
  1151  	require.Len(t, fileInfos.Order, 1, "wrong number of fileInfos")
  1152  	require.Equal(t, fileInfo2.Id, fileInfos.Order[0], "wrong search")
  1153  
  1154  	terms = "tagged"
  1155  	includeDeletedChannels := true
  1156  	searchParams = model.SearchParameter{
  1157  		Terms:                  &terms,
  1158  		IsOrSearch:             &isOrSearch,
  1159  		TimeZoneOffset:         &timezoneOffset,
  1160  		IncludeDeletedChannels: &includeDeletedChannels,
  1161  	}
  1162  	fileInfos, resp = Client.SearchFilesWithParams(th.BasicTeam.Id, &searchParams)
  1163  	CheckNoError(t, resp)
  1164  	require.Len(t, fileInfos.Order, 3, "wrong search")
  1165  
  1166  	th.App.UpdateConfig(func(cfg *model.Config) {
  1167  		*cfg.TeamSettings.ExperimentalViewArchivedChannels = false
  1168  	})
  1169  
  1170  	fileInfos, resp = Client.SearchFilesWithParams(th.BasicTeam.Id, &searchParams)
  1171  	CheckNoError(t, resp)
  1172  	require.Len(t, fileInfos.Order, 2, "wrong search")
  1173  
  1174  	fileInfos, _ = Client.SearchFiles(th.BasicTeam.Id, "*", false)
  1175  	require.Empty(t, fileInfos.Order, "searching for just * shouldn't return any results")
  1176  
  1177  	fileInfos, resp = Client.SearchFiles(th.BasicTeam.Id, "fileInfo1 fileInfo2", true)
  1178  	CheckNoError(t, resp)
  1179  	require.Len(t, fileInfos.Order, 2, "wrong search results")
  1180  
  1181  	_, resp = Client.SearchFiles("junk", "#sgtitlereview", false)
  1182  	CheckBadRequestStatus(t, resp)
  1183  
  1184  	_, resp = Client.SearchFiles(model.NewId(), "#sgtitlereview", false)
  1185  	CheckForbiddenStatus(t, resp)
  1186  
  1187  	_, resp = Client.SearchFiles(th.BasicTeam.Id, "", false)
  1188  	CheckBadRequestStatus(t, resp)
  1189  
  1190  	Client.Logout()
  1191  	_, resp = Client.SearchFiles(th.BasicTeam.Id, "#sgtitlereview", false)
  1192  	CheckUnauthorizedStatus(t, resp)
  1193  }