github.com/lologarithm/mattermost-server@v5.3.2-0.20181002060438-c82a84ed765b+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  	"net/http/httptest"
    10  	"strings"
    11  	"sync/atomic"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/dyatlov/go-opengraph/opengraph"
    16  	"github.com/stretchr/testify/assert"
    17  	"github.com/stretchr/testify/require"
    18  
    19  	"github.com/mattermost/mattermost-server/model"
    20  	"github.com/mattermost/mattermost-server/store"
    21  	"github.com/mattermost/mattermost-server/store/storetest"
    22  )
    23  
    24  func TestUpdatePostEditAt(t *testing.T) {
    25  	th := Setup().InitBasic()
    26  	defer th.TearDown()
    27  
    28  	post := &model.Post{}
    29  	*post = *th.BasicPost
    30  
    31  	post.IsPinned = true
    32  	if saved, err := th.App.UpdatePost(post, true); err != nil {
    33  		t.Fatal(err)
    34  	} else if saved.EditAt != post.EditAt {
    35  		t.Fatal("shouldn't have updated post.EditAt when pinning post")
    36  
    37  		*post = *saved
    38  	}
    39  
    40  	time.Sleep(time.Millisecond * 100)
    41  
    42  	post.Message = model.NewId()
    43  	if saved, err := th.App.UpdatePost(post, true); err != nil {
    44  		t.Fatal(err)
    45  	} else if saved.EditAt == post.EditAt {
    46  		t.Fatal("should have updated post.EditAt when updating post message")
    47  	}
    48  
    49  	time.Sleep(time.Millisecond * 200)
    50  }
    51  
    52  func TestUpdatePostTimeLimit(t *testing.T) {
    53  	th := Setup().InitBasic()
    54  	defer th.TearDown()
    55  
    56  	post := &model.Post{}
    57  	*post = *th.BasicPost
    58  
    59  	th.App.SetLicense(model.NewTestLicense())
    60  
    61  	th.App.UpdateConfig(func(cfg *model.Config) {
    62  		*cfg.ServiceSettings.PostEditTimeLimit = -1
    63  	})
    64  	if _, err := th.App.UpdatePost(post, true); err != nil {
    65  		t.Fatal(err)
    66  	}
    67  
    68  	th.App.UpdateConfig(func(cfg *model.Config) {
    69  		*cfg.ServiceSettings.PostEditTimeLimit = 1000000000
    70  	})
    71  	post.Message = model.NewId()
    72  	if _, err := th.App.UpdatePost(post, true); err != nil {
    73  		t.Fatal("should allow you to edit the post")
    74  	}
    75  
    76  	th.App.UpdateConfig(func(cfg *model.Config) {
    77  		*cfg.ServiceSettings.PostEditTimeLimit = 1
    78  	})
    79  	post.Message = model.NewId()
    80  	if _, err := th.App.UpdatePost(post, true); err == nil {
    81  		t.Fatal("should fail on update old post")
    82  	}
    83  
    84  	th.App.UpdateConfig(func(cfg *model.Config) {
    85  		*cfg.ServiceSettings.PostEditTimeLimit = -1
    86  	})
    87  }
    88  
    89  func TestPostReplyToPostWhereRootPosterLeftChannel(t *testing.T) {
    90  	// This test ensures that when replying to a root post made by a user who has since left the channel, the reply
    91  	// post completes successfully. This is a regression test for PLT-6523.
    92  	th := Setup().InitBasic()
    93  	defer th.TearDown()
    94  
    95  	channel := th.BasicChannel
    96  	userInChannel := th.BasicUser2
    97  	userNotInChannel := th.BasicUser
    98  	rootPost := th.BasicPost
    99  
   100  	if _, err := th.App.AddUserToChannel(userInChannel, channel); err != nil {
   101  		t.Fatal(err)
   102  	}
   103  
   104  	if err := th.App.RemoveUserFromChannel(userNotInChannel.Id, "", channel); err != nil {
   105  		t.Fatal(err)
   106  	}
   107  
   108  	replyPost := model.Post{
   109  		Message:       "asd",
   110  		ChannelId:     channel.Id,
   111  		RootId:        rootPost.Id,
   112  		ParentId:      rootPost.Id,
   113  		PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
   114  		UserId:        userInChannel.Id,
   115  		CreateAt:      0,
   116  	}
   117  
   118  	if _, err := th.App.CreatePostAsUser(&replyPost, false); err != nil {
   119  		t.Fatal(err)
   120  	}
   121  }
   122  
   123  func TestPostAction(t *testing.T) {
   124  	th := Setup().InitBasic()
   125  	defer th.TearDown()
   126  
   127  	th.App.UpdateConfig(func(cfg *model.Config) {
   128  		*cfg.ServiceSettings.AllowedUntrustedInternalConnections = "localhost 127.0.0.1"
   129  	})
   130  
   131  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   132  		request := model.PostActionIntegrationRequesteFromJson(r.Body)
   133  		assert.NotNil(t, request)
   134  
   135  		assert.Equal(t, request.UserId, th.BasicUser.Id)
   136  		assert.Equal(t, request.ChannelId, th.BasicChannel.Id)
   137  		assert.Equal(t, request.TeamId, th.BasicTeam.Id)
   138  		if request.Type == model.POST_ACTION_TYPE_SELECT {
   139  			assert.Equal(t, request.DataSource, "some_source")
   140  			assert.Equal(t, request.Context["selected_option"], "selected")
   141  		} else {
   142  			assert.Equal(t, request.DataSource, "")
   143  		}
   144  		assert.Equal(t, "foo", request.Context["s"])
   145  		assert.EqualValues(t, 3, request.Context["n"])
   146  		fmt.Fprintf(w, `{"post": {"message": "updated"}, "ephemeral_text": "foo"}`)
   147  	}))
   148  	defer ts.Close()
   149  
   150  	interactivePost := model.Post{
   151  		Message:       "Interactive post",
   152  		ChannelId:     th.BasicChannel.Id,
   153  		PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
   154  		UserId:        th.BasicUser.Id,
   155  		Props: model.StringInterface{
   156  			"attachments": []*model.SlackAttachment{
   157  				{
   158  					Text: "hello",
   159  					Actions: []*model.PostAction{
   160  						{
   161  							Integration: &model.PostActionIntegration{
   162  								Context: model.StringInterface{
   163  									"s": "foo",
   164  									"n": 3,
   165  								},
   166  								URL: ts.URL,
   167  							},
   168  							Name:       "action",
   169  							Type:       "some_type",
   170  							DataSource: "some_source",
   171  						},
   172  					},
   173  				},
   174  			},
   175  		},
   176  	}
   177  
   178  	post, err := th.App.CreatePostAsUser(&interactivePost, false)
   179  	require.Nil(t, err)
   180  
   181  	attachments, ok := post.Props["attachments"].([]*model.SlackAttachment)
   182  	require.True(t, ok)
   183  
   184  	require.NotEmpty(t, attachments[0].Actions)
   185  	require.NotEmpty(t, attachments[0].Actions[0].Id)
   186  
   187  	menuPost := model.Post{
   188  		Message:       "Interactive post",
   189  		ChannelId:     th.BasicChannel.Id,
   190  		PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
   191  		UserId:        th.BasicUser.Id,
   192  		Props: model.StringInterface{
   193  			"attachments": []*model.SlackAttachment{
   194  				{
   195  					Text: "hello",
   196  					Actions: []*model.PostAction{
   197  						{
   198  							Integration: &model.PostActionIntegration{
   199  								Context: model.StringInterface{
   200  									"s": "foo",
   201  									"n": 3,
   202  								},
   203  								URL: ts.URL,
   204  							},
   205  							Name:       "action",
   206  							Type:       model.POST_ACTION_TYPE_SELECT,
   207  							DataSource: "some_source",
   208  						},
   209  					},
   210  				},
   211  			},
   212  		},
   213  	}
   214  
   215  	post2, err := th.App.CreatePostAsUser(&menuPost, false)
   216  	require.Nil(t, err)
   217  
   218  	attachments2, ok := post2.Props["attachments"].([]*model.SlackAttachment)
   219  	require.True(t, ok)
   220  
   221  	require.NotEmpty(t, attachments2[0].Actions)
   222  	require.NotEmpty(t, attachments2[0].Actions[0].Id)
   223  
   224  	err = th.App.DoPostAction(post.Id, "notavalidid", th.BasicUser.Id, "")
   225  	require.NotNil(t, err)
   226  	assert.Equal(t, http.StatusNotFound, err.StatusCode)
   227  
   228  	err = th.App.DoPostAction(post.Id, attachments[0].Actions[0].Id, th.BasicUser.Id, "")
   229  	require.Nil(t, err)
   230  
   231  	err = th.App.DoPostAction(post2.Id, attachments2[0].Actions[0].Id, th.BasicUser.Id, "selected")
   232  	require.Nil(t, err)
   233  
   234  	th.App.UpdateConfig(func(cfg *model.Config) {
   235  		*cfg.ServiceSettings.AllowedUntrustedInternalConnections = ""
   236  	})
   237  
   238  	err = th.App.DoPostAction(post.Id, attachments[0].Actions[0].Id, th.BasicUser.Id, "")
   239  	require.NotNil(t, err)
   240  	require.True(t, strings.Contains(err.Error(), "address forbidden"))
   241  
   242  	interactivePostPlugin := model.Post{
   243  		Message:       "Interactive post",
   244  		ChannelId:     th.BasicChannel.Id,
   245  		PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
   246  		UserId:        th.BasicUser.Id,
   247  		Props: model.StringInterface{
   248  			"attachments": []*model.SlackAttachment{
   249  				{
   250  					Text: "hello",
   251  					Actions: []*model.PostAction{
   252  						{
   253  							Integration: &model.PostActionIntegration{
   254  								Context: model.StringInterface{
   255  									"s": "foo",
   256  									"n": 3,
   257  								},
   258  								URL: ts.URL + "/plugins/myplugin/myaction",
   259  							},
   260  							Name:       "action",
   261  							Type:       "some_type",
   262  							DataSource: "some_source",
   263  						},
   264  					},
   265  				},
   266  			},
   267  		},
   268  	}
   269  
   270  	postplugin, err := th.App.CreatePostAsUser(&interactivePostPlugin, false)
   271  	require.Nil(t, err)
   272  
   273  	attachmentsPlugin, ok := postplugin.Props["attachments"].([]*model.SlackAttachment)
   274  	require.True(t, ok)
   275  
   276  	err = th.App.DoPostAction(postplugin.Id, attachmentsPlugin[0].Actions[0].Id, th.BasicUser.Id, "")
   277  	require.Nil(t, err)
   278  
   279  	th.App.UpdateConfig(func(cfg *model.Config) {
   280  		*cfg.ServiceSettings.SiteURL = "http://127.1.1.1"
   281  	})
   282  
   283  	interactivePostSiteURL := model.Post{
   284  		Message:       "Interactive post",
   285  		ChannelId:     th.BasicChannel.Id,
   286  		PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
   287  		UserId:        th.BasicUser.Id,
   288  		Props: model.StringInterface{
   289  			"attachments": []*model.SlackAttachment{
   290  				{
   291  					Text: "hello",
   292  					Actions: []*model.PostAction{
   293  						{
   294  							Integration: &model.PostActionIntegration{
   295  								Context: model.StringInterface{
   296  									"s": "foo",
   297  									"n": 3,
   298  								},
   299  								URL: "http://127.1.1.1/plugins/myplugin/myaction",
   300  							},
   301  							Name:       "action",
   302  							Type:       "some_type",
   303  							DataSource: "some_source",
   304  						},
   305  					},
   306  				},
   307  			},
   308  		},
   309  	}
   310  
   311  	postSiteURL, err := th.App.CreatePostAsUser(&interactivePostSiteURL, false)
   312  	require.Nil(t, err)
   313  
   314  	attachmentsSiteURL, ok := postSiteURL.Props["attachments"].([]*model.SlackAttachment)
   315  	require.True(t, ok)
   316  
   317  	err = th.App.DoPostAction(postSiteURL.Id, attachmentsSiteURL[0].Actions[0].Id, th.BasicUser.Id, "")
   318  	require.NotNil(t, err)
   319  	require.False(t, strings.Contains(err.Error(), "address forbidden"))
   320  
   321  	th.App.UpdateConfig(func(cfg *model.Config) {
   322  		*cfg.ServiceSettings.SiteURL = ts.URL + "/subpath"
   323  	})
   324  
   325  	interactivePostSubpath := model.Post{
   326  		Message:       "Interactive post",
   327  		ChannelId:     th.BasicChannel.Id,
   328  		PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
   329  		UserId:        th.BasicUser.Id,
   330  		Props: model.StringInterface{
   331  			"attachments": []*model.SlackAttachment{
   332  				{
   333  					Text: "hello",
   334  					Actions: []*model.PostAction{
   335  						{
   336  							Integration: &model.PostActionIntegration{
   337  								Context: model.StringInterface{
   338  									"s": "foo",
   339  									"n": 3,
   340  								},
   341  								URL: ts.URL + "/subpath/plugins/myplugin/myaction",
   342  							},
   343  							Name:       "action",
   344  							Type:       "some_type",
   345  							DataSource: "some_source",
   346  						},
   347  					},
   348  				},
   349  			},
   350  		},
   351  	}
   352  
   353  	postSubpath, err := th.App.CreatePostAsUser(&interactivePostSubpath, false)
   354  	require.Nil(t, err)
   355  
   356  	attachmentsSubpath, ok := postSubpath.Props["attachments"].([]*model.SlackAttachment)
   357  	require.True(t, ok)
   358  
   359  	err = th.App.DoPostAction(postSubpath.Id, attachmentsSubpath[0].Actions[0].Id, th.BasicUser.Id, "")
   360  	require.Nil(t, err)
   361  }
   362  
   363  func TestPostChannelMentions(t *testing.T) {
   364  	th := Setup().InitBasic()
   365  	defer th.TearDown()
   366  
   367  	channel := th.BasicChannel
   368  	user := th.BasicUser
   369  
   370  	channelToMention, err := th.App.CreateChannel(&model.Channel{
   371  		DisplayName: "Mention Test",
   372  		Name:        "mention-test",
   373  		Type:        model.CHANNEL_OPEN,
   374  		TeamId:      th.BasicTeam.Id,
   375  	}, false)
   376  	if err != nil {
   377  		t.Fatal(err.Error())
   378  	}
   379  	defer th.App.PermanentDeleteChannel(channelToMention)
   380  
   381  	_, err = th.App.AddUserToChannel(user, channel)
   382  	require.Nil(t, err)
   383  
   384  	post := &model.Post{
   385  		Message:       fmt.Sprintf("hello, ~%v!", channelToMention.Name),
   386  		ChannelId:     channel.Id,
   387  		PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
   388  		UserId:        user.Id,
   389  		CreateAt:      0,
   390  	}
   391  
   392  	result, err := th.App.CreatePostAsUser(post, false)
   393  	require.Nil(t, err)
   394  	assert.Equal(t, map[string]interface{}{
   395  		"mention-test": map[string]interface{}{
   396  			"display_name": "Mention Test",
   397  		},
   398  	}, result.Props["channel_mentions"])
   399  
   400  	post.Message = fmt.Sprintf("goodbye, ~%v!", channelToMention.Name)
   401  	result, err = th.App.UpdatePost(post, false)
   402  	require.Nil(t, err)
   403  	assert.Equal(t, map[string]interface{}{
   404  		"mention-test": map[string]interface{}{
   405  			"display_name": "Mention Test",
   406  		},
   407  	}, result.Props["channel_mentions"])
   408  }
   409  
   410  func TestImageProxy(t *testing.T) {
   411  	th := Setup().InitBasic()
   412  	defer th.TearDown()
   413  
   414  	th.App.UpdateConfig(func(cfg *model.Config) {
   415  		*cfg.ServiceSettings.SiteURL = "http://mymattermost.com"
   416  	})
   417  
   418  	for name, tc := range map[string]struct {
   419  		ProxyType       string
   420  		ProxyURL        string
   421  		ProxyOptions    string
   422  		ImageURL        string
   423  		ProxiedImageURL string
   424  	}{
   425  		"atmos/camo": {
   426  			ProxyType:       "atmos/camo",
   427  			ProxyURL:        "https://127.0.0.1",
   428  			ProxyOptions:    "foo",
   429  			ImageURL:        "http://mydomain.com/myimage",
   430  			ProxiedImageURL: "https://127.0.0.1/f8dace906d23689e8d5b12c3cefbedbf7b9b72f5/687474703a2f2f6d79646f6d61696e2e636f6d2f6d79696d616765",
   431  		},
   432  		"atmos/camo_SameSite": {
   433  			ProxyType:       "atmos/camo",
   434  			ProxyURL:        "https://127.0.0.1",
   435  			ProxyOptions:    "foo",
   436  			ImageURL:        "http://mymattermost.com/myimage",
   437  			ProxiedImageURL: "http://mymattermost.com/myimage",
   438  		},
   439  		"atmos/camo_PathOnly": {
   440  			ProxyType:       "atmos/camo",
   441  			ProxyURL:        "https://127.0.0.1",
   442  			ProxyOptions:    "foo",
   443  			ImageURL:        "/myimage",
   444  			ProxiedImageURL: "/myimage",
   445  		},
   446  		"atmos/camo_EmptyImageURL": {
   447  			ProxyType:       "atmos/camo",
   448  			ProxyURL:        "https://127.0.0.1",
   449  			ProxyOptions:    "foo",
   450  			ImageURL:        "",
   451  			ProxiedImageURL: "",
   452  		},
   453  	} {
   454  		t.Run(name, func(t *testing.T) {
   455  			th.App.UpdateConfig(func(cfg *model.Config) {
   456  				cfg.ServiceSettings.ImageProxyType = model.NewString(tc.ProxyType)
   457  				cfg.ServiceSettings.ImageProxyOptions = model.NewString(tc.ProxyOptions)
   458  				cfg.ServiceSettings.ImageProxyURL = model.NewString(tc.ProxyURL)
   459  			})
   460  
   461  			post := &model.Post{
   462  				Id:      model.NewId(),
   463  				Message: "![foo](" + tc.ImageURL + ")",
   464  			}
   465  
   466  			list := model.NewPostList()
   467  			list.Posts[post.Id] = post
   468  
   469  			assert.Equal(t, "![foo]("+tc.ProxiedImageURL+")", th.App.PostListWithProxyAddedToImageURLs(list).Posts[post.Id].Message)
   470  			assert.Equal(t, "![foo]("+tc.ProxiedImageURL+")", th.App.PostWithProxyAddedToImageURLs(post).Message)
   471  
   472  			assert.Equal(t, "![foo]("+tc.ImageURL+")", th.App.PostWithProxyRemovedFromImageURLs(post).Message)
   473  			post.Message = "![foo](" + tc.ProxiedImageURL + ")"
   474  			assert.Equal(t, "![foo]("+tc.ImageURL+")", th.App.PostWithProxyRemovedFromImageURLs(post).Message)
   475  
   476  			if tc.ImageURL != "" {
   477  				post.Message = "![foo](" + tc.ImageURL + " =500x200)"
   478  				assert.Equal(t, "![foo]("+tc.ProxiedImageURL+" =500x200)", th.App.PostWithProxyAddedToImageURLs(post).Message)
   479  				assert.Equal(t, "![foo]("+tc.ImageURL+" =500x200)", th.App.PostWithProxyRemovedFromImageURLs(post).Message)
   480  				post.Message = "![foo](" + tc.ProxiedImageURL + " =500x200)"
   481  				assert.Equal(t, "![foo]("+tc.ImageURL+" =500x200)", th.App.PostWithProxyRemovedFromImageURLs(post).Message)
   482  			}
   483  		})
   484  	}
   485  }
   486  
   487  func BenchmarkForceHTMLEncodingToUTF8(b *testing.B) {
   488  	HTML := `
   489  		<html>
   490  			<head>
   491  				<meta property="og:url" content="https://example.com/apps/mattermost">
   492  				<meta property="og:image" content="https://images.example.com/image.png">
   493  			</head>
   494  		</html>
   495  	`
   496  	ContentType := "text/html; utf-8"
   497  
   498  	b.Run("with converting", func(b *testing.B) {
   499  		for i := 0; i < b.N; i++ {
   500  			r := forceHTMLEncodingToUTF8(strings.NewReader(HTML), ContentType)
   501  
   502  			og := opengraph.NewOpenGraph()
   503  			og.ProcessHTML(r)
   504  		}
   505  	})
   506  
   507  	b.Run("without converting", func(b *testing.B) {
   508  		for i := 0; i < b.N; i++ {
   509  			og := opengraph.NewOpenGraph()
   510  			og.ProcessHTML(strings.NewReader(HTML))
   511  		}
   512  	})
   513  }
   514  
   515  func TestMakeOpenGraphURLsAbsolute(t *testing.T) {
   516  	for name, tc := range map[string]struct {
   517  		HTML       string
   518  		RequestURL string
   519  		URL        string
   520  		ImageURL   string
   521  	}{
   522  		"absolute URLs": {
   523  			HTML: `
   524  				<html>
   525  					<head>
   526  						<meta property="og:url" content="https://example.com/apps/mattermost">
   527  						<meta property="og:image" content="https://images.example.com/image.png">
   528  					</head>
   529  				</html>`,
   530  			RequestURL: "https://example.com",
   531  			URL:        "https://example.com/apps/mattermost",
   532  			ImageURL:   "https://images.example.com/image.png",
   533  		},
   534  		"URLs starting with /": {
   535  			HTML: `
   536  				<html>
   537  					<head>
   538  						<meta property="og:url" content="/apps/mattermost">
   539  						<meta property="og:image" content="/image.png">
   540  					</head>
   541  				</html>`,
   542  			RequestURL: "http://example.com",
   543  			URL:        "http://example.com/apps/mattermost",
   544  			ImageURL:   "http://example.com/image.png",
   545  		},
   546  		"HTTPS URLs starting with /": {
   547  			HTML: `
   548  				<html>
   549  					<head>
   550  						<meta property="og:url" content="/apps/mattermost">
   551  						<meta property="og:image" content="/image.png">
   552  					</head>
   553  				</html>`,
   554  			RequestURL: "https://example.com",
   555  			URL:        "https://example.com/apps/mattermost",
   556  			ImageURL:   "https://example.com/image.png",
   557  		},
   558  		"missing image URL": {
   559  			HTML: `
   560  				<html>
   561  					<head>
   562  						<meta property="og:url" content="/apps/mattermost">
   563  					</head>
   564  				</html>`,
   565  			RequestURL: "http://example.com",
   566  			URL:        "http://example.com/apps/mattermost",
   567  			ImageURL:   "",
   568  		},
   569  		"relative URLs": {
   570  			HTML: `
   571  				<html>
   572  					<head>
   573  						<meta property="og:url" content="index.html">
   574  						<meta property="og:image" content="../resources/image.png">
   575  					</head>
   576  				</html>`,
   577  			RequestURL: "http://example.com/content/index.html",
   578  			URL:        "http://example.com/content/index.html",
   579  			ImageURL:   "http://example.com/resources/image.png",
   580  		},
   581  	} {
   582  		t.Run(name, func(t *testing.T) {
   583  			og := opengraph.NewOpenGraph()
   584  			if err := og.ProcessHTML(strings.NewReader(tc.HTML)); err != nil {
   585  				t.Fatal(err)
   586  			}
   587  
   588  			makeOpenGraphURLsAbsolute(og, tc.RequestURL)
   589  
   590  			if og.URL != tc.URL {
   591  				t.Fatalf("incorrect url, expected %v, got %v", tc.URL, og.URL)
   592  			}
   593  
   594  			if len(og.Images) > 0 {
   595  				if og.Images[0].URL != tc.ImageURL {
   596  					t.Fatalf("incorrect image url, expected %v, got %v", tc.ImageURL, og.Images[0].URL)
   597  				}
   598  			} else if tc.ImageURL != "" {
   599  				t.Fatalf("missing image url, expected %v, got nothing", tc.ImageURL)
   600  			}
   601  		})
   602  	}
   603  }
   604  
   605  func TestMaxPostSize(t *testing.T) {
   606  	t.Parallel()
   607  
   608  	testCases := []struct {
   609  		Description         string
   610  		StoreMaxPostSize    int
   611  		ExpectedMaxPostSize int
   612  		ExpectedError       *model.AppError
   613  	}{
   614  		{
   615  			"error fetching max post size",
   616  			0,
   617  			model.POST_MESSAGE_MAX_RUNES_V1,
   618  			model.NewAppError("TestMaxPostSize", "this is an error", nil, "", http.StatusBadRequest),
   619  		},
   620  		{
   621  			"4000 rune limit",
   622  			4000,
   623  			4000,
   624  			nil,
   625  		},
   626  		{
   627  			"16383 rune limit",
   628  			16383,
   629  			16383,
   630  			nil,
   631  		},
   632  	}
   633  
   634  	for _, testCase := range testCases {
   635  		testCase := testCase
   636  		t.Run(testCase.Description, func(t *testing.T) {
   637  			t.Parallel()
   638  
   639  			mockStore := &storetest.Store{}
   640  			defer mockStore.AssertExpectations(t)
   641  
   642  			mockStore.PostStore.On("GetMaxPostSize").Return(
   643  				storetest.NewStoreChannel(store.StoreResult{
   644  					Data: testCase.StoreMaxPostSize,
   645  					Err:  testCase.ExpectedError,
   646  				}),
   647  			)
   648  
   649  			app := App{
   650  				Srv: &Server{
   651  					Store: mockStore,
   652  				},
   653  				config: atomic.Value{},
   654  			}
   655  
   656  			assert.Equal(t, testCase.ExpectedMaxPostSize, app.MaxPostSize())
   657  		})
   658  	}
   659  }