github.com/mad-app/mattermost-server@v5.11.1+incompatible/app/post_test.go (about)

     1  // Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
     2  // See License.txt for license information.
     3  
     4  package app
     5  
     6  import (
     7  	"fmt"
     8  	"net/http"
     9  	"sync"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/stretchr/testify/assert"
    14  	"github.com/stretchr/testify/require"
    15  
    16  	"github.com/mattermost/mattermost-server/model"
    17  	"github.com/mattermost/mattermost-server/store"
    18  	"github.com/mattermost/mattermost-server/store/storetest"
    19  )
    20  
    21  func TestCreatePostDeduplicate(t *testing.T) {
    22  	th := Setup(t).InitBasic()
    23  	defer th.TearDown()
    24  
    25  	t.Run("duplicate create post is idempotent", func(t *testing.T) {
    26  		pendingPostId := model.NewId()
    27  		post, err := th.App.CreatePostAsUser(&model.Post{
    28  			UserId:        th.BasicUser.Id,
    29  			ChannelId:     th.BasicChannel.Id,
    30  			Message:       "message",
    31  			PendingPostId: pendingPostId,
    32  		}, "")
    33  		require.Nil(t, err)
    34  		require.Equal(t, "message", post.Message)
    35  
    36  		duplicatePost, err := th.App.CreatePostAsUser(&model.Post{
    37  			UserId:        th.BasicUser.Id,
    38  			ChannelId:     th.BasicChannel.Id,
    39  			Message:       "message",
    40  			PendingPostId: pendingPostId,
    41  		}, "")
    42  		require.Nil(t, err)
    43  		require.Equal(t, post.Id, duplicatePost.Id, "should have returned previously created post id")
    44  		require.Equal(t, "message", duplicatePost.Message)
    45  	})
    46  
    47  	t.Run("post rejected by plugin leaves cache ready for non-deduplicated try", func(t *testing.T) {
    48  		setupPluginApiTest(t, `
    49  			package main
    50  
    51  			import (
    52  				"github.com/mattermost/mattermost-server/plugin"
    53  				"github.com/mattermost/mattermost-server/model"
    54  			)
    55  
    56  			type MyPlugin struct {
    57  				plugin.MattermostPlugin
    58  				allow bool
    59  			}
    60  
    61  			func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) {
    62  				if !p.allow {
    63  					p.allow = true
    64  					return nil, "rejected"
    65  				}
    66  
    67  				return nil, ""
    68  			}
    69  
    70  			func main() {
    71  				plugin.ClientMain(&MyPlugin{})
    72  			}
    73  		`, `{"id": "testrejectfirstpost", "backend": {"executable": "backend.exe"}}`, "testrejectfirstpost", th.App)
    74  
    75  		pendingPostId := model.NewId()
    76  		post, err := th.App.CreatePostAsUser(&model.Post{
    77  			UserId:        th.BasicUser.Id,
    78  			ChannelId:     th.BasicChannel.Id,
    79  			Message:       "message",
    80  			PendingPostId: pendingPostId,
    81  		}, "")
    82  		require.NotNil(t, err)
    83  		require.Equal(t, "Post rejected by plugin. rejected", err.Id)
    84  		require.Nil(t, post)
    85  
    86  		duplicatePost, err := th.App.CreatePostAsUser(&model.Post{
    87  			UserId:        th.BasicUser.Id,
    88  			ChannelId:     th.BasicChannel.Id,
    89  			Message:       "message",
    90  			PendingPostId: pendingPostId,
    91  		}, "")
    92  		require.Nil(t, err)
    93  		require.Equal(t, "message", duplicatePost.Message)
    94  	})
    95  
    96  	t.Run("slow posting after cache entry blocks duplicate request", func(t *testing.T) {
    97  		setupPluginApiTest(t, `
    98  			package main
    99  
   100  			import (
   101  				"github.com/mattermost/mattermost-server/plugin"
   102  				"github.com/mattermost/mattermost-server/model"
   103  				"time"
   104  			)
   105  
   106  			type MyPlugin struct {
   107  				plugin.MattermostPlugin
   108  				instant bool
   109  			}
   110  
   111  			func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) {
   112  				if !p.instant {
   113  					p.instant = true
   114  					time.Sleep(3 * time.Second)
   115  				}
   116  
   117  				return nil, ""
   118  			}
   119  
   120  			func main() {
   121  				plugin.ClientMain(&MyPlugin{})
   122  			}
   123  		`, `{"id": "testdelayfirstpost", "backend": {"executable": "backend.exe"}}`, "testdelayfirstpost", th.App)
   124  
   125  		var post *model.Post
   126  		pendingPostId := model.NewId()
   127  
   128  		wg := sync.WaitGroup{}
   129  
   130  		// Launch a goroutine to make the first CreatePost call that will get delayed
   131  		// by the plugin above.
   132  		wg.Add(1)
   133  		go func() {
   134  			defer wg.Done()
   135  			var err error
   136  			post, err = th.App.CreatePostAsUser(&model.Post{
   137  				UserId:        th.BasicUser.Id,
   138  				ChannelId:     th.BasicChannel.Id,
   139  				Message:       "plugin delayed",
   140  				PendingPostId: pendingPostId,
   141  			}, "")
   142  			require.Nil(t, err)
   143  			require.Equal(t, post.Message, "plugin delayed")
   144  		}()
   145  
   146  		// Give the goroutine above a chance to start and get delayed by the plugin.
   147  		time.Sleep(2 * time.Second)
   148  
   149  		// Try creating a duplicate post
   150  		duplicatePost, err := th.App.CreatePostAsUser(&model.Post{
   151  			UserId:        th.BasicUser.Id,
   152  			ChannelId:     th.BasicChannel.Id,
   153  			Message:       "plugin delayed",
   154  			PendingPostId: pendingPostId,
   155  		}, "")
   156  		require.NotNil(t, err)
   157  		require.Equal(t, "api.post.deduplicate_create_post.pending", err.Id)
   158  		require.Nil(t, duplicatePost)
   159  
   160  		// Wait for the first CreatePost to finish to ensure assertions are made.
   161  		wg.Wait()
   162  	})
   163  
   164  	t.Run("duplicate create post after cache expires is not idempotent", func(t *testing.T) {
   165  		pendingPostId := model.NewId()
   166  		post, err := th.App.CreatePostAsUser(&model.Post{
   167  			UserId:        th.BasicUser.Id,
   168  			ChannelId:     th.BasicChannel.Id,
   169  			Message:       "message",
   170  			PendingPostId: pendingPostId,
   171  		}, "")
   172  		require.Nil(t, err)
   173  		require.Equal(t, "message", post.Message)
   174  
   175  		time.Sleep(PENDING_POST_IDS_CACHE_TTL)
   176  
   177  		duplicatePost, err := th.App.CreatePostAsUser(&model.Post{
   178  			UserId:        th.BasicUser.Id,
   179  			ChannelId:     th.BasicChannel.Id,
   180  			Message:       "message",
   181  			PendingPostId: pendingPostId,
   182  		}, "")
   183  		require.Nil(t, err)
   184  		require.NotEqual(t, post.Id, duplicatePost.Id, "should have created new post id")
   185  		require.Equal(t, "message", duplicatePost.Message)
   186  	})
   187  }
   188  
   189  func TestAttachFilesToPost(t *testing.T) {
   190  	t.Run("should attach files", func(t *testing.T) {
   191  		th := Setup(t).InitBasic()
   192  		defer th.TearDown()
   193  
   194  		info1 := store.Must(th.App.Srv.Store.FileInfo().Save(&model.FileInfo{
   195  			CreatorId: th.BasicUser.Id,
   196  			Path:      "path.txt",
   197  		})).(*model.FileInfo)
   198  		info2 := store.Must(th.App.Srv.Store.FileInfo().Save(&model.FileInfo{
   199  			CreatorId: th.BasicUser.Id,
   200  			Path:      "path.txt",
   201  		})).(*model.FileInfo)
   202  
   203  		post := th.BasicPost
   204  		post.FileIds = []string{info1.Id, info2.Id}
   205  
   206  		err := th.App.attachFilesToPost(post)
   207  		assert.Nil(t, err)
   208  
   209  		infos, err := th.App.GetFileInfosForPost(post.Id)
   210  		assert.Nil(t, err)
   211  		assert.Len(t, infos, 2)
   212  	})
   213  
   214  	t.Run("should update File.PostIds after failing to add files", func(t *testing.T) {
   215  		th := Setup(t).InitBasic()
   216  		defer th.TearDown()
   217  
   218  		info1 := store.Must(th.App.Srv.Store.FileInfo().Save(&model.FileInfo{
   219  			CreatorId: th.BasicUser.Id,
   220  			Path:      "path.txt",
   221  			PostId:    model.NewId(),
   222  		})).(*model.FileInfo)
   223  		info2 := store.Must(th.App.Srv.Store.FileInfo().Save(&model.FileInfo{
   224  			CreatorId: th.BasicUser.Id,
   225  			Path:      "path.txt",
   226  		})).(*model.FileInfo)
   227  
   228  		post := th.BasicPost
   229  		post.FileIds = []string{info1.Id, info2.Id}
   230  
   231  		err := th.App.attachFilesToPost(post)
   232  		assert.Nil(t, err)
   233  
   234  		infos, err := th.App.GetFileInfosForPost(post.Id)
   235  		assert.Nil(t, err)
   236  		assert.Len(t, infos, 1)
   237  		assert.Equal(t, info2.Id, infos[0].Id)
   238  
   239  		updated, err := th.App.GetSinglePost(post.Id)
   240  		require.Nil(t, err)
   241  		assert.Len(t, updated.FileIds, 1)
   242  		assert.Contains(t, updated.FileIds, info2.Id)
   243  	})
   244  }
   245  
   246  func TestUpdatePostEditAt(t *testing.T) {
   247  	th := Setup(t).InitBasic()
   248  	defer th.TearDown()
   249  
   250  	post := &model.Post{}
   251  	*post = *th.BasicPost
   252  
   253  	post.IsPinned = true
   254  	if saved, err := th.App.UpdatePost(post, true); err != nil {
   255  		t.Fatal(err)
   256  	} else if saved.EditAt != post.EditAt {
   257  		t.Fatal("shouldn't have updated post.EditAt when pinning post")
   258  
   259  		*post = *saved
   260  	}
   261  
   262  	time.Sleep(time.Millisecond * 100)
   263  
   264  	post.Message = model.NewId()
   265  	if saved, err := th.App.UpdatePost(post, true); err != nil {
   266  		t.Fatal(err)
   267  	} else if saved.EditAt == post.EditAt {
   268  		t.Fatal("should have updated post.EditAt when updating post message")
   269  	}
   270  
   271  	time.Sleep(time.Millisecond * 200)
   272  }
   273  
   274  func TestUpdatePostTimeLimit(t *testing.T) {
   275  	th := Setup(t).InitBasic()
   276  	defer th.TearDown()
   277  
   278  	post := &model.Post{}
   279  	*post = *th.BasicPost
   280  
   281  	th.App.SetLicense(model.NewTestLicense())
   282  
   283  	th.App.UpdateConfig(func(cfg *model.Config) {
   284  		*cfg.ServiceSettings.PostEditTimeLimit = -1
   285  	})
   286  	if _, err := th.App.UpdatePost(post, true); err != nil {
   287  		t.Fatal(err)
   288  	}
   289  
   290  	th.App.UpdateConfig(func(cfg *model.Config) {
   291  		*cfg.ServiceSettings.PostEditTimeLimit = 1000000000
   292  	})
   293  	post.Message = model.NewId()
   294  	if _, err := th.App.UpdatePost(post, true); err != nil {
   295  		t.Fatal("should allow you to edit the post")
   296  	}
   297  
   298  	th.App.UpdateConfig(func(cfg *model.Config) {
   299  		*cfg.ServiceSettings.PostEditTimeLimit = 1
   300  	})
   301  	post.Message = model.NewId()
   302  	if _, err := th.App.UpdatePost(post, true); err == nil {
   303  		t.Fatal("should fail on update old post")
   304  	}
   305  
   306  	th.App.UpdateConfig(func(cfg *model.Config) {
   307  		*cfg.ServiceSettings.PostEditTimeLimit = -1
   308  	})
   309  }
   310  
   311  func TestUpdatePostInArchivedChannel(t *testing.T) {
   312  	th := Setup(t).InitBasic()
   313  	defer th.TearDown()
   314  
   315  	archivedChannel := th.CreateChannel(th.BasicTeam)
   316  	post := th.CreatePost(archivedChannel)
   317  	th.App.DeleteChannel(archivedChannel, "")
   318  
   319  	_, err := th.App.UpdatePost(post, true)
   320  	require.NotNil(t, err)
   321  	require.Equal(t, "api.post.update_post.can_not_update_post_in_deleted.error", err.Id)
   322  }
   323  
   324  func TestPostReplyToPostWhereRootPosterLeftChannel(t *testing.T) {
   325  	// This test ensures that when replying to a root post made by a user who has since left the channel, the reply
   326  	// post completes successfully. This is a regression test for PLT-6523.
   327  	th := Setup(t).InitBasic()
   328  	defer th.TearDown()
   329  
   330  	channel := th.BasicChannel
   331  	userInChannel := th.BasicUser2
   332  	userNotInChannel := th.BasicUser
   333  	rootPost := th.BasicPost
   334  
   335  	if _, err := th.App.AddUserToChannel(userInChannel, channel); err != nil {
   336  		t.Fatal(err)
   337  	}
   338  
   339  	if err := th.App.RemoveUserFromChannel(userNotInChannel.Id, "", channel); err != nil {
   340  		t.Fatal(err)
   341  	}
   342  
   343  	replyPost := model.Post{
   344  		Message:       "asd",
   345  		ChannelId:     channel.Id,
   346  		RootId:        rootPost.Id,
   347  		ParentId:      rootPost.Id,
   348  		PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
   349  		UserId:        userInChannel.Id,
   350  		CreateAt:      0,
   351  	}
   352  
   353  	if _, err := th.App.CreatePostAsUser(&replyPost, ""); err != nil {
   354  		t.Fatal(err)
   355  	}
   356  }
   357  
   358  func TestPostAttachPostToChildPost(t *testing.T) {
   359  	th := Setup(t).InitBasic()
   360  	defer th.TearDown()
   361  
   362  	channel := th.BasicChannel
   363  	user := th.BasicUser
   364  	rootPost := th.BasicPost
   365  
   366  	replyPost1 := model.Post{
   367  		Message:       "reply one",
   368  		ChannelId:     channel.Id,
   369  		RootId:        rootPost.Id,
   370  		ParentId:      rootPost.Id,
   371  		PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
   372  		UserId:        user.Id,
   373  		CreateAt:      0,
   374  	}
   375  
   376  	res1, err := th.App.CreatePostAsUser(&replyPost1, "")
   377  	if err != nil {
   378  		t.Fatal(err)
   379  	}
   380  
   381  	replyPost2 := model.Post{
   382  		Message:       "reply two",
   383  		ChannelId:     channel.Id,
   384  		RootId:        res1.Id,
   385  		ParentId:      res1.Id,
   386  		PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
   387  		UserId:        user.Id,
   388  		CreateAt:      0,
   389  	}
   390  
   391  	_, err = th.App.CreatePostAsUser(&replyPost2, "")
   392  	if err.StatusCode != http.StatusBadRequest {
   393  		t.Fatal(fmt.Sprintf("Expected BadRequest error, got %v", err))
   394  	}
   395  
   396  	replyPost3 := model.Post{
   397  		Message:       "reply three",
   398  		ChannelId:     channel.Id,
   399  		RootId:        rootPost.Id,
   400  		ParentId:      rootPost.Id,
   401  		PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
   402  		UserId:        user.Id,
   403  		CreateAt:      0,
   404  	}
   405  
   406  	if _, err := th.App.CreatePostAsUser(&replyPost3, ""); err != nil {
   407  		t.Fatal(err)
   408  	}
   409  }
   410  
   411  func TestPostChannelMentions(t *testing.T) {
   412  	th := Setup(t).InitBasic()
   413  	defer th.TearDown()
   414  
   415  	channel := th.BasicChannel
   416  	user := th.BasicUser
   417  
   418  	channelToMention, err := th.App.CreateChannel(&model.Channel{
   419  		DisplayName: "Mention Test",
   420  		Name:        "mention-test",
   421  		Type:        model.CHANNEL_OPEN,
   422  		TeamId:      th.BasicTeam.Id,
   423  	}, false)
   424  	if err != nil {
   425  		t.Fatal(err.Error())
   426  	}
   427  	defer th.App.PermanentDeleteChannel(channelToMention)
   428  
   429  	_, err = th.App.AddUserToChannel(user, channel)
   430  	require.Nil(t, err)
   431  
   432  	post := &model.Post{
   433  		Message:       fmt.Sprintf("hello, ~%v!", channelToMention.Name),
   434  		ChannelId:     channel.Id,
   435  		PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
   436  		UserId:        user.Id,
   437  		CreateAt:      0,
   438  	}
   439  
   440  	result, err := th.App.CreatePostAsUser(post, "")
   441  	require.Nil(t, err)
   442  	assert.Equal(t, map[string]interface{}{
   443  		"mention-test": map[string]interface{}{
   444  			"display_name": "Mention Test",
   445  		},
   446  	}, result.Props["channel_mentions"])
   447  
   448  	post.Message = fmt.Sprintf("goodbye, ~%v!", channelToMention.Name)
   449  	result, err = th.App.UpdatePost(post, false)
   450  	require.Nil(t, err)
   451  	assert.Equal(t, map[string]interface{}{
   452  		"mention-test": map[string]interface{}{
   453  			"display_name": "Mention Test",
   454  		},
   455  	}, result.Props["channel_mentions"])
   456  }
   457  
   458  func TestImageProxy(t *testing.T) {
   459  	th := Setup(t).InitBasic()
   460  	defer th.TearDown()
   461  
   462  	th.App.UpdateConfig(func(cfg *model.Config) {
   463  		*cfg.ServiceSettings.SiteURL = "http://mymattermost.com"
   464  	})
   465  
   466  	for name, tc := range map[string]struct {
   467  		ProxyType       string
   468  		ProxyURL        string
   469  		ProxyOptions    string
   470  		ImageURL        string
   471  		ProxiedImageURL string
   472  	}{
   473  		"atmos/camo": {
   474  			ProxyType:       model.IMAGE_PROXY_TYPE_ATMOS_CAMO,
   475  			ProxyURL:        "https://127.0.0.1",
   476  			ProxyOptions:    "foo",
   477  			ImageURL:        "http://mydomain.com/myimage",
   478  			ProxiedImageURL: "https://127.0.0.1/f8dace906d23689e8d5b12c3cefbedbf7b9b72f5/687474703a2f2f6d79646f6d61696e2e636f6d2f6d79696d616765",
   479  		},
   480  		"atmos/camo_SameSite": {
   481  			ProxyType:       model.IMAGE_PROXY_TYPE_ATMOS_CAMO,
   482  			ProxyURL:        "https://127.0.0.1",
   483  			ProxyOptions:    "foo",
   484  			ImageURL:        "http://mymattermost.com/myimage",
   485  			ProxiedImageURL: "http://mymattermost.com/myimage",
   486  		},
   487  		"atmos/camo_PathOnly": {
   488  			ProxyType:       model.IMAGE_PROXY_TYPE_ATMOS_CAMO,
   489  			ProxyURL:        "https://127.0.0.1",
   490  			ProxyOptions:    "foo",
   491  			ImageURL:        "/myimage",
   492  			ProxiedImageURL: "/myimage",
   493  		},
   494  		"atmos/camo_EmptyImageURL": {
   495  			ProxyType:       model.IMAGE_PROXY_TYPE_ATMOS_CAMO,
   496  			ProxyURL:        "https://127.0.0.1",
   497  			ProxyOptions:    "foo",
   498  			ImageURL:        "",
   499  			ProxiedImageURL: "",
   500  		},
   501  		"local": {
   502  			ProxyType:       model.IMAGE_PROXY_TYPE_LOCAL,
   503  			ImageURL:        "http://mydomain.com/myimage",
   504  			ProxiedImageURL: "http://mymattermost.com/api/v4/image?url=http%3A%2F%2Fmydomain.com%2Fmyimage",
   505  		},
   506  		"local_SameSite": {
   507  			ProxyType:       model.IMAGE_PROXY_TYPE_LOCAL,
   508  			ImageURL:        "http://mymattermost.com/myimage",
   509  			ProxiedImageURL: "http://mymattermost.com/myimage",
   510  		},
   511  		"local_PathOnly": {
   512  			ProxyType:       model.IMAGE_PROXY_TYPE_LOCAL,
   513  			ImageURL:        "/myimage",
   514  			ProxiedImageURL: "/myimage",
   515  		},
   516  		"local_EmptyImageURL": {
   517  			ProxyType:       model.IMAGE_PROXY_TYPE_LOCAL,
   518  			ImageURL:        "",
   519  			ProxiedImageURL: "",
   520  		},
   521  	} {
   522  		t.Run(name, func(t *testing.T) {
   523  			th.App.UpdateConfig(func(cfg *model.Config) {
   524  				cfg.ImageProxySettings.Enable = model.NewBool(true)
   525  				cfg.ImageProxySettings.ImageProxyType = model.NewString(tc.ProxyType)
   526  				cfg.ImageProxySettings.RemoteImageProxyOptions = model.NewString(tc.ProxyOptions)
   527  				cfg.ImageProxySettings.RemoteImageProxyURL = model.NewString(tc.ProxyURL)
   528  			})
   529  
   530  			post := &model.Post{
   531  				Id:      model.NewId(),
   532  				Message: "![foo](" + tc.ImageURL + ")",
   533  			}
   534  
   535  			list := model.NewPostList()
   536  			list.Posts[post.Id] = post
   537  
   538  			assert.Equal(t, "![foo]("+tc.ProxiedImageURL+")", th.App.PostWithProxyAddedToImageURLs(post).Message)
   539  
   540  			assert.Equal(t, "![foo]("+tc.ImageURL+")", th.App.PostWithProxyRemovedFromImageURLs(post).Message)
   541  			post.Message = "![foo](" + tc.ProxiedImageURL + ")"
   542  			assert.Equal(t, "![foo]("+tc.ImageURL+")", th.App.PostWithProxyRemovedFromImageURLs(post).Message)
   543  
   544  			if tc.ImageURL != "" {
   545  				post.Message = "![foo](" + tc.ImageURL + " =500x200)"
   546  				assert.Equal(t, "![foo]("+tc.ProxiedImageURL+" =500x200)", th.App.PostWithProxyAddedToImageURLs(post).Message)
   547  				assert.Equal(t, "![foo]("+tc.ImageURL+" =500x200)", th.App.PostWithProxyRemovedFromImageURLs(post).Message)
   548  				post.Message = "![foo](" + tc.ProxiedImageURL + " =500x200)"
   549  				assert.Equal(t, "![foo]("+tc.ImageURL+" =500x200)", th.App.PostWithProxyRemovedFromImageURLs(post).Message)
   550  			}
   551  		})
   552  	}
   553  }
   554  
   555  func TestMaxPostSize(t *testing.T) {
   556  	t.Parallel()
   557  
   558  	testCases := []struct {
   559  		Description         string
   560  		StoreMaxPostSize    int
   561  		ExpectedMaxPostSize int
   562  		ExpectedError       *model.AppError
   563  	}{
   564  		{
   565  			"error fetching max post size",
   566  			0,
   567  			model.POST_MESSAGE_MAX_RUNES_V1,
   568  			model.NewAppError("TestMaxPostSize", "this is an error", nil, "", http.StatusBadRequest),
   569  		},
   570  		{
   571  			"4000 rune limit",
   572  			4000,
   573  			4000,
   574  			nil,
   575  		},
   576  		{
   577  			"16383 rune limit",
   578  			16383,
   579  			16383,
   580  			nil,
   581  		},
   582  	}
   583  
   584  	for _, testCase := range testCases {
   585  		testCase := testCase
   586  		t.Run(testCase.Description, func(t *testing.T) {
   587  			t.Parallel()
   588  
   589  			mockStore := &storetest.Store{}
   590  			defer mockStore.AssertExpectations(t)
   591  
   592  			mockStore.PostStore.On("GetMaxPostSize").Return(
   593  				storetest.NewStoreChannel(store.StoreResult{
   594  					Data: testCase.StoreMaxPostSize,
   595  					Err:  testCase.ExpectedError,
   596  				}),
   597  			)
   598  
   599  			app := App{
   600  				Srv: &Server{
   601  					Store: mockStore,
   602  				},
   603  			}
   604  
   605  			assert.Equal(t, testCase.ExpectedMaxPostSize, app.MaxPostSize())
   606  		})
   607  	}
   608  }
   609  
   610  func TestDeletePostWithFileAttachments(t *testing.T) {
   611  	th := Setup(t).InitBasic()
   612  	defer th.TearDown()
   613  
   614  	// Create a post with a file attachment.
   615  	teamId := th.BasicTeam.Id
   616  	channelId := th.BasicChannel.Id
   617  	userId := th.BasicUser.Id
   618  	filename := "test"
   619  	data := []byte("abcd")
   620  
   621  	info1, err := th.App.DoUploadFile(time.Date(2007, 2, 4, 1, 2, 3, 4, time.Local), teamId, channelId, userId, filename, data)
   622  	if err != nil {
   623  		t.Fatal(err)
   624  	} else {
   625  		defer func() {
   626  			<-th.App.Srv.Store.FileInfo().PermanentDelete(info1.Id)
   627  			th.App.RemoveFile(info1.Path)
   628  		}()
   629  	}
   630  
   631  	post := &model.Post{
   632  		Message:       "asd",
   633  		ChannelId:     channelId,
   634  		PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
   635  		UserId:        userId,
   636  		CreateAt:      0,
   637  		FileIds:       []string{info1.Id},
   638  	}
   639  
   640  	post, err = th.App.CreatePost(post, th.BasicChannel, false)
   641  	assert.Nil(t, err)
   642  
   643  	// Delete the post.
   644  	post, err = th.App.DeletePost(post.Id, userId)
   645  	assert.Nil(t, err)
   646  
   647  	// Wait for the cleanup routine to finish.
   648  	time.Sleep(time.Millisecond * 100)
   649  
   650  	// Check that the file can no longer be reached.
   651  	_, err = th.App.GetFileInfo(info1.Id)
   652  	assert.NotNil(t, err)
   653  }
   654  
   655  func TestDeletePostInArchivedChannel(t *testing.T) {
   656  	th := Setup(t).InitBasic()
   657  	defer th.TearDown()
   658  
   659  	archivedChannel := th.CreateChannel(th.BasicTeam)
   660  	post := th.CreatePost(archivedChannel)
   661  	th.App.DeleteChannel(archivedChannel, "")
   662  
   663  	_, err := th.App.DeletePost(post.Id, "")
   664  	require.NotNil(t, err)
   665  	require.Equal(t, "api.post.delete_post.can_not_delete_post_in_deleted.error", err.Id)
   666  }
   667  
   668  func TestCreatePost(t *testing.T) {
   669  	t.Run("call PreparePostForClient before returning", func(t *testing.T) {
   670  		th := Setup(t).InitBasic()
   671  		defer th.TearDown()
   672  
   673  		th.App.UpdateConfig(func(cfg *model.Config) {
   674  			*cfg.ExperimentalSettings.DisablePostMetadata = true
   675  			*cfg.ImageProxySettings.Enable = true
   676  			*cfg.ImageProxySettings.ImageProxyType = "atmos/camo"
   677  			*cfg.ImageProxySettings.RemoteImageProxyURL = "https://127.0.0.1"
   678  			*cfg.ImageProxySettings.RemoteImageProxyOptions = "foo"
   679  		})
   680  
   681  		imageURL := "http://mydomain.com/myimage"
   682  		proxiedImageURL := "https://127.0.0.1/f8dace906d23689e8d5b12c3cefbedbf7b9b72f5/687474703a2f2f6d79646f6d61696e2e636f6d2f6d79696d616765"
   683  
   684  		post := &model.Post{
   685  			ChannelId: th.BasicChannel.Id,
   686  			Message:   "![image](" + imageURL + ")",
   687  			UserId:    th.BasicUser.Id,
   688  		}
   689  
   690  		rpost, err := th.App.CreatePost(post, th.BasicChannel, false)
   691  		require.Nil(t, err)
   692  		assert.Equal(t, "![image]("+proxiedImageURL+")", rpost.Message)
   693  	})
   694  }
   695  
   696  func TestPatchPost(t *testing.T) {
   697  	t.Run("call PreparePostForClient before returning", func(t *testing.T) {
   698  		th := Setup(t).InitBasic()
   699  		defer th.TearDown()
   700  
   701  		th.App.UpdateConfig(func(cfg *model.Config) {
   702  			*cfg.ExperimentalSettings.DisablePostMetadata = true
   703  			*cfg.ImageProxySettings.Enable = true
   704  			*cfg.ImageProxySettings.ImageProxyType = "atmos/camo"
   705  			*cfg.ImageProxySettings.RemoteImageProxyURL = "https://127.0.0.1"
   706  			*cfg.ImageProxySettings.RemoteImageProxyOptions = "foo"
   707  		})
   708  
   709  		imageURL := "http://mydomain.com/myimage"
   710  		proxiedImageURL := "https://127.0.0.1/f8dace906d23689e8d5b12c3cefbedbf7b9b72f5/687474703a2f2f6d79646f6d61696e2e636f6d2f6d79696d616765"
   711  
   712  		post := &model.Post{
   713  			ChannelId: th.BasicChannel.Id,
   714  			Message:   "![image](http://mydomain/anotherimage)",
   715  			UserId:    th.BasicUser.Id,
   716  		}
   717  
   718  		rpost, err := th.App.CreatePost(post, th.BasicChannel, false)
   719  		require.Nil(t, err)
   720  		assert.NotEqual(t, "![image]("+proxiedImageURL+")", rpost.Message)
   721  
   722  		patch := &model.PostPatch{
   723  			Message: model.NewString("![image](" + imageURL + ")"),
   724  		}
   725  
   726  		rpost, err = th.App.PatchPost(rpost.Id, patch)
   727  		require.Nil(t, err)
   728  		assert.Equal(t, "![image]("+proxiedImageURL+")", rpost.Message)
   729  	})
   730  }
   731  
   732  func TestPatchPostInArchivedChannel(t *testing.T) {
   733  	th := Setup(t).InitBasic()
   734  	defer th.TearDown()
   735  
   736  	archivedChannel := th.CreateChannel(th.BasicTeam)
   737  	post := th.CreatePost(archivedChannel)
   738  	th.App.DeleteChannel(archivedChannel, "")
   739  
   740  	_, err := th.App.PatchPost(post.Id, &model.PostPatch{IsPinned: model.NewBool(true)})
   741  	require.NotNil(t, err)
   742  	require.Equal(t, "api.post.patch_post.can_not_update_post_in_deleted.error", err.Id)
   743  }
   744  
   745  func TestUpdatePost(t *testing.T) {
   746  	t.Run("call PreparePostForClient before returning", func(t *testing.T) {
   747  		th := Setup(t).InitBasic()
   748  		defer th.TearDown()
   749  
   750  		th.App.UpdateConfig(func(cfg *model.Config) {
   751  			*cfg.ExperimentalSettings.DisablePostMetadata = true
   752  			*cfg.ImageProxySettings.Enable = true
   753  			*cfg.ImageProxySettings.ImageProxyType = "atmos/camo"
   754  			*cfg.ImageProxySettings.RemoteImageProxyURL = "https://127.0.0.1"
   755  			*cfg.ImageProxySettings.RemoteImageProxyOptions = "foo"
   756  		})
   757  
   758  		imageURL := "http://mydomain.com/myimage"
   759  		proxiedImageURL := "https://127.0.0.1/f8dace906d23689e8d5b12c3cefbedbf7b9b72f5/687474703a2f2f6d79646f6d61696e2e636f6d2f6d79696d616765"
   760  
   761  		post := &model.Post{
   762  			ChannelId: th.BasicChannel.Id,
   763  			Message:   "![image](http://mydomain/anotherimage)",
   764  			UserId:    th.BasicUser.Id,
   765  		}
   766  
   767  		rpost, err := th.App.CreatePost(post, th.BasicChannel, false)
   768  		require.Nil(t, err)
   769  		assert.NotEqual(t, "![image]("+proxiedImageURL+")", rpost.Message)
   770  
   771  		post.Id = rpost.Id
   772  		post.Message = "![image](" + imageURL + ")"
   773  
   774  		rpost, err = th.App.UpdatePost(post, false)
   775  		require.Nil(t, err)
   776  		assert.Equal(t, "![image]("+proxiedImageURL+")", rpost.Message)
   777  	})
   778  }