github.com/xzl8028/xenia-server@v0.0.0-20190809101854-18450a97da63/app/post_test.go (about)

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