github.com/coincircle/mattermost-server@v4.8.1-0.20180321182714-9d701c704416+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  	"encoding/json"
     8  	"fmt"
     9  	"net/http"
    10  	"net/http/httptest"
    11  	"strings"
    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  )
    21  
    22  func TestUpdatePostEditAt(t *testing.T) {
    23  	th := Setup().InitBasic()
    24  	defer th.TearDown()
    25  
    26  	post := &model.Post{}
    27  	*post = *th.BasicPost
    28  
    29  	post.IsPinned = true
    30  	if saved, err := th.App.UpdatePost(post, true); err != nil {
    31  		t.Fatal(err)
    32  	} else if saved.EditAt != post.EditAt {
    33  		t.Fatal("shouldn't have updated post.EditAt when pinning post")
    34  
    35  		*post = *saved
    36  	}
    37  
    38  	time.Sleep(time.Millisecond * 100)
    39  
    40  	post.Message = model.NewId()
    41  	if saved, err := th.App.UpdatePost(post, true); err != nil {
    42  		t.Fatal(err)
    43  	} else if saved.EditAt == post.EditAt {
    44  		t.Fatal("should have updated post.EditAt when updating post message")
    45  	}
    46  }
    47  
    48  func TestPostReplyToPostWhereRootPosterLeftChannel(t *testing.T) {
    49  	// This test ensures that when replying to a root post made by a user who has since left the channel, the reply
    50  	// post completes successfully. This is a regression test for PLT-6523.
    51  	th := Setup().InitBasic()
    52  	defer th.TearDown()
    53  
    54  	channel := th.BasicChannel
    55  	userInChannel := th.BasicUser2
    56  	userNotInChannel := th.BasicUser
    57  	rootPost := th.BasicPost
    58  
    59  	if _, err := th.App.AddUserToChannel(userInChannel, channel); err != nil {
    60  		t.Fatal(err)
    61  	}
    62  
    63  	if err := th.App.RemoveUserFromChannel(userNotInChannel.Id, "", channel); err != nil {
    64  		t.Fatal(err)
    65  	}
    66  
    67  	replyPost := model.Post{
    68  		Message:       "asd",
    69  		ChannelId:     channel.Id,
    70  		RootId:        rootPost.Id,
    71  		ParentId:      rootPost.Id,
    72  		PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
    73  		UserId:        userInChannel.Id,
    74  		CreateAt:      0,
    75  	}
    76  
    77  	if _, err := th.App.CreatePostAsUser(&replyPost); err != nil {
    78  		t.Fatal(err)
    79  	}
    80  }
    81  
    82  func TestPostAction(t *testing.T) {
    83  	th := Setup().InitBasic()
    84  	defer th.TearDown()
    85  
    86  	th.App.UpdateConfig(func(cfg *model.Config) {
    87  		*cfg.ServiceSettings.AllowedUntrustedInternalConnections = "localhost 127.0.0.1"
    88  	})
    89  
    90  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    91  		var request model.PostActionIntegrationRequest
    92  		err := json.NewDecoder(r.Body).Decode(&request)
    93  		assert.NoError(t, err)
    94  		assert.Equal(t, request.UserId, th.BasicUser.Id)
    95  		assert.Equal(t, "foo", request.Context["s"])
    96  		assert.EqualValues(t, 3, request.Context["n"])
    97  		fmt.Fprintf(w, `{"update": {"message": "updated"}, "ephemeral_text": "foo"}`)
    98  	}))
    99  	defer ts.Close()
   100  
   101  	interactivePost := model.Post{
   102  		Message:       "Interactive post",
   103  		ChannelId:     th.BasicChannel.Id,
   104  		PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
   105  		UserId:        th.BasicUser.Id,
   106  		Props: model.StringInterface{
   107  			"attachments": []*model.SlackAttachment{
   108  				{
   109  					Text: "hello",
   110  					Actions: []*model.PostAction{
   111  						{
   112  							Integration: &model.PostActionIntegration{
   113  								Context: model.StringInterface{
   114  									"s": "foo",
   115  									"n": 3,
   116  								},
   117  								URL: ts.URL,
   118  							},
   119  							Name: "action",
   120  						},
   121  					},
   122  				},
   123  			},
   124  		},
   125  	}
   126  
   127  	post, err := th.App.CreatePostAsUser(&interactivePost)
   128  	require.Nil(t, err)
   129  
   130  	attachments, ok := post.Props["attachments"].([]*model.SlackAttachment)
   131  	require.True(t, ok)
   132  
   133  	require.NotEmpty(t, attachments[0].Actions)
   134  	require.NotEmpty(t, attachments[0].Actions[0].Id)
   135  
   136  	err = th.App.DoPostAction(post.Id, "notavalidid", th.BasicUser.Id)
   137  	require.NotNil(t, err)
   138  	assert.Equal(t, http.StatusNotFound, err.StatusCode)
   139  
   140  	err = th.App.DoPostAction(post.Id, attachments[0].Actions[0].Id, th.BasicUser.Id)
   141  	require.Nil(t, err)
   142  }
   143  
   144  func TestPostChannelMentions(t *testing.T) {
   145  	th := Setup().InitBasic()
   146  	defer th.TearDown()
   147  
   148  	channel := th.BasicChannel
   149  	user := th.BasicUser
   150  
   151  	channelToMention, err := th.App.CreateChannel(&model.Channel{
   152  		DisplayName: "Mention Test",
   153  		Name:        "mention-test",
   154  		Type:        model.CHANNEL_OPEN,
   155  		TeamId:      th.BasicTeam.Id,
   156  	}, false)
   157  	if err != nil {
   158  		t.Fatal(err.Error())
   159  	}
   160  	defer th.App.PermanentDeleteChannel(channelToMention)
   161  
   162  	_, err = th.App.AddUserToChannel(user, channel)
   163  	require.Nil(t, err)
   164  
   165  	post := &model.Post{
   166  		Message:       fmt.Sprintf("hello, ~%v!", channelToMention.Name),
   167  		ChannelId:     channel.Id,
   168  		PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
   169  		UserId:        user.Id,
   170  		CreateAt:      0,
   171  	}
   172  
   173  	result, err := th.App.CreatePostAsUser(post)
   174  	require.Nil(t, err)
   175  	assert.Equal(t, map[string]interface{}{
   176  		"mention-test": map[string]interface{}{
   177  			"display_name": "Mention Test",
   178  		},
   179  	}, result.Props["channel_mentions"])
   180  
   181  	post.Message = fmt.Sprintf("goodbye, ~%v!", channelToMention.Name)
   182  	result, err = th.App.UpdatePost(post, false)
   183  	require.Nil(t, err)
   184  	assert.Equal(t, map[string]interface{}{
   185  		"mention-test": map[string]interface{}{
   186  			"display_name": "Mention Test",
   187  		},
   188  	}, result.Props["channel_mentions"])
   189  }
   190  
   191  func TestImageProxy(t *testing.T) {
   192  	th := Setup().InitBasic()
   193  	defer th.TearDown()
   194  
   195  	th.App.UpdateConfig(func(cfg *model.Config) {
   196  		*cfg.ServiceSettings.SiteURL = "http://mymattermost.com"
   197  	})
   198  
   199  	for name, tc := range map[string]struct {
   200  		ProxyType       string
   201  		ProxyURL        string
   202  		ProxyOptions    string
   203  		ImageURL        string
   204  		ProxiedImageURL string
   205  	}{
   206  		"atmos/camo": {
   207  			ProxyType:       "atmos/camo",
   208  			ProxyURL:        "https://127.0.0.1",
   209  			ProxyOptions:    "foo",
   210  			ImageURL:        "http://mydomain.com/myimage",
   211  			ProxiedImageURL: "https://127.0.0.1/f8dace906d23689e8d5b12c3cefbedbf7b9b72f5/687474703a2f2f6d79646f6d61696e2e636f6d2f6d79696d616765",
   212  		},
   213  		"atmos/camo_SameSite": {
   214  			ProxyType:       "atmos/camo",
   215  			ProxyURL:        "https://127.0.0.1",
   216  			ProxyOptions:    "foo",
   217  			ImageURL:        "http://mymattermost.com/myimage",
   218  			ProxiedImageURL: "http://mymattermost.com/myimage",
   219  		},
   220  		"atmos/camo_PathOnly": {
   221  			ProxyType:       "atmos/camo",
   222  			ProxyURL:        "https://127.0.0.1",
   223  			ProxyOptions:    "foo",
   224  			ImageURL:        "/myimage",
   225  			ProxiedImageURL: "/myimage",
   226  		},
   227  		"atmos/camo_EmptyImageURL": {
   228  			ProxyType:       "atmos/camo",
   229  			ProxyURL:        "https://127.0.0.1",
   230  			ProxyOptions:    "foo",
   231  			ImageURL:        "",
   232  			ProxiedImageURL: "",
   233  		},
   234  	} {
   235  		t.Run(name, func(t *testing.T) {
   236  			th.App.UpdateConfig(func(cfg *model.Config) {
   237  				cfg.ServiceSettings.ImageProxyType = model.NewString(tc.ProxyType)
   238  				cfg.ServiceSettings.ImageProxyOptions = model.NewString(tc.ProxyOptions)
   239  				cfg.ServiceSettings.ImageProxyURL = model.NewString(tc.ProxyURL)
   240  			})
   241  
   242  			post := &model.Post{
   243  				Id:      model.NewId(),
   244  				Message: "![foo](" + tc.ImageURL + ")",
   245  			}
   246  
   247  			list := model.NewPostList()
   248  			list.Posts[post.Id] = post
   249  
   250  			assert.Equal(t, "![foo]("+tc.ProxiedImageURL+")", th.App.PostListWithProxyAddedToImageURLs(list).Posts[post.Id].Message)
   251  			assert.Equal(t, "![foo]("+tc.ProxiedImageURL+")", th.App.PostWithProxyAddedToImageURLs(post).Message)
   252  
   253  			assert.Equal(t, "![foo]("+tc.ImageURL+")", th.App.PostWithProxyRemovedFromImageURLs(post).Message)
   254  			post.Message = "![foo](" + tc.ProxiedImageURL + ")"
   255  			assert.Equal(t, "![foo]("+tc.ImageURL+")", th.App.PostWithProxyRemovedFromImageURLs(post).Message)
   256  		})
   257  	}
   258  }
   259  
   260  func TestMakeOpenGraphURLsAbsolute(t *testing.T) {
   261  	for name, tc := range map[string]struct {
   262  		HTML       string
   263  		RequestURL string
   264  		URL        string
   265  		ImageURL   string
   266  	}{
   267  		"absolute URLs": {
   268  			HTML: `
   269  				<html>
   270  					<head>
   271  						<meta property="og:url" content="https://example.com/apps/mattermost">
   272  						<meta property="og:image" content="https://images.example.com/image.png">
   273  					</head>
   274  				</html>`,
   275  			RequestURL: "https://example.com",
   276  			URL:        "https://example.com/apps/mattermost",
   277  			ImageURL:   "https://images.example.com/image.png",
   278  		},
   279  		"URLs starting with /": {
   280  			HTML: `
   281  				<html>
   282  					<head>
   283  						<meta property="og:url" content="/apps/mattermost">
   284  						<meta property="og:image" content="/image.png">
   285  					</head>
   286  				</html>`,
   287  			RequestURL: "http://example.com",
   288  			URL:        "http://example.com/apps/mattermost",
   289  			ImageURL:   "http://example.com/image.png",
   290  		},
   291  		"HTTPS URLs starting with /": {
   292  			HTML: `
   293  				<html>
   294  					<head>
   295  						<meta property="og:url" content="/apps/mattermost">
   296  						<meta property="og:image" content="/image.png">
   297  					</head>
   298  				</html>`,
   299  			RequestURL: "https://example.com",
   300  			URL:        "https://example.com/apps/mattermost",
   301  			ImageURL:   "https://example.com/image.png",
   302  		},
   303  		"missing image URL": {
   304  			HTML: `
   305  				<html>
   306  					<head>
   307  						<meta property="og:url" content="/apps/mattermost">
   308  					</head>
   309  				</html>`,
   310  			RequestURL: "http://example.com",
   311  			URL:        "http://example.com/apps/mattermost",
   312  			ImageURL:   "",
   313  		},
   314  		"relative URLs": {
   315  			HTML: `
   316  				<html>
   317  					<head>
   318  						<meta property="og:url" content="index.html">
   319  						<meta property="og:image" content="../resources/image.png">
   320  					</head>
   321  				</html>`,
   322  			RequestURL: "http://example.com/content/index.html",
   323  			URL:        "http://example.com/content/index.html",
   324  			ImageURL:   "http://example.com/resources/image.png",
   325  		},
   326  	} {
   327  		t.Run(name, func(t *testing.T) {
   328  			og := opengraph.NewOpenGraph()
   329  			if err := og.ProcessHTML(strings.NewReader(tc.HTML)); err != nil {
   330  				t.Fatal(err)
   331  			}
   332  
   333  			makeOpenGraphURLsAbsolute(og, tc.RequestURL)
   334  
   335  			if og.URL != tc.URL {
   336  				t.Fatalf("incorrect url, expected %v, got %v", tc.URL, og.URL)
   337  			}
   338  
   339  			if len(og.Images) > 0 {
   340  				if og.Images[0].URL != tc.ImageURL {
   341  					t.Fatalf("incorrect image url, expected %v, got %v", tc.ImageURL, og.Images[0].URL)
   342  				}
   343  			} else if tc.ImageURL != "" {
   344  				t.Fatalf("missing image url, expected %v, got nothing", tc.ImageURL)
   345  			}
   346  		})
   347  	}
   348  }