github.com/status-im/status-go@v1.1.0/protocol/messenger_share_urls_test.go (about) 1 package protocol 2 3 import ( 4 "context" 5 "fmt" 6 "testing" 7 8 "github.com/stretchr/testify/suite" 9 10 "github.com/status-im/status-go/api/multiformat" 11 "github.com/status-im/status-go/eth-node/crypto" 12 "github.com/status-im/status-go/eth-node/types" 13 "github.com/status-im/status-go/protocol/communities" 14 "github.com/status-im/status-go/protocol/protobuf" 15 "github.com/status-im/status-go/protocol/requests" 16 ) 17 18 const ( 19 userURL = "https://status.app/u#zQ3shwQPhRuDJSjVGVBnTjCdgXy5i9WQaeVPdGJD6yTarJQSj" 20 userURLWithData = "https://status.app/u/G10A4B0JdgwyRww90WXtnP1oNH1ZLQNM0yX0Ja9YyAMjrqSZIYINOHCbFhrnKRAcPGStPxCMJDSZlGCKzmZrJcimHY8BbcXlORrElv_BbQEegnMDPx1g9C5VVNl0fE4y#zQ3shwQPhRuDJSjVGVBnTjCdgXy5i9WQaeVPdGJD6yTarJQSj" 21 communityURL = "https://status.app/c#zQ3shYSHp7GoiXaauJMnDcjwU2yNjdzpXLosAWapPS4CFxc11" 22 communityURLWithData = "https://status.app/c/iyKACkQKB0Rvb2RsZXMSJ0NvbG9yaW5nIHRoZSB3b3JsZCB3aXRoIGpveSDigKIg4bSXIOKAohiYohsiByMxMzFEMkYqAwEhMwM=#zQ3shYSHp7GoiXaauJMnDcjwU2yNjdzpXLosAWapPS4CFxc11" 23 communityURLWithDataNoTags = "https://status.app/c/CxCACh8KBFBJR1MSDHdlIGxvdmUgcGlncxgBIgcjRDM0NEM1Aw==#zQ3shZp9gY1FXfYkcd3CMrFLHriHQfrXvpF9XbZMwJhTcZsq8" 24 communityURLWithDataWithTags = "https://status.app/c/CxKACiMKBFBJR1MSDHdlIGxvdmUgcGlncxgBIgcjRDM0NEM1KgIjHgM=#zQ3shZp9gY1FXfYkcd3CMrFLHriHQfrXvpF9XbZMwJhTcZsq8" 25 channelURL = "https://status.app/cc/003cdcd5-e065-48f9-b166-b1a94ac75a11#zQ3shYSHp7GoiXaauJMnDcjwU2yNjdzpXLosAWapPS4CFxc11" 26 channelURLWithData = "https://status.app/cc/G54AAKwObLdpiGjXnckYzRcOSq0QQAS_CURGfqVU42ceGHCObstUIknTTZDOKF3E8y2MSicncpO7fTskXnoACiPKeejvjtLTGWNxUhlT7fyQS7Jrr33UVHluxv_PLjV2ePGw5GQ33innzeK34pInIgUGs5RjdQifMVmURalxxQKwiuoY5zwIjixWWRHqjHM=#zQ3shYSHp7GoiXaauJMnDcjwU2yNjdzpXLosAWapPS4CFxc11" 27 ) 28 29 func TestMessengerShareUrlsSuite(t *testing.T) { 30 suite.Run(t, new(MessengerShareUrlsSuite)) 31 } 32 33 type MessengerShareUrlsSuite struct { 34 MessengerBaseTestSuite 35 } 36 37 func (s *MessengerShareUrlsSuite) createCommunity() *communities.Community { 38 description := &requests.CreateCommunity{ 39 Membership: protobuf.CommunityPermissions_AUTO_ACCEPT, 40 Name: "status", 41 Color: "#ffffff", 42 Description: "status community description", 43 Tags: RandomCommunityTags(3), 44 } 45 46 // Create a community chat 47 response, err := s.m.CreateCommunity(description, false) 48 s.Require().NoError(err) 49 s.Require().NotNil(response) 50 return response.Communities()[0] 51 } 52 53 func (s *MessengerShareUrlsSuite) createContact() (*Messenger, *Contact) { 54 theirMessenger := s.newMessenger() 55 56 contactID := types.EncodeHex(crypto.FromECDSAPub(&theirMessenger.identity.PublicKey)) 57 ensName := "blah.stateofus.eth" 58 59 s.Require().NoError(s.m.ENSVerified(contactID, ensName)) 60 61 response, err := s.m.AddContact(context.Background(), &requests.AddContact{ID: contactID}) 62 s.Require().NoError(err) 63 s.Require().NotNil(response) 64 s.Require().Len(response.Contacts, 1) 65 66 contact := response.Contacts[0] 67 s.Require().Equal(ensName, contact.EnsName) 68 s.Require().True(contact.ENSVerified) 69 70 return theirMessenger, contact 71 } 72 73 func (s *MessengerShareUrlsSuite) createCommunityWithChannel() (*communities.Community, *protobuf.CommunityChat, string) { 74 community := s.createCommunity() 75 76 chat := &protobuf.CommunityChat{ 77 Permissions: &protobuf.CommunityPermissions{ 78 Access: protobuf.CommunityPermissions_AUTO_ACCEPT, 79 }, 80 Identity: &protobuf.ChatIdentity{ 81 DisplayName: "status-core", 82 Emoji: "😎", 83 Description: "status-core community chat", 84 }, 85 } 86 response, err := s.m.CreateCommunityChat(community.ID(), chat) 87 s.Require().NoError(err) 88 s.Require().NotNil(response) 89 s.Require().Len(response.Communities(), 1) 90 s.Require().Len(response.Chats(), 1) 91 92 community = response.Communities()[0] 93 s.Require().Len(community.Chats(), 1) 94 95 var channelID string 96 var channel *protobuf.CommunityChat 97 98 for key, value := range community.Chats() { 99 channelID = key 100 channel = value 101 break 102 } 103 s.Require().NotNil(channel) 104 return community, channel, channelID 105 } 106 107 func (s *MessengerShareUrlsSuite) TestDecodeEncodeDataURL() { 108 ts := [][]byte{ 109 []byte("test data 123"), 110 []byte("test data 123test data 123test data 123test data 123test data 123"), 111 } 112 113 for i := range ts { 114 encodedData, err := encodeDataURL(ts[i]) 115 s.Require().NoError(err) 116 117 decodedData, err := decodeDataURL(encodedData) 118 s.Require().NoError(err) 119 s.Require().Equal(ts[i], decodedData) 120 } 121 } 122 123 func (s *MessengerShareUrlsSuite) TestParseWrongUrls() { 124 const notStatusSharedURLError = "not a status shared url" 125 badURLs := map[string]string{ 126 "https://status.appc/#zQ3shYSHp7GoiXaauJMnDcjwU2yNjdzpXLosAWapPS4CFxc11": notStatusSharedURLError, 127 "https://status.app/cc#zQ3shYSHp7GoiXaauJMnDcjwU2yNjdzpXLosAWapPS4CFxc11": notStatusSharedURLError, 128 "https://status.app/a#zQ3shYSHp7GoiXaauJMnDcjwU2yNjdzpXLosAWapPS4CFxc11": notStatusSharedURLError, 129 "https://status.im/u#zQ3shYSHp7GoiXaauJMnDcjwU2yNjdzpXLosAWapPS4CFxc11": notStatusSharedURLError, 130 "https://status.app/u/": "url should contain at least one `#` separator", 131 } 132 133 for url, expectedError := range badURLs { 134 urlData, err := ParseSharedURL(url) 135 s.Require().Error(err) 136 s.Require().Equal(err.Error(), expectedError) 137 s.Require().Nil(urlData) 138 } 139 } 140 141 func (s *MessengerShareUrlsSuite) TestIsStatusSharedUrl() { 142 testCases := []struct { 143 Name string 144 URL string 145 Result bool 146 }{ 147 { 148 Name: "Direct website link", 149 URL: "https://status.app", 150 Result: false, 151 }, 152 { 153 Name: "Website page link", 154 URL: "https://status.app/features/messenger", 155 Result: false, 156 }, 157 { 158 // starts with `/c`, but no `#` after 159 Name: "Website page link", 160 URL: "https://status.app/communities", 161 Result: false, 162 }, 163 { 164 Name: "User link", 165 URL: userURL, 166 Result: true, 167 }, 168 { 169 Name: "User link with data", 170 URL: userURLWithData, 171 Result: true, 172 }, 173 { 174 Name: "Community link", 175 URL: communityURL, 176 Result: true, 177 }, 178 { 179 Name: "Community link with data", 180 URL: communityURLWithData, 181 Result: true, 182 }, 183 { 184 Name: "Channel link", 185 URL: channelURL, 186 Result: true, 187 }, 188 { 189 Name: "Channel link with data", 190 URL: channelURLWithData, 191 Result: true, 192 }, 193 } 194 195 for _, tc := range testCases { 196 s.Run(tc.Name, func() { 197 result := IsStatusSharedURL(tc.URL) 198 s.Require().Equal(tc.Result, result) 199 }) 200 } 201 } 202 203 func (s *MessengerShareUrlsSuite) TestShareCommunityURLWithChatKey() { 204 community := s.createCommunity() 205 206 url, err := s.m.ShareCommunityURLWithChatKey(community.ID()) 207 s.Require().NoError(err) 208 209 shortID, err := community.SerializedID() 210 s.Require().NoError(err) 211 212 expectedURL := fmt.Sprintf("%s/c#%s", baseShareURL, shortID) 213 s.Require().Equal(expectedURL, url) 214 } 215 216 func (s *MessengerShareUrlsSuite) TestParseCommunityURLWithChatKey() { 217 community := s.createCommunity() 218 219 shortID, err := community.SerializedID() 220 s.Require().NoError(err) 221 222 url := fmt.Sprintf("%s/c#%s", baseShareURL, shortID) 223 224 urlData, err := ParseSharedURL(url) 225 s.Require().NoError(err) 226 s.Require().NotNil(urlData) 227 228 s.Require().NotNil(urlData.Community) 229 s.Require().Equal(community.IDString(), urlData.Community.CommunityID) 230 s.Require().Equal("", urlData.Community.DisplayName) 231 s.Require().Equal("", urlData.Community.Description) 232 s.Require().Equal(uint32(0), urlData.Community.MembersCount) 233 s.Require().Equal("", urlData.Community.Color) 234 s.Require().Equal([]uint32{}, urlData.Community.TagIndices) 235 } 236 237 func (s *MessengerShareUrlsSuite) TestShareCommunityURLWithData() { 238 community := s.createCommunity() 239 240 url, err := s.m.ShareCommunityURLWithData(community.ID()) 241 s.Require().NoError(err) 242 243 communityData, chatKey, err := s.m.prepareEncodedCommunityData(community) 244 s.Require().NoError(err) 245 246 expectedURL := fmt.Sprintf("%s/c/%s#%s", baseShareURL, communityData, chatKey) 247 s.Require().Equal(expectedURL, url) 248 249 response, err := ParseSharedURL(url) 250 s.Require().NoError(err) 251 s.Require().NotNil(response) 252 s.Require().NotNil(response.Community) 253 s.Require().Equal(community.IDString(), response.Community.CommunityID) 254 s.Require().Equal(community.TagsIndices(), response.Community.TagIndices) 255 } 256 257 func (s *MessengerShareUrlsSuite) TestParseCommunityURLWithData() { 258 urlData, err := ParseSharedURL(communityURLWithData) 259 s.Require().NoError(err) 260 s.Require().NotNil(urlData) 261 262 s.Require().NotNil(urlData.Community) 263 s.Require().Equal("0x02a3d2fdb9ac335917bf9d46b38d7496c00bbfadbaf832e8aa61d13ac2b4452084", urlData.Community.CommunityID) 264 s.Require().Equal("Doodles", urlData.Community.DisplayName) 265 s.Require().Equal("Coloring the world with joy • ᴗ •", urlData.Community.Description) 266 s.Require().Equal(uint32(446744), urlData.Community.MembersCount) 267 s.Require().Equal("#131D2F", urlData.Community.Color) 268 s.Require().Equal([]uint32{1, 33, 51}, urlData.Community.TagIndices) 269 } 270 271 func (s *MessengerShareUrlsSuite) TestParseCommunityURLWithDataNoTags() { 272 urlData, err := ParseSharedURL(communityURLWithDataNoTags) 273 s.Require().NoError(err) 274 s.Require().NotNil(urlData) 275 276 s.Require().NotNil(urlData.Community) 277 s.Require().Equal("0x02b84843377a24ff498b6c37bd63e2b285c1ee2ccbab82d7a4afa25fff8c5076df", urlData.Community.CommunityID) 278 s.Require().Equal("PIGS", urlData.Community.DisplayName) 279 s.Require().Equal("we love pigs", urlData.Community.Description) 280 s.Require().Equal(uint32(0x1), urlData.Community.MembersCount) 281 s.Require().Equal("#D344C5", urlData.Community.Color) 282 s.Require().Equal([]uint32{}, urlData.Community.TagIndices) 283 } 284 285 func (s *MessengerShareUrlsSuite) TestParseCommunityURLWithDataWithTags() { 286 urlData, err := ParseSharedURL(communityURLWithDataWithTags) 287 s.Require().NoError(err) 288 s.Require().NotNil(urlData) 289 290 s.Require().NotNil(urlData.Community) 291 s.Require().Equal("0x02b84843377a24ff498b6c37bd63e2b285c1ee2ccbab82d7a4afa25fff8c5076df", urlData.Community.CommunityID) 292 s.Require().Equal("PIGS", urlData.Community.DisplayName) 293 s.Require().Equal("we love pigs", urlData.Community.Description) 294 s.Require().Equal(uint32(0x1), urlData.Community.MembersCount) 295 s.Require().Equal("#D344C5", urlData.Community.Color) 296 s.Require().Equal([]uint32{0x23, 0x1e}, urlData.Community.TagIndices) 297 } 298 299 func (s *MessengerShareUrlsSuite) TestShareAndParseCommunityURLWithData() { 300 community := s.createCommunity() 301 302 url, err := s.m.ShareCommunityURLWithData(community.ID()) 303 s.Require().NoError(err) 304 305 urlData, err := ParseSharedURL(url) 306 s.Require().NoError(err) 307 308 s.Require().Equal(community.Identity().DisplayName, urlData.Community.DisplayName) 309 s.Require().Equal(community.DescriptionText(), urlData.Community.Description) 310 s.Require().Equal(uint32(community.MembersCount()), urlData.Community.MembersCount) 311 s.Require().Equal(community.Identity().GetColor(), urlData.Community.Color) 312 s.Require().Equal(community.TagsIndices(), urlData.Community.TagIndices) 313 } 314 315 func (s *MessengerShareUrlsSuite) TestShareCommunityChannelURLWithChatKey() { 316 community := s.createCommunity() 317 channelID := "003cdcd5-e065-48f9-b166-b1a94ac75a11" 318 319 request := &requests.CommunityChannelShareURL{ 320 CommunityID: community.ID(), 321 ChannelID: channelID, 322 } 323 url, err := s.m.ShareCommunityChannelURLWithChatKey(request) 324 s.Require().NoError(err) 325 326 shortID, err := community.SerializedID() 327 s.Require().NoError(err) 328 329 expectedURL := fmt.Sprintf("%s/cc/%s#%s", baseShareURL, channelID, shortID) 330 s.Require().Equal(expectedURL, url) 331 } 332 333 func (s *MessengerShareUrlsSuite) TestParseCommunityChannelURLWithChatKey() { 334 const channelUUID = "003cdcd5-e065-48f9-b166-b1a94ac75a11" 335 const communityID = "0x02a3d2fdb9ac335917bf9d46b38d7496c00bbfadbaf832e8aa61d13ac2b4452084" 336 337 urlData, err := ParseSharedURL(channelURL) 338 s.Require().NoError(err) 339 s.Require().NotNil(urlData) 340 341 s.Require().NotNil(urlData.Community) 342 s.Require().Equal(communityID, urlData.Community.CommunityID) 343 s.Require().Equal("", urlData.Community.DisplayName) 344 s.Require().Equal("", urlData.Community.Description) 345 s.Require().Equal(uint32(0), urlData.Community.MembersCount) 346 s.Require().Equal("", urlData.Community.Color) 347 s.Require().Equal([]uint32{}, urlData.Community.TagIndices) 348 349 s.Require().NotNil(urlData.Channel) 350 s.Require().Equal(channelUUID, urlData.Channel.ChannelUUID) 351 s.Require().Equal("", urlData.Channel.Emoji) 352 s.Require().Equal("", urlData.Channel.DisplayName) 353 s.Require().Equal("", urlData.Channel.Color) 354 } 355 356 func (s *MessengerShareUrlsSuite) TestShareCommunityChannelURLWithData() { 357 community, channel, channelID := s.createCommunityWithChannel() 358 359 request := &requests.CommunityChannelShareURL{ 360 CommunityID: community.ID(), 361 ChannelID: channelID, 362 } 363 url, err := s.m.ShareCommunityChannelURLWithData(request) 364 s.Require().NoError(err) 365 366 communityChannelData, chatKey, err := s.m.prepareEncodedCommunityChannelData(community, channel, channelID) 367 s.Require().NoError(err) 368 369 expectedURL := fmt.Sprintf("%s/cc/%s#%s", baseShareURL, communityChannelData, chatKey) 370 s.Require().Equal(expectedURL, url) 371 } 372 373 func (s *MessengerShareUrlsSuite) TestParseCommunityChannelURLWithData() { 374 urlData, err := ParseSharedURL(channelURLWithData) 375 s.Require().NoError(err) 376 s.Require().NotNil(urlData) 377 378 s.Require().NotNil(urlData.Community) 379 s.Require().Equal("Doodles", urlData.Community.DisplayName) 380 381 s.Require().NotNil(urlData.Channel) 382 s.Require().Equal("🍿", urlData.Channel.Emoji) 383 s.Require().Equal("design", urlData.Channel.DisplayName) 384 s.Require().Equal("#131D2F", urlData.Channel.Color) 385 } 386 387 func (s *MessengerShareUrlsSuite) TestShareAndParseCommunityChannelURLWithData() { 388 community, channel, channelID := s.createCommunityWithChannel() 389 390 request := &requests.CommunityChannelShareURL{ 391 CommunityID: community.ID(), 392 ChannelID: channelID, 393 } 394 url, err := s.m.ShareCommunityChannelURLWithData(request) 395 s.Require().NoError(err) 396 397 urlData, err := ParseSharedURL(url) 398 s.Require().NoError(err) 399 400 s.Require().Equal(community.Identity().DisplayName, urlData.Community.DisplayName) 401 s.Require().Equal(community.DescriptionText(), urlData.Community.Description) 402 s.Require().Equal(uint32(community.MembersCount()), urlData.Community.MembersCount) 403 s.Require().Equal(community.Identity().GetColor(), urlData.Community.Color) 404 s.Require().Equal(community.TagsIndices(), urlData.Community.TagIndices) 405 406 s.Require().NotNil(urlData.Channel) 407 s.Require().Equal(channel.Identity.Emoji, urlData.Channel.Emoji) 408 s.Require().Equal(channel.Identity.DisplayName, urlData.Channel.DisplayName) 409 s.Require().Equal(channel.Identity.Color, urlData.Channel.Color) 410 } 411 412 func (s *MessengerShareUrlsSuite) TestShareUserURLWithChatKey() { 413 _, contact := s.createContact() 414 415 url, err := s.m.ShareUserURLWithChatKey(contact.ID) 416 s.Require().NoError(err) 417 418 shortKey, err := multiformat.SerializeLegacyKey(contact.ID) 419 s.Require().NoError(err) 420 421 expectedURL := fmt.Sprintf("%s/u#%s", baseShareURL, shortKey) 422 s.Require().Equal(expectedURL, url) 423 } 424 425 func (s *MessengerShareUrlsSuite) TestParseUserURLWithChatKey() { 426 urlData, err := ParseSharedURL(userURL) 427 s.Require().NoError(err) 428 s.Require().NotNil(urlData) 429 430 s.Require().NotNil(urlData.Contact) 431 s.Require().Equal("", urlData.Contact.DisplayName) 432 s.Require().Equal("", urlData.Contact.Description) 433 } 434 435 func (s *MessengerShareUrlsSuite) TestShareUserURLWithENS() { 436 _, contact := s.createContact() 437 438 url, err := s.m.ShareUserURLWithENS(contact.ID) 439 s.Require().NoError(err) 440 441 expectedURL := fmt.Sprintf("%s/u#%s", baseShareURL, contact.EnsName) 442 s.Require().Equal(expectedURL, url) 443 } 444 445 // TODO: ens in the next ticket 446 // func (s *MessengerShareUrlsSuite) TestParseUserURLWithENS() { 447 // _, contact := s.createContact() 448 449 // url := fmt.Sprintf("%s/u#%s", baseShareURL, contact.EnsName) 450 451 // urlData, err := ParseSharedURL(url) 452 // s.Require().NoError(err) 453 // s.Require().NotNil(urlData) 454 455 // s.Require().NotNil(urlData.Contact) 456 // s.Require().Equal(contact.DisplayName, urlData.Contact.DisplayName) 457 // s.Require().Equal(contact.Bio, urlData.Contact.DisplayName) 458 // } 459 460 func (s *MessengerShareUrlsSuite) TestParseUserURLWithData() { 461 urlData, err := ParseSharedURL(userURLWithData) 462 s.Require().NoError(err) 463 s.Require().NotNil(urlData) 464 465 s.Require().NotNil(urlData.Contact) 466 s.Require().Equal("Mark Cole", urlData.Contact.DisplayName) 467 s.Require().Equal("Visual designer @Status, cat lover, pizza enthusiast, yoga afficionada", urlData.Contact.Description) 468 s.Require().Equal("zQ3shwQPhRuDJSjVGVBnTjCdgXy5i9WQaeVPdGJD6yTarJQSj", urlData.Contact.PublicKey) 469 } 470 471 func (s *MessengerShareUrlsSuite) TestShareUserURLWithData() { 472 _, contact := s.createContact() 473 474 url, err := s.m.ShareUserURLWithData(contact.ID) 475 s.Require().NoError(err) 476 477 userData, chatKey, err := s.m.prepareEncodedUserData(contact) 478 s.Require().NoError(err) 479 480 expectedURL := fmt.Sprintf("%s/u/%s#%s", baseShareURL, userData, chatKey) 481 s.Require().Equal(expectedURL, url) 482 } 483 484 func (s *MessengerShareUrlsSuite) TestShareAndParseUserURLWithData() { 485 _, contact := s.createContact() 486 487 shortKey, err := multiformat.SerializeLegacyKey(contact.ID) 488 s.Require().NoError(err) 489 490 url, err := s.m.ShareUserURLWithData(contact.ID) 491 s.Require().NoError(err) 492 493 urlData, err := ParseSharedURL(url) 494 s.Require().NoError(err) 495 496 s.Require().NotNil(urlData.Contact) 497 s.Require().Equal(contact.DisplayName, urlData.Contact.DisplayName) 498 s.Require().Equal(shortKey, urlData.Contact.PublicKey) 499 }