github.com/mattermosttest/mattermost-server/v5@v5.0.0-20200917143240-9dfa12e121f9/model/post_test.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See LICENSE.txt for license information.
     3  
     4  package model
     5  
     6  import (
     7  	"io/ioutil"
     8  	"strings"
     9  	"sync"
    10  	"testing"
    11  
    12  	"github.com/stretchr/testify/assert"
    13  	"github.com/stretchr/testify/require"
    14  )
    15  
    16  func TestPostToJson(t *testing.T) {
    17  	o := Post{Id: NewId(), Message: NewId()}
    18  	j := o.ToJson()
    19  	ro := PostFromJson(strings.NewReader(j))
    20  
    21  	assert.NotNil(t, ro)
    22  	assert.Equal(t, &o, ro.Clone())
    23  }
    24  
    25  func TestPostFromJsonError(t *testing.T) {
    26  	ro := PostFromJson(strings.NewReader(""))
    27  	assert.Nil(t, ro)
    28  }
    29  
    30  func TestPostIsValid(t *testing.T) {
    31  	o := Post{}
    32  	maxPostSize := 10000
    33  
    34  	err := o.IsValid(maxPostSize)
    35  	require.NotNil(t, err)
    36  
    37  	o.Id = NewId()
    38  	err = o.IsValid(maxPostSize)
    39  	require.NotNil(t, err)
    40  
    41  	o.CreateAt = GetMillis()
    42  	err = o.IsValid(maxPostSize)
    43  	require.NotNil(t, err)
    44  
    45  	o.UpdateAt = GetMillis()
    46  	err = o.IsValid(maxPostSize)
    47  	require.NotNil(t, err)
    48  
    49  	o.UserId = NewId()
    50  	err = o.IsValid(maxPostSize)
    51  	require.NotNil(t, err)
    52  
    53  	o.ChannelId = NewId()
    54  	o.RootId = "123"
    55  	err = o.IsValid(maxPostSize)
    56  	require.NotNil(t, err)
    57  
    58  	o.RootId = ""
    59  	o.ParentId = "123"
    60  	err = o.IsValid(maxPostSize)
    61  	require.NotNil(t, err)
    62  
    63  	o.ParentId = NewId()
    64  	o.RootId = ""
    65  	err = o.IsValid(maxPostSize)
    66  	require.NotNil(t, err)
    67  
    68  	o.ParentId = ""
    69  	o.Message = strings.Repeat("0", maxPostSize+1)
    70  	err = o.IsValid(maxPostSize)
    71  	require.NotNil(t, err)
    72  
    73  	o.Message = strings.Repeat("0", maxPostSize)
    74  	err = o.IsValid(maxPostSize)
    75  	require.Nil(t, err)
    76  
    77  	o.Message = "test"
    78  	err = o.IsValid(maxPostSize)
    79  	require.Nil(t, err)
    80  	o.Type = "junk"
    81  	err = o.IsValid(maxPostSize)
    82  	require.NotNil(t, err)
    83  
    84  	o.Type = POST_CUSTOM_TYPE_PREFIX + "type"
    85  	err = o.IsValid(maxPostSize)
    86  	require.Nil(t, err)
    87  }
    88  
    89  func TestPostPreSave(t *testing.T) {
    90  	o := Post{Message: "test"}
    91  	o.PreSave()
    92  
    93  	require.NotEqual(t, 0, o.CreateAt)
    94  
    95  	past := GetMillis() - 1
    96  	o = Post{Message: "test", CreateAt: past}
    97  	o.PreSave()
    98  
    99  	require.LessOrEqual(t, o.CreateAt, past)
   100  
   101  	o.Etag()
   102  }
   103  
   104  func TestPostIsSystemMessage(t *testing.T) {
   105  	post1 := Post{Message: "test_1"}
   106  	post1.PreSave()
   107  
   108  	require.False(t, post1.IsSystemMessage())
   109  
   110  	post2 := Post{Message: "test_2", Type: POST_JOIN_LEAVE}
   111  	post2.PreSave()
   112  
   113  	require.True(t, post2.IsSystemMessage())
   114  }
   115  
   116  func TestPostChannelMentions(t *testing.T) {
   117  	post := Post{Message: "~a ~b ~b ~c/~d."}
   118  	assert.Equal(t, []string{"a", "b", "c", "d"}, post.ChannelMentions())
   119  }
   120  
   121  func TestPostSanitizeProps(t *testing.T) {
   122  	post1 := &Post{
   123  		Message: "test",
   124  	}
   125  
   126  	post1.SanitizeProps()
   127  
   128  	require.Nil(t, post1.GetProp(PROPS_ADD_CHANNEL_MEMBER))
   129  
   130  	post2 := &Post{
   131  		Message: "test",
   132  		Props: StringInterface{
   133  			PROPS_ADD_CHANNEL_MEMBER: "test",
   134  		},
   135  	}
   136  
   137  	post2.SanitizeProps()
   138  
   139  	require.Nil(t, post2.GetProp(PROPS_ADD_CHANNEL_MEMBER))
   140  
   141  	post3 := &Post{
   142  		Message: "test",
   143  		Props: StringInterface{
   144  			PROPS_ADD_CHANNEL_MEMBER: "no good",
   145  			"attachments":            "good",
   146  		},
   147  	}
   148  
   149  	post3.SanitizeProps()
   150  
   151  	require.Nil(t, post3.GetProp(PROPS_ADD_CHANNEL_MEMBER))
   152  
   153  	require.NotNil(t, post3.GetProp("attachments"))
   154  }
   155  
   156  func TestPost_AttachmentsEqual(t *testing.T) {
   157  	post1 := &Post{}
   158  	post2 := &Post{}
   159  	for name, tc := range map[string]struct {
   160  		Attachments1 []*SlackAttachment
   161  		Attachments2 []*SlackAttachment
   162  		Expected     bool
   163  	}{
   164  		"Empty": {
   165  			nil,
   166  			nil,
   167  			true,
   168  		},
   169  		"DifferentLength": {
   170  			[]*SlackAttachment{
   171  				{
   172  					Text: "Hello World",
   173  				},
   174  			},
   175  			nil,
   176  			false,
   177  		},
   178  		"EqualText": {
   179  			[]*SlackAttachment{
   180  				{
   181  					Text: "Hello World",
   182  				},
   183  			},
   184  			[]*SlackAttachment{
   185  				{
   186  					Text: "Hello World",
   187  				},
   188  			},
   189  			true,
   190  		},
   191  		"DifferentText": {
   192  			[]*SlackAttachment{
   193  				{
   194  					Text: "Hello World",
   195  				},
   196  			},
   197  			[]*SlackAttachment{
   198  				{
   199  					Text: "Hello World 2",
   200  				},
   201  			},
   202  			false,
   203  		},
   204  		"DifferentColor": {
   205  			[]*SlackAttachment{
   206  				{
   207  					Text:  "Hello World",
   208  					Color: "#152313",
   209  				},
   210  			},
   211  			[]*SlackAttachment{
   212  				{
   213  					Text: "Hello World 2",
   214  				},
   215  			},
   216  			false,
   217  		},
   218  		"EqualFields": {
   219  			[]*SlackAttachment{
   220  				{
   221  					Fields: []*SlackAttachmentField{
   222  						{
   223  							Title: "Hello World",
   224  							Value: "FooBar",
   225  						},
   226  						{
   227  							Title: "Hello World2",
   228  							Value: "FooBar2",
   229  						},
   230  					},
   231  				},
   232  			},
   233  			[]*SlackAttachment{
   234  				{
   235  					Fields: []*SlackAttachmentField{
   236  						{
   237  							Title: "Hello World",
   238  							Value: "FooBar",
   239  						},
   240  						{
   241  							Title: "Hello World2",
   242  							Value: "FooBar2",
   243  						},
   244  					},
   245  				},
   246  			},
   247  			true,
   248  		},
   249  		"DifferentFields": {
   250  			[]*SlackAttachment{
   251  				{
   252  					Fields: []*SlackAttachmentField{
   253  						{
   254  							Title: "Hello World",
   255  							Value: "FooBar",
   256  						},
   257  					},
   258  				},
   259  			},
   260  			[]*SlackAttachment{
   261  				{
   262  					Fields: []*SlackAttachmentField{
   263  						{
   264  							Title: "Hello World",
   265  							Value: "FooBar",
   266  							Short: false,
   267  						},
   268  						{
   269  							Title: "Hello World2",
   270  							Value: "FooBar2",
   271  							Short: true,
   272  						},
   273  					},
   274  				},
   275  			},
   276  			false,
   277  		},
   278  		"EqualActions": {
   279  			[]*SlackAttachment{
   280  				{
   281  					Actions: []*PostAction{
   282  						{
   283  							Name: "FooBar",
   284  							Options: []*PostActionOptions{
   285  								{
   286  									Text:  "abcdef",
   287  									Value: "abcdef",
   288  								},
   289  							},
   290  							Integration: &PostActionIntegration{
   291  								URL: "http://localhost",
   292  								Context: map[string]interface{}{
   293  									"context": "foobar",
   294  									"test":    123,
   295  								},
   296  							},
   297  						},
   298  					},
   299  				},
   300  			},
   301  			[]*SlackAttachment{
   302  				{
   303  					Actions: []*PostAction{
   304  						{
   305  							Name: "FooBar",
   306  							Options: []*PostActionOptions{
   307  								{
   308  									Text:  "abcdef",
   309  									Value: "abcdef",
   310  								},
   311  							},
   312  							Integration: &PostActionIntegration{
   313  								URL: "http://localhost",
   314  								Context: map[string]interface{}{
   315  									"context": "foobar",
   316  									"test":    123,
   317  								},
   318  							},
   319  						},
   320  					},
   321  				},
   322  			},
   323  			true,
   324  		},
   325  		"DifferentActions": {
   326  			[]*SlackAttachment{
   327  				{
   328  					Actions: []*PostAction{
   329  						{
   330  							Name: "FooBar",
   331  							Options: []*PostActionOptions{
   332  								{
   333  									Text:  "abcdef",
   334  									Value: "abcdef",
   335  								},
   336  							},
   337  							Integration: &PostActionIntegration{
   338  								URL: "http://localhost",
   339  								Context: map[string]interface{}{
   340  									"context": "foobar",
   341  									"test":    "mattermost",
   342  								},
   343  							},
   344  						},
   345  					},
   346  				},
   347  			},
   348  			[]*SlackAttachment{
   349  				{
   350  					Actions: []*PostAction{
   351  						{
   352  							Name: "FooBar",
   353  							Options: []*PostActionOptions{
   354  								{
   355  									Text:  "abcdef",
   356  									Value: "abcdef",
   357  								},
   358  							},
   359  							Integration: &PostActionIntegration{
   360  								URL: "http://localhost",
   361  								Context: map[string]interface{}{
   362  									"context": "foobar",
   363  									"test":    123,
   364  								},
   365  							},
   366  						},
   367  					},
   368  				},
   369  			},
   370  			false,
   371  		},
   372  	} {
   373  		t.Run(name, func(t *testing.T) {
   374  			post1.AddProp("attachments", tc.Attachments1)
   375  			post2.AddProp("attachments", tc.Attachments2)
   376  			assert.Equal(t, tc.Expected, post1.AttachmentsEqual(post2))
   377  		})
   378  	}
   379  }
   380  
   381  var markdownSample, markdownSampleWithRewrittenImageURLs string
   382  
   383  func init() {
   384  	bytes, err := ioutil.ReadFile("testdata/markdown-sample.md")
   385  	if err != nil {
   386  		panic(err)
   387  	}
   388  	markdownSample = string(bytes)
   389  
   390  	bytes, err = ioutil.ReadFile("testdata/markdown-sample-with-rewritten-image-urls.md")
   391  	if err != nil {
   392  		panic(err)
   393  	}
   394  	markdownSampleWithRewrittenImageURLs = string(bytes)
   395  }
   396  
   397  func TestRewriteImageURLs(t *testing.T) {
   398  	for name, tc := range map[string]struct {
   399  		Markdown string
   400  		Expected string
   401  	}{
   402  		"Empty": {
   403  			Markdown: ``,
   404  			Expected: ``,
   405  		},
   406  		"NoImages": {
   407  			Markdown: `foo`,
   408  			Expected: `foo`,
   409  		},
   410  		"Link": {
   411  			Markdown: `[foo](/url)`,
   412  			Expected: `[foo](/url)`,
   413  		},
   414  		"Image": {
   415  			Markdown: `![foo](/url)`,
   416  			Expected: `![foo](rewritten:/url)`,
   417  		},
   418  		"SpacedURL": {
   419  			Markdown: `![foo]( /url )`,
   420  			Expected: `![foo]( rewritten:/url )`,
   421  		},
   422  		"Title": {
   423  			Markdown: `![foo](/url "title")`,
   424  			Expected: `![foo](rewritten:/url "title")`,
   425  		},
   426  		"Parentheses": {
   427  			Markdown: `![foo](/url(1) "title")`,
   428  			Expected: `![foo](rewritten:/url\(1\) "title")`,
   429  		},
   430  		"AngleBrackets": {
   431  			Markdown: `![foo](</url\<1\>\\> "title")`,
   432  			Expected: `![foo](<rewritten:/url\<1\>\\> "title")`,
   433  		},
   434  		"MultipleLines": {
   435  			Markdown: `![foo](
   436  				</url\<1\>\\>
   437  				"title"
   438  			)`,
   439  			Expected: `![foo](
   440  				<rewritten:/url\<1\>\\>
   441  				"title"
   442  			)`,
   443  		},
   444  		"ReferenceLink": {
   445  			Markdown: `[foo]: </url\<1\>\\> "title"
   446  		 		[foo]`,
   447  			Expected: `[foo]: </url\<1\>\\> "title"
   448  		 		[foo]`,
   449  		},
   450  		"ReferenceImage": {
   451  			Markdown: `[foo]: </url\<1\>\\> "title"
   452  		 		![foo]`,
   453  			Expected: `[foo]: <rewritten:/url\<1\>\\> "title"
   454  		 		![foo]`,
   455  		},
   456  		"MultipleReferenceImages": {
   457  			Markdown: `[foo]: </url1> "title"
   458  				[bar]: </url2>
   459  				[baz]: /url3 "title"
   460  				[qux]: /url4
   461  				![foo]![qux]`,
   462  			Expected: `[foo]: <rewritten:/url1> "title"
   463  				[bar]: </url2>
   464  				[baz]: /url3 "title"
   465  				[qux]: rewritten:/url4
   466  				![foo]![qux]`,
   467  		},
   468  		"DuplicateReferences": {
   469  			Markdown: `[foo]: </url1> "title"
   470  				[foo]: </url2>
   471  				[foo]: /url3 "title"
   472  				[foo]: /url4
   473  				![foo]![foo]![foo]`,
   474  			Expected: `[foo]: <rewritten:/url1> "title"
   475  				[foo]: </url2>
   476  				[foo]: /url3 "title"
   477  				[foo]: /url4
   478  				![foo]![foo]![foo]`,
   479  		},
   480  		"TrailingURL": {
   481  			Markdown: "![foo]\n\n[foo]: /url",
   482  			Expected: "![foo]\n\n[foo]: rewritten:/url",
   483  		},
   484  		"Sample": {
   485  			Markdown: markdownSample,
   486  			Expected: markdownSampleWithRewrittenImageURLs,
   487  		},
   488  	} {
   489  		t.Run(name, func(t *testing.T) {
   490  			assert.Equal(t, tc.Expected, RewriteImageURLs(tc.Markdown, func(url string) string {
   491  				return "rewritten:" + url
   492  			}))
   493  		})
   494  	}
   495  }
   496  
   497  var rewriteImageURLsSink string
   498  
   499  func BenchmarkRewriteImageURLs(b *testing.B) {
   500  	for i := 0; i < b.N; i++ {
   501  		rewriteImageURLsSink = RewriteImageURLs(markdownSample, func(url string) string {
   502  			return "rewritten:" + url
   503  		})
   504  	}
   505  }
   506  
   507  func TestPostShallowCopy(t *testing.T) {
   508  	var dst *Post
   509  	p := &Post{
   510  		Id: NewId(),
   511  	}
   512  
   513  	err := p.ShallowCopy(dst)
   514  	require.Error(t, err)
   515  
   516  	dst = &Post{}
   517  	err = p.ShallowCopy(dst)
   518  	require.NoError(t, err)
   519  	require.Equal(t, p, dst)
   520  	require.Condition(t, func() bool {
   521  		return p != dst
   522  	})
   523  }
   524  
   525  func TestPostClone(t *testing.T) {
   526  	p := &Post{
   527  		Id: NewId(),
   528  	}
   529  
   530  	pp := p.Clone()
   531  	require.Equal(t, p, pp)
   532  	require.Condition(t, func() bool {
   533  		return p != pp
   534  	})
   535  	require.Condition(t, func() bool {
   536  		return &p.propsMu != &pp.propsMu
   537  	})
   538  }
   539  
   540  func BenchmarkClonePost(b *testing.B) {
   541  	p := Post{}
   542  	for i := 0; i < b.N; i++ {
   543  		_ = p.Clone()
   544  	}
   545  }
   546  
   547  func BenchmarkPostPropsGet_indirect(b *testing.B) {
   548  	p := Post{
   549  		Props: make(StringInterface),
   550  	}
   551  	for i := 0; i < b.N; i++ {
   552  		_ = p.GetProps()
   553  	}
   554  }
   555  
   556  func BenchmarkPostPropsGet_direct(b *testing.B) {
   557  	p := Post{
   558  		Props: make(StringInterface),
   559  	}
   560  	for i := 0; i < b.N; i++ {
   561  		_ = p.Props
   562  	}
   563  }
   564  
   565  func BenchmarkPostPropsAdd_indirect(b *testing.B) {
   566  	p := Post{
   567  		Props: make(StringInterface),
   568  	}
   569  	for i := 0; i < b.N; i++ {
   570  		p.AddProp("test", "somevalue")
   571  	}
   572  }
   573  
   574  func BenchmarkPostPropsAdd_direct(b *testing.B) {
   575  	p := Post{
   576  		Props: make(StringInterface),
   577  	}
   578  	for i := 0; i < b.N; i++ {
   579  		p.Props["test"] = "somevalue"
   580  	}
   581  }
   582  
   583  func BenchmarkPostPropsDel_indirect(b *testing.B) {
   584  	p := Post{
   585  		Props: make(StringInterface),
   586  	}
   587  	p.AddProp("test", "somevalue")
   588  	for i := 0; i < b.N; i++ {
   589  		p.DelProp("test")
   590  	}
   591  }
   592  
   593  func BenchmarkPostPropsDel_direct(b *testing.B) {
   594  	p := Post{
   595  		Props: make(StringInterface),
   596  	}
   597  	for i := 0; i < b.N; i++ {
   598  		delete(p.Props, "test")
   599  	}
   600  }
   601  
   602  func BenchmarkPostPropGet_direct(b *testing.B) {
   603  	p := Post{
   604  		Props: make(StringInterface),
   605  	}
   606  	p.Props["somekey"] = "somevalue"
   607  	for i := 0; i < b.N; i++ {
   608  		_ = p.Props["somekey"]
   609  	}
   610  }
   611  
   612  func BenchmarkPostPropGet_indirect(b *testing.B) {
   613  	p := Post{
   614  		Props: make(StringInterface),
   615  	}
   616  	p.Props["somekey"] = "somevalue"
   617  	for i := 0; i < b.N; i++ {
   618  		_ = p.GetProp("somekey")
   619  	}
   620  }
   621  
   622  // TestPostPropsDataRace tries to trigger data race conditions related to Post.Props.
   623  // It's meant to be run with the -race flag.
   624  func TestPostPropsDataRace(t *testing.T) {
   625  	p := Post{Message: "test"}
   626  
   627  	wg := sync.WaitGroup{}
   628  	wg.Add(7)
   629  
   630  	go func() {
   631  		for i := 0; i < 100; i++ {
   632  			p.AddProp("test", "test")
   633  		}
   634  		wg.Done()
   635  	}()
   636  
   637  	go func() {
   638  		for i := 0; i < 100; i++ {
   639  			_ = p.GetProp("test")
   640  		}
   641  		wg.Done()
   642  	}()
   643  
   644  	go func() {
   645  		for i := 0; i < 100; i++ {
   646  			p.AddProp("test", "test2")
   647  		}
   648  		wg.Done()
   649  	}()
   650  
   651  	go func() {
   652  		for i := 0; i < 100; i++ {
   653  			_ = p.GetProps()["test"]
   654  		}
   655  		wg.Done()
   656  	}()
   657  
   658  	go func() {
   659  		for i := 0; i < 100; i++ {
   660  			p.DelProp("test")
   661  		}
   662  		wg.Done()
   663  	}()
   664  
   665  	go func() {
   666  		for i := 0; i < 100; i++ {
   667  			p.SetProps(make(StringInterface))
   668  		}
   669  		wg.Done()
   670  	}()
   671  
   672  	go func() {
   673  		for i := 0; i < 100; i++ {
   674  			_ = p.Clone()
   675  		}
   676  		wg.Done()
   677  	}()
   678  
   679  	wg.Wait()
   680  }
   681  
   682  func Test_findAtChannelMention(t *testing.T) {
   683  	testCases := []struct {
   684  		Name    string
   685  		Message string
   686  		Mention string
   687  		Found   bool
   688  	}{
   689  		{
   690  			"Returns mention for @here wrapped by spaces",
   691  			"hi guys @here wrapped by spaces",
   692  			"@here",
   693  			true,
   694  		},
   695  		{
   696  			"Returns mention for @all wrapped by spaces",
   697  			"hi guys @all wrapped by spaces",
   698  			"@all",
   699  			true,
   700  		},
   701  		{
   702  			"Returns mention for @channel wrapped by spaces",
   703  			"hi guys @channel wrapped by spaces",
   704  			"@channel",
   705  			true,
   706  		},
   707  		{
   708  			"Returns mention for @here wrapped by dash",
   709  			"-@here-",
   710  			"@here",
   711  			true,
   712  		},
   713  		{
   714  			"Returns mention for @all wrapped by back tick",
   715  			"`@all`",
   716  			"@all",
   717  			true,
   718  		},
   719  		{
   720  			"Returns mention for @channel wrapped by tags",
   721  			"<@channel>",
   722  			"@channel",
   723  			true,
   724  		},
   725  		{
   726  			"Returns mention for @channel wrapped by asterisks",
   727  			"*@channel*",
   728  			"@channel",
   729  			true,
   730  		},
   731  		{
   732  			"Does not return mention when prefixed by letters",
   733  			"hi@channel",
   734  			"",
   735  			false,
   736  		},
   737  		{
   738  			"Does not return mention when suffixed by letters",
   739  			"hi @channelanotherword",
   740  			"",
   741  			false,
   742  		},
   743  		{
   744  			"Returns mention when prefixed by word ending in special character",
   745  			"hi-@channel",
   746  			"@channel",
   747  			true,
   748  		},
   749  		{
   750  			"Returns mention when suffixed by word starting in special character",
   751  			"hi @channel-guys",
   752  			"@channel",
   753  			true,
   754  		},
   755  	}
   756  	for _, tc := range testCases {
   757  		t.Run(tc.Name, func(t *testing.T) {
   758  			mention, found := findAtChannelMention(tc.Message)
   759  			assert.Equal(t, tc.Mention, mention)
   760  			assert.Equal(t, tc.Found, found)
   761  		})
   762  	}
   763  }
   764  
   765  func TestPostDisableMentionHighlights(t *testing.T) {
   766  	post := &Post{}
   767  
   768  	testCases := []struct {
   769  		Name            string
   770  		Message         string
   771  		ExpectedProps   StringInterface
   772  		ExpectedMention string
   773  	}{
   774  		{
   775  			"Does nothing for post with no mentions",
   776  			"Sample message with no mentions",
   777  			StringInterface(nil),
   778  			"",
   779  		},
   780  		{
   781  			"Sets POST_PROPS_MENTION_HIGHLIGHT_DISABLED and returns mention",
   782  			"Sample message with @here",
   783  			StringInterface{POST_PROPS_MENTION_HIGHLIGHT_DISABLED: true},
   784  			"@here",
   785  		},
   786  		{
   787  			"Sets POST_PROPS_MENTION_HIGHLIGHT_DISABLED and returns mention",
   788  			"Sample message with @channel",
   789  			StringInterface{POST_PROPS_MENTION_HIGHLIGHT_DISABLED: true},
   790  			"@channel",
   791  		},
   792  		{
   793  			"Sets POST_PROPS_MENTION_HIGHLIGHT_DISABLED and returns mention",
   794  			"Sample message with @all",
   795  			StringInterface{POST_PROPS_MENTION_HIGHLIGHT_DISABLED: true},
   796  			"@all",
   797  		},
   798  	}
   799  	for _, tc := range testCases {
   800  		t.Run(tc.Name, func(t *testing.T) {
   801  			post.Message = tc.Message
   802  			mention := post.DisableMentionHighlights()
   803  			assert.Equal(t, tc.ExpectedMention, mention)
   804  			assert.Equal(t, tc.ExpectedProps, post.Props)
   805  			post.Props = StringInterface{}
   806  		})
   807  	}
   808  }
   809  
   810  func TestPostPatchDisableMentionHighlights(t *testing.T) {
   811  	patch := &PostPatch{}
   812  
   813  	testCases := []struct {
   814  		Name          string
   815  		Message       string
   816  		ExpectedProps *StringInterface
   817  	}{
   818  		{
   819  			"Does nothing for post with no mentions",
   820  			"Sample message with no mentions",
   821  			nil,
   822  		},
   823  		{
   824  			"Sets POST_PROPS_MENTION_HIGHLIGHT_DISABLED",
   825  			"Sample message with @here",
   826  			&StringInterface{POST_PROPS_MENTION_HIGHLIGHT_DISABLED: true},
   827  		},
   828  		{
   829  			"Sets POST_PROPS_MENTION_HIGHLIGHT_DISABLED",
   830  			"Sample message with @channel",
   831  			&StringInterface{POST_PROPS_MENTION_HIGHLIGHT_DISABLED: true},
   832  		},
   833  		{
   834  			"Sets POST_PROPS_MENTION_HIGHLIGHT_DISABLED",
   835  			"Sample message with @all",
   836  			&StringInterface{POST_PROPS_MENTION_HIGHLIGHT_DISABLED: true},
   837  		},
   838  	}
   839  	for _, tc := range testCases {
   840  		t.Run(tc.Name, func(t *testing.T) {
   841  			patch.Message = &tc.Message
   842  			patch.DisableMentionHighlights()
   843  			if tc.ExpectedProps == nil {
   844  				assert.Nil(t, patch.Props)
   845  			} else {
   846  				assert.Equal(t, *tc.ExpectedProps, *patch.Props)
   847  			}
   848  			patch.Props = nil
   849  		})
   850  	}
   851  
   852  	t.Run("TestNilMessage", func(t *testing.T) {
   853  		patch.Message = nil
   854  		patch.DisableMentionHighlights()
   855  		// Useless assertion to prevent compiler elision.
   856  		assert.Nil(t, patch.Message)
   857  	})
   858  }