github.com/status-im/status-go@v1.1.0/protocol/common/message_test.go (about) 1 package common 2 3 import ( 4 "encoding/base64" 5 "encoding/json" 6 "io/ioutil" 7 "os" 8 "testing" 9 10 "github.com/stretchr/testify/require" 11 12 "github.com/status-im/status-go/eth-node/crypto" 13 "github.com/status-im/status-go/eth-node/types" 14 "github.com/status-im/status-go/protocol/protobuf" 15 ) 16 17 const expectedJPEG = "data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=" 18 const expectedAAC = "data:audio/aac;base64,//FQgBw//NoATGF2YzUyLjcwLjAAQniptokphEFCg5qs1v9fn48+qz1rfWNhwvz+CqB5dipmq3T2PlT1Ld6sPj+19fUt1C3NKV0KowiqohZVCrdf19WMatvV3YbIvAuy/q2RafA8UiZPmZY7DdmHZtP9ri25kedWSiMKQRt79ttlod55LkuX7/f7/f7/f7/YGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYHNqo8g5qs1v9fn48+qz1rfWNhwvz+CqAAAAAAAAAAAAAAAAAAAAAAABw//FQgCNf/CFXbUZfDKFRgsYlKDegtXJH9eLkT54uRM1ckDYDcXRzZGF6Kz5Yps5fTeLY6w7gclwly+0PJL3udY3PyekTFI65bdniF3OjvHeafzZfWTs0qRMSkdll1sbb4SNT5e8vX98ytot6jEZ0NhJi2pBVP/tKV2JMyo36n9uxR2tKR+FoLCsP4SVi49kmvaSCWm5bQD96OmVQA9Q40bqnOa7rT8j9N0TlK991XdcenGTLbyS6eUnN2U1ckf14uRPni5EzVyQAAAAAAAAAAx6Q1flBp+KH2LhgH2Xx+14QB2/jcizm6ngck4vB9DoH9/Vcb7E8Dy+D/1ii1pSPwsUUUXCSsXHsk17SBfKwn2uHr6QAAAAAAAHA//FQgBt//CF3VO1KFCFWcd/r04m+O0758/tXHUlvaqEK9lvhUZXEZMXKMV/LQ6B3/mOl/Mrfs6jpD2b7f+n4yt+tm2x5ZmnpD++dZo/V9VgblI3OW/s1b8qt0h1RBiIRIIYIYQIBeCM8yy7etkwt1JAajRSoZGwwNZ07TTFTyMR1mTUVVUTW97vaDaHU5DV1snBf0mN4fraa+rf/vpdZ8FxqatGjNxPh35UuVfpNqc48W4nZ6rOO/16cTfHad8+f2rjqS3tVAAAAAAAAAAAAAAAAAAAAAAAAAAAO//FQgBm//CEXVPU+GiFsPr7x6+N6v+m+q511I4SgtYVyoyWjcMWMxkaxxDGSx1qVcarjDESt8zLQehx/lkil/GrHBy/NfJcHek0XtfanZJLHNXO2rUnFklPAlQSBS4l0pIoXIfORcXx0UYj1nTsSe1/0wXDkkFCfxWHtqRayOmWm3oS6JGdnZdtjesjByefiS8dLW1tVVVC58ijoxN3gmGFYj07+YJ6eth9fePXxvV/031XOupHCUAAAAAAAAAAAAAAAAAAAAAAAAAAA4P/xUIAcf/whN1T9NsMOEK5rxxxxXnid+f0/Ia195vi6oGH1ZVr6kjqScdSF9lt3qXH+Lxf0fo/Oe53r99IUPzybv/YWGZ7Vgk31MGw+DMp05+3y9fPERUTHlt1c9sUyoqCaD5bdXVz2wkG0hnpDmFy8r0fr3VBn/C7Rmg+L0/45EWfdocGq3HQ1uRro0GJK+vsvo837NR82s01l/n97rsWn7RYNBM3WRcDY3cJKosqMJhgdHtj9yflthd65rxxxxXnid+f0/Ia195vi6oAAAAAAAAAAAAAAAAAAAAAAAAAAAABw" 19 20 func TestPrepareContentImage(t *testing.T) { 21 file, err := os.Open("../../_assets/tests/test.jpg") 22 require.NoError(t, err) 23 defer file.Close() 24 25 payload, err := ioutil.ReadAll(file) 26 require.NoError(t, err) 27 28 message := NewMessage() 29 message.ContentType = protobuf.ChatMessage_IMAGE 30 image := protobuf.ImageMessage{ 31 Payload: payload, 32 Format: protobuf.ImageFormat_JPEG, 33 } 34 message.Payload = &protobuf.ChatMessage_Image{Image: &image} 35 36 require.NoError(t, message.PrepareContent("")) 37 require.Equal(t, expectedJPEG, message.Base64Image) 38 } 39 40 func TestPrepareContentAudio(t *testing.T) { 41 file, err := os.Open("../../_assets/tests/test.aac") 42 require.NoError(t, err) 43 defer file.Close() 44 45 payload, err := ioutil.ReadAll(file) 46 require.NoError(t, err) 47 48 message := NewMessage() 49 message.ContentType = protobuf.ChatMessage_AUDIO 50 audio := protobuf.AudioMessage{ 51 Payload: payload, 52 Type: protobuf.AudioMessage_AAC, 53 } 54 message.Payload = &protobuf.ChatMessage_Audio{Audio: &audio} 55 56 require.NoError(t, message.PrepareContent("")) 57 require.Equal(t, expectedAAC, message.Base64Audio) 58 } 59 60 func TestGetAudioMessageMIME(t *testing.T) { 61 aac := &protobuf.AudioMessage{Type: protobuf.AudioMessage_AAC} 62 mime, err := getAudioMessageMIME(aac) 63 require.NoError(t, err) 64 require.Equal(t, "aac", mime) 65 66 amr := &protobuf.AudioMessage{Type: protobuf.AudioMessage_AMR} 67 mime, err = getAudioMessageMIME(amr) 68 require.NoError(t, err) 69 require.Equal(t, "amr", mime) 70 } 71 72 func TestPrepareContentMentions(t *testing.T) { 73 message := NewMessage() 74 pk1, err := crypto.GenerateKey() 75 require.NoError(t, err) 76 pk1String := types.EncodeHex(crypto.FromECDSAPub(&pk1.PublicKey)) 77 78 pk2, err := crypto.GenerateKey() 79 require.NoError(t, err) 80 pk2String := types.EncodeHex(crypto.FromECDSAPub(&pk2.PublicKey)) 81 82 message.Text = "hey @" + pk1String + " @" + pk2String 83 84 require.NoError(t, message.PrepareContent(pk2String)) 85 require.Len(t, message.Mentions, 2) 86 require.Equal(t, message.Mentions[0], pk1String) 87 require.Equal(t, message.Mentions[1], pk2String) 88 require.True(t, message.Mentioned) 89 } 90 91 func TestPrepareContentLinks(t *testing.T) { 92 message := NewMessage() 93 94 link1 := "https://github.com/status-im/status-mobile" 95 link2 := "https://www.youtube.com/watch?v=6RYO8KCY6YE" 96 97 message.Text = "Just look at that repo " + link1 + " . And watch this video - " + link2 98 99 require.NoError(t, message.PrepareContent("")) 100 require.Len(t, message.Links, 2) 101 require.Equal(t, message.Links[0], link1) 102 require.Equal(t, message.Links[1], link2) 103 } 104 105 func TestPrepareSimplifiedText(t *testing.T) { 106 canonicalName1 := "canonical-name-1" 107 canonicalName2 := "canonical-name-2" 108 109 message := NewMessage() 110 pk1, err := crypto.GenerateKey() 111 require.NoError(t, err) 112 pk1String := types.EncodeHex(crypto.FromECDSAPub(&pk1.PublicKey)) 113 114 pk2, err := crypto.GenerateKey() 115 require.NoError(t, err) 116 pk2String := types.EncodeHex(crypto.FromECDSAPub(&pk2.PublicKey)) 117 118 message.Text = "hey @" + pk1String + " @" + pk2String 119 120 require.NoError(t, message.PrepareContent("")) 121 require.Len(t, message.Mentions, 2) 122 require.Equal(t, message.Mentions[0], pk1String) 123 require.Equal(t, message.Mentions[1], pk2String) 124 125 canonicalNames := make(map[string]string) 126 canonicalNames[pk1String] = canonicalName1 127 canonicalNames[pk2String] = canonicalName2 128 129 simplifiedText, err := message.GetSimplifiedText("", canonicalNames) 130 require.NoError(t, err) 131 require.Equal(t, "hey "+canonicalName1+" "+canonicalName2, simplifiedText) 132 } 133 134 func TestConvertLinkPreviewsToProto(t *testing.T) { 135 msg := Message{ 136 LinkPreviews: []LinkPreview{ 137 { 138 Type: protobuf.UnfurledLink_LINK, 139 Description: "GitHub is where people build software.", 140 Hostname: "github.com", 141 Title: "Build software better, together", 142 URL: "https://github.com", 143 Thumbnail: LinkPreviewThumbnail{ 144 Width: 100, 145 Height: 200, 146 URL: "http://localhost:9999", 147 DataURI: "data:image/png;base64,iVBORw0KGgoAAAANSUg=", 148 }, 149 Favicon: LinkPreviewThumbnail{ 150 DataURI: "data:image/png;base64,iVBORw0KGgoAAAANSUg=", 151 }, 152 }, 153 }, 154 } 155 156 unfurledLinks, err := msg.ConvertLinkPreviewsToProto() 157 require.NoError(t, err) 158 require.Len(t, unfurledLinks, 1) 159 160 l := unfurledLinks[0] 161 validPreview := msg.LinkPreviews[0] 162 require.Equal(t, validPreview.Type, l.Type) 163 require.Equal(t, validPreview.Description, l.Description) 164 require.Equal(t, validPreview.Title, l.Title) 165 require.Equal(t, uint32(validPreview.Thumbnail.Width), l.ThumbnailWidth) 166 require.Equal(t, uint32(validPreview.Thumbnail.Height), l.ThumbnailHeight) 167 168 expectedPayload, err := base64.StdEncoding.DecodeString("iVBORw0KGgoAAAANSUg=") 169 require.NoError(t, err) 170 require.Equal(t, expectedPayload, l.ThumbnailPayload) 171 172 expectedFaviconPayload, err := base64.StdEncoding.DecodeString("iVBORw0KGgoAAAANSUg=") 173 require.NoError(t, err) 174 require.Equal(t, expectedFaviconPayload, l.FaviconPayload) 175 176 // Test any invalid link preview causes an early return. 177 invalidPreview := validPreview 178 invalidPreview.Title = "" 179 msg.LinkPreviews = []LinkPreview{invalidPreview} 180 _, err = msg.ConvertLinkPreviewsToProto() 181 require.ErrorContains(t, err, "invalid link preview, url='https://github.com'") 182 183 // Test invalid data URI invalidates a preview. 184 invalidPreview = validPreview 185 invalidPreview.Thumbnail.DataURI = "data:hello/png,iVBOR" 186 msg.LinkPreviews = []LinkPreview{invalidPreview} 187 _, err = msg.ConvertLinkPreviewsToProto() 188 require.ErrorContains(t, err, "could not get data URI payload for link preview thumbnail, url='https://github.com': wrong uri format") 189 190 // Test thumbnail is optional. 191 somePreview := validPreview 192 somePreview.Thumbnail.DataURI = "" 193 somePreview.Thumbnail.Width = 0 194 somePreview.Thumbnail.Height = 0 195 msg.LinkPreviews = []LinkPreview{somePreview} 196 unfurledLinks, err = msg.ConvertLinkPreviewsToProto() 197 require.NoError(t, err) 198 require.Len(t, unfurledLinks, 1) 199 require.Nil(t, unfurledLinks[0].ThumbnailPayload) 200 } 201 202 func TestConvertFromProtoToLinkPreviews(t *testing.T) { 203 204 thumbnailPayload, err := base64.StdEncoding.DecodeString("iVBORw0KGgoAAAANSUg=") 205 require.NoError(t, err) 206 FaviconPayload, err := base64.StdEncoding.DecodeString("iVBORw0KGgoAAAANSUg=") 207 require.NoError(t, err) 208 209 l := &protobuf.UnfurledLink{ 210 Description: "GitHub is where people build software.", 211 Title: "Build software better, together", 212 Type: protobuf.UnfurledLink_LINK, 213 Url: "https://github.com", 214 ThumbnailPayload: thumbnailPayload, 215 FaviconPayload: FaviconPayload, 216 ThumbnailWidth: 100, 217 ThumbnailHeight: 200, 218 } 219 msg := Message{ 220 ID: "42", 221 ChatMessage: &protobuf.ChatMessage{ 222 UnfurledLinks: []*protobuf.UnfurledLink{l}, 223 }, 224 } 225 226 urlMaker := func(msgID string, linkURL string) string { 227 return "https://localhost:6666/" + msgID + "-" + linkURL 228 } 229 230 previews := msg.ConvertFromProtoToLinkPreviews(urlMaker, urlMaker) 231 require.Len(t, previews, 1) 232 p := previews[0] 233 require.Equal(t, l.Type, p.Type) 234 require.Equal(t, "github.com", p.Hostname) 235 require.Equal(t, l.Description, p.Description) 236 require.Equal(t, l.Url, p.URL) 237 require.Equal(t, int(l.ThumbnailHeight), p.Thumbnail.Height) 238 require.Equal(t, int(l.ThumbnailWidth), p.Thumbnail.Width) 239 // Important, don't build up a data URI because the thumbnail should be 240 // fetched from the media server. 241 require.Equal(t, "", p.Thumbnail.DataURI) 242 require.Equal(t, "https://localhost:6666/42-https://github.com", p.Thumbnail.URL) 243 require.Equal(t, "https://localhost:6666/42-https://github.com", p.Favicon.URL) 244 245 // Test when the URL is not parseable by url.Parse. 246 l.Url = "postgres://user:abc{DEf1=ghi@example.com:5432/db?sslmode=require" 247 msg.ChatMessage.UnfurledLinks = []*protobuf.UnfurledLink{l} 248 previews = msg.ConvertFromProtoToLinkPreviews(urlMaker, urlMaker) 249 require.Len(t, previews, 1) 250 p = previews[0] 251 require.Equal(t, l.Url, p.Hostname) 252 253 // Test when there's no thumbnail payload. 254 l = &protobuf.UnfurledLink{ 255 Description: "GitHub is where people build software.", 256 Title: "Build software better, together", 257 Url: "https://github.com", 258 } 259 msg.ChatMessage.UnfurledLinks = []*protobuf.UnfurledLink{l} 260 previews = msg.ConvertFromProtoToLinkPreviews(urlMaker, urlMaker) 261 require.Len(t, previews, 1) 262 p = previews[0] 263 require.Equal(t, 0, p.Thumbnail.Height) 264 require.Equal(t, 0, p.Thumbnail.Width) 265 require.Equal(t, "", p.Thumbnail.URL) 266 } 267 268 func TestConvertStatusLinkPreviewsToProto(t *testing.T) { 269 pk1, err := crypto.GenerateKey() 270 require.NoError(t, err) 271 272 compressedContactPublicKey := crypto.CompressPubkey(&pk1.PublicKey) 273 contactID := types.EncodeHex(crypto.FromECDSAPub(&pk1.PublicKey)) 274 275 pk2, err := crypto.GenerateKey() 276 require.NoError(t, err) 277 278 compressedCommunityPublicKey := crypto.CompressPubkey(&pk2.PublicKey) 279 communityID := types.EncodeHex(compressedCommunityPublicKey) 280 281 pk3, err := crypto.GenerateKey() 282 require.NoError(t, err) 283 284 compressedCommunityPublicKey2 := crypto.CompressPubkey(&pk3.PublicKey) 285 communityID2 := types.EncodeHex(compressedCommunityPublicKey2) 286 287 contact := &StatusContactLinkPreview{ 288 PublicKey: contactID, 289 DisplayName: "DisplayName_2", 290 Description: "Description_3", 291 Icon: LinkPreviewThumbnail{ 292 Width: 10, 293 Height: 20, 294 DataURI: "data:image/png;base64,iVBORw0KGgoAAAANSUg=", 295 }, 296 } 297 298 community := &StatusCommunityLinkPreview{ 299 CommunityID: communityID, 300 DisplayName: "DisplayName_5", 301 Description: "Description_6", 302 MembersCount: 7, 303 Color: "Color_8", 304 Icon: LinkPreviewThumbnail{ 305 Width: 30, 306 Height: 40, 307 DataURI: "data:image/png;base64,iVBORw0KGgoAAAANSUg=", 308 }, 309 Banner: LinkPreviewThumbnail{ 310 Width: 50, 311 Height: 60, 312 DataURI: "data:image/png;base64,iVBORw0KGgoAAAANSUg=", 313 }, 314 } 315 316 channel := &StatusCommunityChannelLinkPreview{ 317 ChannelUUID: "ChannelUUID_11", 318 Emoji: "Emoji_12", 319 DisplayName: "DisplayName_13", 320 Description: "Description_14", 321 Color: "Color_15", 322 Community: &StatusCommunityLinkPreview{ 323 CommunityID: communityID2, 324 DisplayName: "DisplayName_17", 325 Description: "Description_18", 326 MembersCount: 19, 327 Color: "Color_20", 328 Icon: LinkPreviewThumbnail{ 329 Width: 70, 330 Height: 80, 331 DataURI: "data:image/png;base64,iVBORw0KGgoAAAANSUg=", 332 }, 333 Banner: LinkPreviewThumbnail{ 334 Width: 90, 335 Height: 100, 336 DataURI: "data:image/png;base64,iVBORw0KGgoAAAANSUg=", 337 }, 338 }, 339 } 340 341 message := Message{ 342 StatusLinkPreviews: []StatusLinkPreview{ 343 { 344 URL: "https://status.app/u/", 345 Contact: contact, 346 }, 347 { 348 URL: "https://status.app/c/", 349 Community: community, 350 }, 351 { 352 URL: "https://status.app/cc/", 353 Channel: channel, 354 }, 355 }, 356 } 357 358 expectedThumbnailPayload, err := base64.StdEncoding.DecodeString("iVBORw0KGgoAAAANSUg=") 359 require.NoError(t, err) 360 361 unfurledLinks, err := message.ConvertStatusLinkPreviewsToProto() 362 require.NoError(t, err) 363 require.Len(t, unfurledLinks.UnfurledStatusLinks, 3) 364 365 // Contact link 366 367 l1 := unfurledLinks.UnfurledStatusLinks[0] 368 require.Equal(t, "https://status.app/u/", l1.Url) 369 require.NotNil(t, l1.GetContact()) 370 require.Nil(t, l1.GetCommunity()) 371 require.Nil(t, l1.GetChannel()) 372 c1 := l1.GetContact() 373 require.Equal(t, compressedContactPublicKey, c1.PublicKey) 374 require.Equal(t, contact.DisplayName, c1.DisplayName) 375 require.Equal(t, contact.Description, c1.Description) 376 require.NotNil(t, c1.Icon) 377 require.Equal(t, uint32(contact.Icon.Width), c1.Icon.Width) 378 require.Equal(t, uint32(contact.Icon.Height), c1.Icon.Height) 379 require.Equal(t, expectedThumbnailPayload, c1.Icon.Payload) 380 381 // Community link 382 383 l2 := unfurledLinks.UnfurledStatusLinks[1] 384 require.Equal(t, "https://status.app/c/", l2.Url) 385 require.NotNil(t, l2.GetCommunity()) 386 require.Nil(t, l2.GetContact()) 387 require.Nil(t, l2.GetChannel()) 388 c2 := l2.GetCommunity() 389 require.Equal(t, compressedCommunityPublicKey, c2.CommunityId) 390 require.Equal(t, community.DisplayName, c2.DisplayName) 391 require.Equal(t, community.Description, c2.Description) 392 require.Equal(t, community.MembersCount, c2.MembersCount) 393 require.Equal(t, community.Color, c2.Color) 394 require.NotNil(t, c2.Icon) 395 require.Equal(t, uint32(community.Icon.Width), c2.Icon.Width) 396 require.Equal(t, uint32(community.Icon.Height), c2.Icon.Height) 397 require.Equal(t, expectedThumbnailPayload, c2.Icon.Payload) 398 require.NotNil(t, c2.Banner) 399 require.Equal(t, uint32(community.Banner.Width), c2.Banner.Width) 400 require.Equal(t, uint32(community.Banner.Height), c2.Banner.Height) 401 require.Equal(t, expectedThumbnailPayload, c2.Banner.Payload) 402 403 // Channel link 404 405 l3 := unfurledLinks.UnfurledStatusLinks[2] 406 require.Equal(t, "https://status.app/cc/", l3.Url) 407 require.NotNil(t, l3.GetChannel()) 408 require.Nil(t, l3.GetContact()) 409 require.Nil(t, l3.GetCommunity()) 410 411 c3 := l3.GetChannel() 412 require.Equal(t, channel.ChannelUUID, c3.ChannelUuid) 413 require.Equal(t, channel.Emoji, c3.Emoji) 414 require.Equal(t, channel.DisplayName, c3.DisplayName) 415 require.Equal(t, channel.Description, c3.Description) 416 require.Equal(t, channel.Color, c3.Color) 417 418 require.NotNil(t, c3.Community) 419 require.Equal(t, compressedCommunityPublicKey2, c3.Community.CommunityId) 420 require.Equal(t, channel.Community.DisplayName, c3.Community.DisplayName) 421 require.Equal(t, channel.Community.Color, c3.Community.Color) 422 require.Equal(t, channel.Community.Description, c3.Community.Description) 423 require.Equal(t, channel.Community.MembersCount, c3.Community.MembersCount) 424 require.NotNil(t, c3.Community.Icon) 425 require.Equal(t, uint32(channel.Community.Icon.Width), c3.Community.Icon.Width) 426 require.Equal(t, uint32(channel.Community.Icon.Height), c3.Community.Icon.Height) 427 require.Equal(t, expectedThumbnailPayload, c3.Community.Icon.Payload) 428 require.NotNil(t, c3.Community.Banner) 429 require.Equal(t, uint32(channel.Community.Banner.Width), c3.Community.Banner.Width) 430 require.Equal(t, uint32(channel.Community.Banner.Height), c3.Community.Banner.Height) 431 require.Equal(t, expectedThumbnailPayload, c3.Community.Banner.Payload) 432 433 // Test any invalid link preview causes an early return. 434 invalidContactPreview := contact 435 invalidContactPreview.PublicKey = "" 436 invalidPreview := message.StatusLinkPreviews[0] 437 invalidPreview.Contact = invalidContactPreview 438 message.StatusLinkPreviews = []StatusLinkPreview{invalidPreview} 439 _, err = message.ConvertStatusLinkPreviewsToProto() 440 require.ErrorContains(t, err, "invalid status link preview, url='https://status.app/u/'") 441 } 442 443 func TestConvertFromProtoToStatusLinkPreviews(t *testing.T) { 444 pk1, err := crypto.GenerateKey() 445 require.NoError(t, err) 446 447 compressedContactPublicKey := crypto.CompressPubkey(&pk1.PublicKey) 448 contactID := types.EncodeHex(crypto.FromECDSAPub(&pk1.PublicKey)) 449 450 pk2, err := crypto.GenerateKey() 451 require.NoError(t, err) 452 453 compressedCommunityPublicKey := crypto.CompressPubkey(&pk2.PublicKey) 454 communityID := types.EncodeHex(compressedCommunityPublicKey) 455 456 pk3, err := crypto.GenerateKey() 457 require.NoError(t, err) 458 459 compressedCommunityPublicKey2 := crypto.CompressPubkey(&pk3.PublicKey) 460 communityID2 := types.EncodeHex(compressedCommunityPublicKey2) 461 462 thumbnailPayload, err := base64.StdEncoding.DecodeString("iVBORw0KGgoAAAANSUg=") 463 require.NoError(t, err) 464 465 contact := &protobuf.UnfurledStatusContactLink{ 466 PublicKey: compressedContactPublicKey, 467 DisplayName: "DisplayName_2", 468 Description: "Description_3", 469 Icon: &protobuf.UnfurledLinkThumbnail{ 470 Width: 10, 471 Height: 20, 472 Payload: thumbnailPayload, 473 }, 474 } 475 476 community := &protobuf.UnfurledStatusCommunityLink{ 477 CommunityId: compressedCommunityPublicKey, 478 DisplayName: "DisplayName_5", 479 Description: "Description_6", 480 MembersCount: 7, 481 Color: "Color_8", 482 Icon: &protobuf.UnfurledLinkThumbnail{ 483 Width: 30, 484 Height: 40, 485 Payload: thumbnailPayload, 486 }, 487 Banner: &protobuf.UnfurledLinkThumbnail{ 488 Width: 50, 489 Height: 60, 490 Payload: thumbnailPayload, 491 }, 492 } 493 494 channel := &protobuf.UnfurledStatusChannelLink{ 495 ChannelUuid: "ChannelUuid_11", 496 Emoji: "Emoji_12", 497 DisplayName: "DisplayName_13", 498 Description: "Description_14", 499 Color: "Color_15", 500 Community: &protobuf.UnfurledStatusCommunityLink{ 501 CommunityId: compressedCommunityPublicKey2, 502 DisplayName: "DisplayName_17", 503 Description: "Description_18", 504 MembersCount: 19, 505 Color: "Color_20", 506 Icon: &protobuf.UnfurledLinkThumbnail{ 507 Width: 70, 508 Height: 80, 509 Payload: thumbnailPayload, 510 }, 511 Banner: &protobuf.UnfurledLinkThumbnail{ 512 Width: 90, 513 Height: 100, 514 Payload: thumbnailPayload, 515 }, 516 }, 517 } 518 519 msg := Message{ 520 ID: "42", 521 ChatMessage: &protobuf.ChatMessage{ 522 UnfurledStatusLinks: &protobuf.UnfurledStatusLinks{ 523 UnfurledStatusLinks: []*protobuf.UnfurledStatusLink{ 524 { 525 Url: "https://status.app/u/", 526 Payload: &protobuf.UnfurledStatusLink_Contact{ 527 Contact: contact, 528 }, 529 }, 530 { 531 Url: "https://status.app/c/", 532 Payload: &protobuf.UnfurledStatusLink_Community{ 533 Community: community, 534 }, 535 }, 536 { 537 Url: "https://status.app/cc/", 538 Payload: &protobuf.UnfurledStatusLink_Channel{ 539 Channel: channel, 540 }, 541 }, 542 }, 543 }, 544 }, 545 } 546 547 urlMaker := func(msgID string, linkURL string, imageID MediaServerImageID) string { 548 return "https://localhost:6666/" + msgID + "-" + linkURL + "-" + string(imageID) 549 } 550 551 previews := msg.ConvertFromProtoToStatusLinkPreviews(urlMaker) 552 require.Len(t, previews, 3) 553 554 // Contact preview 555 556 p1 := previews[0] 557 require.Equal(t, "https://status.app/u/", p1.URL) 558 require.NotNil(t, p1.Contact) 559 require.Nil(t, p1.Community) 560 require.Nil(t, p1.Channel) 561 562 c1 := p1.Contact 563 require.NotNil(t, c1) 564 require.Equal(t, contactID, c1.PublicKey) 565 require.Equal(t, contact.DisplayName, c1.DisplayName) 566 require.Equal(t, contact.Description, c1.Description) 567 require.NotNil(t, c1.Icon) 568 require.Equal(t, int(contact.Icon.Width), c1.Icon.Width) 569 require.Equal(t, int(contact.Icon.Height), c1.Icon.Height) 570 require.Equal(t, "", c1.Icon.DataURI) 571 require.Equal(t, "https://localhost:6666/42-https://status.app/u/-contact-icon", c1.Icon.URL) 572 573 // Community preview 574 575 p2 := previews[1] 576 require.Equal(t, "https://status.app/c/", p2.URL) 577 require.NotNil(t, p2.Community) 578 require.Nil(t, p2.Contact) 579 require.Nil(t, p2.Channel) 580 581 c2 := p2.Community 582 require.Equal(t, communityID, c2.CommunityID) 583 require.Equal(t, community.DisplayName, c2.DisplayName) 584 require.Equal(t, community.Description, c2.Description) 585 require.Equal(t, community.MembersCount, c2.MembersCount) 586 require.Equal(t, community.Color, c2.Color) 587 require.NotNil(t, c2.Icon) 588 require.Equal(t, int(community.Icon.Width), c2.Icon.Width) 589 require.Equal(t, int(community.Icon.Height), c2.Icon.Height) 590 require.Equal(t, "", c2.Icon.DataURI) 591 require.Equal(t, "https://localhost:6666/42-https://status.app/c/-community-icon", c2.Icon.URL) 592 require.NotNil(t, c2.Banner) 593 require.Equal(t, int(community.Banner.Width), c2.Banner.Width) 594 require.Equal(t, int(community.Banner.Height), c2.Banner.Height) 595 require.Equal(t, "", c2.Banner.DataURI) 596 require.Equal(t, "https://localhost:6666/42-https://status.app/c/-community-banner", c2.Banner.URL) 597 598 // Channel preview 599 600 p3 := previews[2] 601 require.Equal(t, "https://status.app/cc/", p3.URL) 602 require.NotNil(t, p3.Channel) 603 require.Nil(t, p3.Contact) 604 require.Nil(t, p3.Community) 605 606 c3 := previews[2].Channel 607 require.Equal(t, channel.ChannelUuid, c3.ChannelUUID) 608 require.Equal(t, channel.Emoji, c3.Emoji) 609 require.Equal(t, channel.DisplayName, c3.DisplayName) 610 require.Equal(t, channel.Description, c3.Description) 611 require.Equal(t, channel.Color, c3.Color) 612 613 require.NotNil(t, p3.Channel.Community) 614 require.Equal(t, communityID2, c3.Community.CommunityID) 615 require.Equal(t, channel.Community.DisplayName, c3.Community.DisplayName) 616 require.Equal(t, channel.Community.Color, c3.Community.Color) 617 require.Equal(t, channel.Community.Description, c3.Community.Description) 618 require.Equal(t, channel.Community.MembersCount, c3.Community.MembersCount) 619 require.NotNil(t, c3.Community.Icon) 620 require.Equal(t, int(channel.Community.Icon.Width), c3.Community.Icon.Width) 621 require.Equal(t, int(channel.Community.Icon.Height), c3.Community.Icon.Height) 622 require.Equal(t, "", c3.Community.Icon.DataURI) 623 require.Equal(t, "https://localhost:6666/42-https://status.app/cc/-community-channel-icon", c3.Community.Icon.URL) 624 require.NotNil(t, c3.Community.Banner) 625 require.Equal(t, int(channel.Community.Banner.Width), c3.Community.Banner.Width) 626 require.Equal(t, int(channel.Community.Banner.Height), c3.Community.Banner.Height) 627 require.Equal(t, "", c3.Community.Banner.DataURI) 628 require.Equal(t, "https://localhost:6666/42-https://status.app/cc/-community-channel-banner", c3.Community.Banner.URL) 629 630 } 631 632 func assertMarshalAndUnmarshalJSON[T any](t *testing.T, obj *T, msgAndArgs ...any) { 633 rawJSON, err := json.Marshal(obj) 634 require.NoError(t, err, msgAndArgs...) 635 636 var unmarshalled T 637 err = json.Unmarshal(rawJSON, &unmarshalled) 638 require.NoError(t, err, msgAndArgs...) 639 require.Equal(t, obj, &unmarshalled, msgAndArgs...) 640 } 641 642 func TestMarshalMessageJSON(t *testing.T) { 643 msg := &Message{ 644 ID: "1", 645 From: "0x04c51631b3354242d5a56f044c3b7703bcc001e8c725c4706928b3fac3c2a12ec9019e1e224d487f5c893389405bcec998bc687307f290a569d6a97d24b711bca8", 646 LinkPreviews: []LinkPreview{ 647 { 648 Type: protobuf.UnfurledLink_LINK, 649 Description: "GitHub is where people build software.", 650 Hostname: "github.com", 651 Title: "Build software better, together", 652 URL: "https://github.com", 653 Thumbnail: LinkPreviewThumbnail{ 654 Width: 100, 655 Height: 200, 656 URL: "http://localhost:9999", 657 DataURI: "data:image/png;base64,iVBORw0KGgoAAAANSUg=", 658 }, 659 }, 660 }, 661 } 662 663 assertMarshalAndUnmarshalJSON(t, msg, "message ID='%s'", msg.ID) 664 }