github.com/mattermosttest/mattermost-server/v5@v5.0.0-20200917143240-9dfa12e121f9/model/link_metadata_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  	"encoding/json"
     8  	"fmt"
     9  	"strings"
    10  	"testing"
    11  	"unicode/utf8"
    12  
    13  	"github.com/dyatlov/go-opengraph/opengraph"
    14  	"github.com/stretchr/testify/assert"
    15  	"github.com/stretchr/testify/require"
    16  )
    17  
    18  const BigText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus maximus faucibus ex, vitae placerat neque feugiat ac. Nam tempus libero quis pellentesque feugiat. Cras tristique diam vel condimentum viverra. Proin molestie posuere leo. Nam pulvinar, ex quis tristique cursus, turpis ante commodo elit, a dapibus est ipsum id eros. Mauris tortor dolor, posuere ac velit vitae, faucibus viverra fusce."
    19  
    20  func sampleImage(imageName string) *opengraph.Image {
    21  	return &opengraph.Image{
    22  		URL:       fmt.Sprintf("http://example.com/%s", imageName),
    23  		SecureURL: fmt.Sprintf("https://example.com/%s", imageName),
    24  		Type:      "png",
    25  		Width:     32,
    26  		Height:    32,
    27  	}
    28  }
    29  
    30  func TestLinkMetadataIsValid(t *testing.T) {
    31  	for _, test := range []struct {
    32  		Name     string
    33  		Metadata *LinkMetadata
    34  		Expected bool
    35  	}{
    36  		{
    37  			Name: "should be valid image metadata",
    38  			Metadata: &LinkMetadata{
    39  				URL:       "http://example.com",
    40  				Timestamp: 1546300800000,
    41  				Type:      LINK_METADATA_TYPE_IMAGE,
    42  				Data:      &PostImage{},
    43  			},
    44  			Expected: true,
    45  		},
    46  		{
    47  			Name: "should be valid opengraph metadata",
    48  			Metadata: &LinkMetadata{
    49  				URL:       "http://example.com",
    50  				Timestamp: 1546300800000,
    51  				Type:      LINK_METADATA_TYPE_OPENGRAPH,
    52  				Data:      &opengraph.OpenGraph{},
    53  			},
    54  			Expected: true,
    55  		},
    56  		{
    57  			Name: "should be valid with no metadata",
    58  			Metadata: &LinkMetadata{
    59  				URL:       "http://example.com",
    60  				Timestamp: 1546300800000,
    61  				Type:      LINK_METADATA_TYPE_NONE,
    62  				Data:      nil,
    63  			},
    64  			Expected: true,
    65  		},
    66  		{
    67  			Name: "should be invalid because of empty URL",
    68  			Metadata: &LinkMetadata{
    69  				Timestamp: 1546300800000,
    70  				Type:      LINK_METADATA_TYPE_IMAGE,
    71  				Data:      &PostImage{},
    72  			},
    73  			Expected: false,
    74  		},
    75  		{
    76  			Name: "should be invalid because of empty timestamp",
    77  			Metadata: &LinkMetadata{
    78  				URL:  "http://example.com",
    79  				Type: LINK_METADATA_TYPE_IMAGE,
    80  				Data: &PostImage{},
    81  			},
    82  			Expected: false,
    83  		},
    84  		{
    85  			Name: "should be invalid because of unrounded timestamp",
    86  			Metadata: &LinkMetadata{
    87  				URL:       "http://example.com",
    88  				Timestamp: 1546300800001,
    89  				Type:      LINK_METADATA_TYPE_IMAGE,
    90  				Data:      &PostImage{},
    91  			},
    92  			Expected: false,
    93  		},
    94  		{
    95  			Name: "should be invalid because of invalid type",
    96  			Metadata: &LinkMetadata{
    97  				URL:       "http://example.com",
    98  				Timestamp: 1546300800000,
    99  				Type:      "garbage",
   100  				Data:      &PostImage{},
   101  			},
   102  			Expected: false,
   103  		},
   104  		{
   105  			Name: "should be invalid because of empty data",
   106  			Metadata: &LinkMetadata{
   107  				URL:       "http://example.com",
   108  				Timestamp: 1546300800000,
   109  				Type:      LINK_METADATA_TYPE_IMAGE,
   110  			},
   111  			Expected: false,
   112  		},
   113  		{
   114  			Name: "should be invalid because of mismatched data and type, image type and opengraph data",
   115  			Metadata: &LinkMetadata{
   116  				URL:       "http://example.com",
   117  				Timestamp: 1546300800000,
   118  				Type:      LINK_METADATA_TYPE_IMAGE,
   119  				Data:      &opengraph.OpenGraph{},
   120  			},
   121  			Expected: false,
   122  		},
   123  		{
   124  			Name: "should be invalid because of mismatched data and type, opengraph type and image data",
   125  			Metadata: &LinkMetadata{
   126  				URL:       "http://example.com",
   127  				Timestamp: 1546300800000,
   128  				Type:      LINK_METADATA_TYPE_OPENGRAPH,
   129  				Data:      &PostImage{},
   130  			},
   131  			Expected: false,
   132  		},
   133  		{
   134  			Name: "should be invalid because of mismatched data and type, image type and random data",
   135  			Metadata: &LinkMetadata{
   136  				URL:       "http://example.com",
   137  				Timestamp: 1546300800000,
   138  				Type:      LINK_METADATA_TYPE_OPENGRAPH,
   139  				Data:      &Channel{},
   140  			},
   141  			Expected: false,
   142  		},
   143  	} {
   144  		t.Run(test.Name, func(t *testing.T) {
   145  			err := test.Metadata.IsValid()
   146  
   147  			if test.Expected {
   148  				assert.Nil(t, err)
   149  			} else {
   150  				assert.NotNil(t, err)
   151  			}
   152  		})
   153  	}
   154  }
   155  
   156  func TestLinkMetadataDeserializeDataToConcreteType(t *testing.T) {
   157  	t.Run("should convert []byte to PostImage", func(t *testing.T) {
   158  		image := &PostImage{
   159  			Height: 400,
   160  			Width:  500,
   161  		}
   162  
   163  		metadata := &LinkMetadata{
   164  			Type: LINK_METADATA_TYPE_IMAGE,
   165  			Data: []byte(image.ToJson()),
   166  		}
   167  
   168  		require.IsType(t, []byte{}, metadata.Data)
   169  
   170  		err := metadata.DeserializeDataToConcreteType()
   171  
   172  		assert.Nil(t, err)
   173  		assert.IsType(t, &PostImage{}, metadata.Data)
   174  		assert.Equal(t, *image, *metadata.Data.(*PostImage))
   175  	})
   176  
   177  	t.Run("should convert string to OpenGraph", func(t *testing.T) {
   178  		og := &opengraph.OpenGraph{
   179  			URL:         "http://example.com",
   180  			Description: "Hello, world!",
   181  			Images: []*opengraph.Image{
   182  				{
   183  					URL: "http://example.com/image.png",
   184  				},
   185  			},
   186  		}
   187  
   188  		b, err := json.Marshal(og)
   189  
   190  		require.Nil(t, err)
   191  
   192  		metadata := &LinkMetadata{
   193  			Type: LINK_METADATA_TYPE_OPENGRAPH,
   194  			Data: b,
   195  		}
   196  
   197  		require.IsType(t, []byte{}, metadata.Data)
   198  
   199  		err = metadata.DeserializeDataToConcreteType()
   200  
   201  		assert.Nil(t, err)
   202  		assert.IsType(t, &opengraph.OpenGraph{}, metadata.Data)
   203  		assert.Equal(t, *og, *metadata.Data.(*opengraph.OpenGraph))
   204  	})
   205  
   206  	t.Run("should ignore data of the correct type", func(t *testing.T) {
   207  		metadata := &LinkMetadata{
   208  			Type: LINK_METADATA_TYPE_OPENGRAPH,
   209  			Data: 1234,
   210  		}
   211  
   212  		err := metadata.DeserializeDataToConcreteType()
   213  
   214  		assert.Nil(t, err)
   215  	})
   216  
   217  	t.Run("should ignore an invalid type", func(t *testing.T) {
   218  		metadata := &LinkMetadata{
   219  			Type: "garbage",
   220  			Data: "garbage",
   221  		}
   222  
   223  		err := metadata.DeserializeDataToConcreteType()
   224  
   225  		assert.Nil(t, err)
   226  	})
   227  
   228  	t.Run("should return error for invalid data", func(t *testing.T) {
   229  		metadata := &LinkMetadata{
   230  			Type: LINK_METADATA_TYPE_IMAGE,
   231  			Data: "garbage",
   232  		}
   233  
   234  		err := metadata.DeserializeDataToConcreteType()
   235  
   236  		assert.NotNil(t, err)
   237  	})
   238  }
   239  
   240  func TestFloorToNearestHour(t *testing.T) {
   241  	assert.True(t, isRoundedToNearestHour(FloorToNearestHour(1546346096000)))
   242  }
   243  
   244  func TestTruncateText(t *testing.T) {
   245  	t.Run("Shouldn't affect strings smaller than 300 characters", func(t *testing.T) {
   246  		assert.Equal(t, utf8.RuneCountInString(truncateText("abc")), 3, "should be 3")
   247  	})
   248  	t.Run("Shouldn't affect empty strings", func(t *testing.T) {
   249  		assert.Equal(t, utf8.RuneCountInString(truncateText("")), 0, "should be empty")
   250  	})
   251  	t.Run("Truncates string to 300 + 5", func(t *testing.T) {
   252  		assert.Equal(t, utf8.RuneCountInString(truncateText(BigText)), 305, "should be 300 chars + 5")
   253  	})
   254  	t.Run("Truncated text ends in elipsis", func(t *testing.T) {
   255  		assert.True(t, strings.HasSuffix(truncateText(BigText), "[...]"))
   256  	})
   257  }
   258  
   259  func TestFirstNImages(t *testing.T) {
   260  	t.Run("when empty, return an empty one", func(t *testing.T) {
   261  		empty := make([]*opengraph.Image, 0)
   262  		assert.Exactly(t, firstNImages(empty, 1), empty, "Should be the same element")
   263  	})
   264  	t.Run("when it contains one element, return the same array", func(t *testing.T) {
   265  		one := []*opengraph.Image{sampleImage("image.png")}
   266  		assert.Exactly(t, firstNImages(one, 1), one, "Should be the same element")
   267  	})
   268  	t.Run("when it contains more than one element and asking for only one, return the first one", func(t *testing.T) {
   269  		two := []*opengraph.Image{sampleImage("image.png"), sampleImage("notme.png")}
   270  		assert.True(t, strings.HasSuffix(firstNImages(two, 1)[0].URL, "image.png"), "Should be the image element")
   271  	})
   272  	t.Run("when it contains less than asked, return the original", func(t *testing.T) {
   273  		two := []*opengraph.Image{sampleImage("image.png"), sampleImage("notme.png")}
   274  		assert.Equal(t, two, firstNImages(two, 10), "should be the same pointer")
   275  	})
   276  
   277  	t.Run("asking for negative images", func(t *testing.T) {
   278  		six := []*opengraph.Image{
   279  			sampleImage("image.png"),
   280  			sampleImage("another.png"),
   281  			sampleImage("yetanother.jpg"),
   282  			sampleImage("metoo.gif"),
   283  			sampleImage("fifth.ico"),
   284  			sampleImage("notme.tiff"),
   285  		}
   286  		assert.Len(t, firstNImages(six, -10), MAX_IMAGES, "On negative, go for defaults")
   287  	})
   288  
   289  }
   290  
   291  func TestTruncateOpenGraph(t *testing.T) {
   292  	og := opengraph.OpenGraph{
   293  		Type:             "something",
   294  		URL:              "http://myawesomesite.com",
   295  		Title:            BigText,
   296  		Description:      BigText,
   297  		Determiner:       BigText,
   298  		SiteName:         BigText,
   299  		Locale:           "[EN-en]",
   300  		LocalesAlternate: []string{"[EN-ca]", "[ES-es]"},
   301  		Images: []*opengraph.Image{
   302  			sampleImage("image.png"),
   303  			sampleImage("another.png"),
   304  			sampleImage("yetanother.jpg"),
   305  			sampleImage("metoo.gif"),
   306  			sampleImage("fifth.ico"),
   307  			sampleImage("notme.tiff")},
   308  		Audios:  []*opengraph.Audio{{}},
   309  		Videos:  []*opengraph.Video{{}},
   310  		Article: &opengraph.Article{},
   311  		Book:    &opengraph.Book{},
   312  		Profile: &opengraph.Profile{},
   313  	}
   314  	result := TruncateOpenGraph(&og)
   315  	assert.Nil(t, result.Article, "No article stored")
   316  	assert.Nil(t, result.Book, "No book stored")
   317  	assert.Nil(t, result.Profile, "No profile stored")
   318  	assert.Len(t, result.Images, 5, "Only the first 5 images")
   319  	assert.Empty(t, result.Audios, "No audios stored")
   320  	assert.Empty(t, result.Videos, "No videos stored")
   321  	assert.Empty(t, result.LocalesAlternate, "No alternate locales stored")
   322  	assert.Equal(t, result.Determiner, "", "No determiner stored")
   323  	assert.Equal(t, utf8.RuneCountInString(result.Title), 305, "Title text is truncated")
   324  	assert.Equal(t, utf8.RuneCountInString(result.Description), 305, "Description text is truncated")
   325  	assert.Equal(t, utf8.RuneCountInString(result.SiteName), 305, "SiteName text is truncated")
   326  }