Files
Igor Sirotin f3e4363808 refactor: move protocol to internal/protocol
Part of the Go project layout migration, item 27.

Pure move plus import-path rewrite across 502 files. No API or behaviour
change. `internal/` keeps the messaging application logic unimportable
from outside the module, which is what the issue asks for -- status-go is
consumed through the C-bindings in mobile/, not as a Go library.

Things that had to follow the move, beyond the Go imports:

- tools/generate-handlers/template.txt. messenger_handlers.go is
  generated, and the template hard-codes the imports it emits, so the
  generated file kept importing protocol/common and failed typecheck.
- .gitignore. The ignore rule for that generated file was pinned to the
  old path; without moving it, a 1486-line generated file starts being
  tracked.
- Makefile: the logosstorage and torrent test targets (both the archive
  packages and ./protocol itself), the archive README, migration-protocol.
- scripts/run_unit_tests.sh, which names the protocol package explicitly
  to shard its tests.
- scripts/cleanup_generated_files.sh and .golangci.yml.

scripts/migration_check.sh also needed a fix that is not specific to this
move: it validated every file the branch touched under a migration dir
against the timestamp naming rule, and a directory rename makes every
migration in it look newly added. It now excludes renames, so moving a
migration is not mistaken for adding one.

refs #7067
2026-08-21 10:11:05 +02:00

682 lines
24 KiB
Go

package common
import (
"encoding/base64"
"encoding/json"
"io"
"os"
"testing"
"github.com/stretchr/testify/require"
"github.com/status-im/status-go/internal/crypto"
"github.com/status-im/status-go/internal/crypto/types"
"github.com/status-im/status-go/internal/protocol/protobuf"
)
const expectedJPEG = "data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k="
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"
func TestPrepareContentImage(t *testing.T) {
file, err := os.Open("testdata/test.jpg")
require.NoError(t, err)
defer file.Close()
payload, err := io.ReadAll(file)
require.NoError(t, err)
message := NewMessage()
message.ContentType = protobuf.ChatMessage_IMAGE
image := protobuf.ImageMessage{
Payload: payload,
Format: protobuf.ImageFormat_JPEG,
}
message.Payload = &protobuf.ChatMessage_Image{Image: &image}
require.NoError(t, message.PrepareContent(""))
require.Equal(t, expectedJPEG, message.Base64Image)
}
func TestPrepareContentAudio(t *testing.T) {
file, err := os.Open("testdata/test.aac")
require.NoError(t, err)
defer file.Close()
payload, err := io.ReadAll(file)
require.NoError(t, err)
message := NewMessage()
message.ContentType = protobuf.ChatMessage_AUDIO
audio := protobuf.AudioMessage{
Payload: payload,
Type: protobuf.AudioMessage_AAC,
}
message.Payload = &protobuf.ChatMessage_Audio{Audio: &audio}
require.NoError(t, message.PrepareContent(""))
require.Equal(t, expectedAAC, message.Base64Audio)
}
func TestGetAudioMessageMIME(t *testing.T) {
aac := &protobuf.AudioMessage{Type: protobuf.AudioMessage_AAC}
mime, err := getAudioMessageMIME(aac)
require.NoError(t, err)
require.Equal(t, "aac", mime)
amr := &protobuf.AudioMessage{Type: protobuf.AudioMessage_AMR}
mime, err = getAudioMessageMIME(amr)
require.NoError(t, err)
require.Equal(t, "amr", mime)
}
func TestPrepareContentMentions(t *testing.T) {
message := NewMessage()
pk1, err := crypto.GenerateKey()
require.NoError(t, err)
pk1String := types.EncodeHex(crypto.FromECDSAPub(&pk1.PublicKey))
pk2, err := crypto.GenerateKey()
require.NoError(t, err)
pk2String := types.EncodeHex(crypto.FromECDSAPub(&pk2.PublicKey))
message.Text = "hey @" + pk1String + " @" + pk2String
require.NoError(t, message.PrepareContent(pk2String))
require.Len(t, message.Mentions, 2)
require.Equal(t, message.Mentions[0], pk1String)
require.Equal(t, message.Mentions[1], pk2String)
require.True(t, message.Mentioned)
}
func TestPrepareContentLinks(t *testing.T) {
message := NewMessage()
link1 := "https://github.com/status-im/status-mobile"
link2 := "https://www.youtube.com/watch?v=6RYO8KCY6YE"
message.Text = "Just look at that repo " + link1 + " . And watch this video - " + link2
require.NoError(t, message.PrepareContent(""))
require.Len(t, message.Links, 2)
require.Equal(t, message.Links[0], link1)
require.Equal(t, message.Links[1], link2)
}
func TestPrepareSimplifiedText(t *testing.T) {
canonicalName1 := "canonical-name-1"
canonicalName2 := "canonical-name-2"
message := NewMessage()
pk1, err := crypto.GenerateKey()
require.NoError(t, err)
pk1String := types.EncodeHex(crypto.FromECDSAPub(&pk1.PublicKey))
pk2, err := crypto.GenerateKey()
require.NoError(t, err)
pk2String := types.EncodeHex(crypto.FromECDSAPub(&pk2.PublicKey))
message.Text = "hey @" + pk1String + " @" + pk2String
require.NoError(t, message.PrepareContent(""))
require.Len(t, message.Mentions, 2)
require.Equal(t, message.Mentions[0], pk1String)
require.Equal(t, message.Mentions[1], pk2String)
canonicalNames := make(map[string]string)
canonicalNames[pk1String] = canonicalName1
canonicalNames[pk2String] = canonicalName2
simplifiedText, err := message.GetSimplifiedText("", canonicalNames)
require.NoError(t, err)
require.Equal(t, "hey "+canonicalName1+" "+canonicalName2, simplifiedText)
}
func TestConvertLinkPreviewsToProto(t *testing.T) {
msg := Message{
LinkPreviews: []LinkPreview{
{
Type: protobuf.UnfurledLink_LINK,
Description: "GitHub is where people build software.",
Hostname: "github.com",
Title: "Build software better, together",
URL: "https://github.com",
Thumbnail: LinkPreviewThumbnail{
Width: 100,
Height: 200,
URL: "http://localhost:9999",
DataURI: "data:image/png;base64,iVBORw0KGgoAAAANSUg=",
},
Favicon: LinkPreviewThumbnail{
DataURI: "data:image/png;base64,iVBORw0KGgoAAAANSUg=",
},
},
},
}
unfurledLinks, err := msg.ConvertLinkPreviewsToProto()
require.NoError(t, err)
require.Len(t, unfurledLinks, 1)
l := unfurledLinks[0]
validPreview := msg.LinkPreviews[0]
require.Equal(t, validPreview.Type, l.Type)
require.Equal(t, validPreview.Description, l.Description)
require.Equal(t, validPreview.Title, l.Title)
require.Equal(t, uint32(validPreview.Thumbnail.Width), l.ThumbnailWidth)
require.Equal(t, uint32(validPreview.Thumbnail.Height), l.ThumbnailHeight)
expectedPayload, err := base64.StdEncoding.DecodeString("iVBORw0KGgoAAAANSUg=")
require.NoError(t, err)
require.Equal(t, expectedPayload, l.ThumbnailPayload)
expectedFaviconPayload, err := base64.StdEncoding.DecodeString("iVBORw0KGgoAAAANSUg=")
require.NoError(t, err)
require.Equal(t, expectedFaviconPayload, l.FaviconPayload)
// Test any invalid link preview causes an early return.
invalidPreview := validPreview
invalidPreview.Title = ""
msg.LinkPreviews = []LinkPreview{invalidPreview}
_, err = msg.ConvertLinkPreviewsToProto()
require.ErrorContains(t, err, "invalid link preview, url='https://github.com'")
// Test invalid data URI invalidates a preview.
invalidPreview = validPreview
invalidPreview.Thumbnail.DataURI = "data:hello/png,iVBOR"
msg.LinkPreviews = []LinkPreview{invalidPreview}
_, err = msg.ConvertLinkPreviewsToProto()
require.ErrorContains(t, err, "could not get data URI payload for link preview thumbnail, url='https://github.com': wrong uri format")
// Test thumbnail is optional.
somePreview := validPreview
somePreview.Thumbnail.DataURI = ""
somePreview.Thumbnail.Width = 0
somePreview.Thumbnail.Height = 0
msg.LinkPreviews = []LinkPreview{somePreview}
unfurledLinks, err = msg.ConvertLinkPreviewsToProto()
require.NoError(t, err)
require.Len(t, unfurledLinks, 1)
require.Nil(t, unfurledLinks[0].ThumbnailPayload)
}
func TestConvertFromProtoToLinkPreviews(t *testing.T) {
thumbnailPayload, err := base64.StdEncoding.DecodeString("iVBORw0KGgoAAAANSUg=")
require.NoError(t, err)
FaviconPayload, err := base64.StdEncoding.DecodeString("iVBORw0KGgoAAAANSUg=")
require.NoError(t, err)
l := &protobuf.UnfurledLink{
Description: "GitHub is where people build software.",
Title: "Build software better, together",
Type: protobuf.UnfurledLink_LINK,
Url: "https://github.com",
ThumbnailPayload: thumbnailPayload,
FaviconPayload: FaviconPayload,
ThumbnailWidth: 100,
ThumbnailHeight: 200,
}
msg := Message{
ID: "42",
ChatMessage: &protobuf.ChatMessage{
UnfurledLinks: []*protobuf.UnfurledLink{l},
},
}
urlMaker := func(msgID string, linkURL string) string {
return "https://localhost:6666/" + msgID + "-" + linkURL
}
previews := msg.ConvertFromProtoToLinkPreviews(urlMaker, urlMaker)
require.Len(t, previews, 1)
p := previews[0]
require.Equal(t, l.Type, p.Type)
require.Equal(t, "github.com", p.Hostname)
require.Equal(t, l.Description, p.Description)
require.Equal(t, l.Url, p.URL)
require.Equal(t, int(l.ThumbnailHeight), p.Thumbnail.Height)
require.Equal(t, int(l.ThumbnailWidth), p.Thumbnail.Width)
// Important, don't build up a data URI because the thumbnail should be
// fetched from the media server.
require.Equal(t, "", p.Thumbnail.DataURI)
require.Equal(t, "https://localhost:6666/42-https://github.com", p.Thumbnail.URL)
require.Equal(t, "https://localhost:6666/42-https://github.com", p.Favicon.URL)
// Test when the URL is not parseable by url.Parse.
l.Url = "postgres://user:abc{DEf1=ghi@example.com:5432/db?sslmode=require"
msg.ChatMessage.UnfurledLinks = []*protobuf.UnfurledLink{l}
previews = msg.ConvertFromProtoToLinkPreviews(urlMaker, urlMaker)
require.Len(t, previews, 1)
p = previews[0]
require.Equal(t, l.Url, p.Hostname)
// Test when there's no thumbnail payload.
l = &protobuf.UnfurledLink{
Description: "GitHub is where people build software.",
Title: "Build software better, together",
Url: "https://github.com",
}
msg.ChatMessage.UnfurledLinks = []*protobuf.UnfurledLink{l}
previews = msg.ConvertFromProtoToLinkPreviews(urlMaker, urlMaker)
require.Len(t, previews, 1)
p = previews[0]
require.Equal(t, 0, p.Thumbnail.Height)
require.Equal(t, 0, p.Thumbnail.Width)
require.Equal(t, "", p.Thumbnail.URL)
}
func TestConvertStatusLinkPreviewsToProto(t *testing.T) {
pk1, err := crypto.GenerateKey()
require.NoError(t, err)
compressedContactPublicKey := crypto.CompressPubkey(&pk1.PublicKey)
contactID := types.EncodeHex(crypto.FromECDSAPub(&pk1.PublicKey))
pk2, err := crypto.GenerateKey()
require.NoError(t, err)
compressedCommunityPublicKey := crypto.CompressPubkey(&pk2.PublicKey)
communityID := types.EncodeHex(compressedCommunityPublicKey)
pk3, err := crypto.GenerateKey()
require.NoError(t, err)
compressedCommunityPublicKey2 := crypto.CompressPubkey(&pk3.PublicKey)
communityID2 := types.EncodeHex(compressedCommunityPublicKey2)
contact := &StatusContactLinkPreview{
PublicKey: contactID,
DisplayName: "DisplayName_2",
Description: "Description_3",
Icon: LinkPreviewThumbnail{
Width: 10,
Height: 20,
DataURI: "data:image/png;base64,iVBORw0KGgoAAAANSUg=",
},
}
community := &StatusCommunityLinkPreview{
CommunityID: communityID,
DisplayName: "DisplayName_5",
Description: "Description_6",
MembersCount: 7,
Color: "Color_8",
Icon: LinkPreviewThumbnail{
Width: 30,
Height: 40,
DataURI: "data:image/png;base64,iVBORw0KGgoAAAANSUg=",
},
Banner: LinkPreviewThumbnail{
Width: 50,
Height: 60,
DataURI: "data:image/png;base64,iVBORw0KGgoAAAANSUg=",
},
}
channel := &StatusCommunityChannelLinkPreview{
ChannelUUID: "ChannelUUID_11",
Emoji: "Emoji_12",
DisplayName: "DisplayName_13",
Description: "Description_14",
Color: "Color_15",
Community: &StatusCommunityLinkPreview{
CommunityID: communityID2,
DisplayName: "DisplayName_17",
Description: "Description_18",
MembersCount: 19,
Color: "Color_20",
Icon: LinkPreviewThumbnail{
Width: 70,
Height: 80,
DataURI: "data:image/png;base64,iVBORw0KGgoAAAANSUg=",
},
Banner: LinkPreviewThumbnail{
Width: 90,
Height: 100,
DataURI: "data:image/png;base64,iVBORw0KGgoAAAANSUg=",
},
},
}
message := Message{
StatusLinkPreviews: []StatusLinkPreview{
{
URL: "https://status.app/u/",
Contact: contact,
},
{
URL: "https://status.app/c/",
Community: community,
},
{
URL: "https://status.app/cc/",
Channel: channel,
},
},
}
expectedThumbnailPayload, err := base64.StdEncoding.DecodeString("iVBORw0KGgoAAAANSUg=")
require.NoError(t, err)
unfurledLinks, err := message.ConvertStatusLinkPreviewsToProto()
require.NoError(t, err)
require.Len(t, unfurledLinks.UnfurledStatusLinks, 3)
// Contact link
l1 := unfurledLinks.UnfurledStatusLinks[0]
require.Equal(t, "https://status.app/u/", l1.Url)
require.NotNil(t, l1.GetContact())
require.Nil(t, l1.GetCommunity())
require.Nil(t, l1.GetChannel())
c1 := l1.GetContact()
require.Equal(t, compressedContactPublicKey, c1.PublicKey)
require.Equal(t, contact.DisplayName, c1.DisplayName)
require.Equal(t, contact.Description, c1.Description)
require.NotNil(t, c1.Icon)
require.Equal(t, uint32(contact.Icon.Width), c1.Icon.Width)
require.Equal(t, uint32(contact.Icon.Height), c1.Icon.Height)
require.Equal(t, expectedThumbnailPayload, c1.Icon.Payload)
// Community link
l2 := unfurledLinks.UnfurledStatusLinks[1]
require.Equal(t, "https://status.app/c/", l2.Url)
require.NotNil(t, l2.GetCommunity())
require.Nil(t, l2.GetContact())
require.Nil(t, l2.GetChannel())
c2 := l2.GetCommunity()
require.Equal(t, compressedCommunityPublicKey, c2.CommunityId)
require.Equal(t, community.DisplayName, c2.DisplayName)
require.Equal(t, community.Description, c2.Description)
require.Equal(t, community.MembersCount, c2.MembersCount)
require.Equal(t, community.Color, c2.Color)
require.NotNil(t, c2.Icon)
require.Equal(t, uint32(community.Icon.Width), c2.Icon.Width)
require.Equal(t, uint32(community.Icon.Height), c2.Icon.Height)
require.Equal(t, expectedThumbnailPayload, c2.Icon.Payload)
require.NotNil(t, c2.Banner)
require.Equal(t, uint32(community.Banner.Width), c2.Banner.Width)
require.Equal(t, uint32(community.Banner.Height), c2.Banner.Height)
require.Equal(t, expectedThumbnailPayload, c2.Banner.Payload)
// Channel link
l3 := unfurledLinks.UnfurledStatusLinks[2]
require.Equal(t, "https://status.app/cc/", l3.Url)
require.NotNil(t, l3.GetChannel())
require.Nil(t, l3.GetContact())
require.Nil(t, l3.GetCommunity())
c3 := l3.GetChannel()
require.Equal(t, channel.ChannelUUID, c3.ChannelUuid)
require.Equal(t, channel.Emoji, c3.Emoji)
require.Equal(t, channel.DisplayName, c3.DisplayName)
require.Equal(t, channel.Description, c3.Description)
require.Equal(t, channel.Color, c3.Color)
require.NotNil(t, c3.Community)
require.Equal(t, compressedCommunityPublicKey2, c3.Community.CommunityId)
require.Equal(t, channel.Community.DisplayName, c3.Community.DisplayName)
require.Equal(t, channel.Community.Color, c3.Community.Color)
require.Equal(t, channel.Community.Description, c3.Community.Description)
require.Equal(t, channel.Community.MembersCount, c3.Community.MembersCount)
require.NotNil(t, c3.Community.Icon)
require.Equal(t, uint32(channel.Community.Icon.Width), c3.Community.Icon.Width)
require.Equal(t, uint32(channel.Community.Icon.Height), c3.Community.Icon.Height)
require.Equal(t, expectedThumbnailPayload, c3.Community.Icon.Payload)
require.NotNil(t, c3.Community.Banner)
require.Equal(t, uint32(channel.Community.Banner.Width), c3.Community.Banner.Width)
require.Equal(t, uint32(channel.Community.Banner.Height), c3.Community.Banner.Height)
require.Equal(t, expectedThumbnailPayload, c3.Community.Banner.Payload)
// Test any invalid link preview causes an early return.
invalidContactPreview := contact
invalidContactPreview.PublicKey = ""
invalidPreview := message.StatusLinkPreviews[0]
invalidPreview.Contact = invalidContactPreview
message.StatusLinkPreviews = []StatusLinkPreview{invalidPreview}
_, err = message.ConvertStatusLinkPreviewsToProto()
require.ErrorContains(t, err, "invalid status link preview, url='https://status.app/u/'")
}
func TestConvertFromProtoToStatusLinkPreviews(t *testing.T) {
pk1, err := crypto.GenerateKey()
require.NoError(t, err)
compressedContactPublicKey := crypto.CompressPubkey(&pk1.PublicKey)
contactID := types.EncodeHex(crypto.FromECDSAPub(&pk1.PublicKey))
pk2, err := crypto.GenerateKey()
require.NoError(t, err)
compressedCommunityPublicKey := crypto.CompressPubkey(&pk2.PublicKey)
communityID := types.EncodeHex(compressedCommunityPublicKey)
pk3, err := crypto.GenerateKey()
require.NoError(t, err)
compressedCommunityPublicKey2 := crypto.CompressPubkey(&pk3.PublicKey)
communityID2 := types.EncodeHex(compressedCommunityPublicKey2)
thumbnailPayload, err := base64.StdEncoding.DecodeString("iVBORw0KGgoAAAANSUg=")
require.NoError(t, err)
contact := &protobuf.UnfurledStatusContactLink{
PublicKey: compressedContactPublicKey,
DisplayName: "DisplayName_2",
Description: "Description_3",
Icon: &protobuf.UnfurledLinkThumbnail{
Width: 10,
Height: 20,
Payload: thumbnailPayload,
},
}
community := &protobuf.UnfurledStatusCommunityLink{
CommunityId: compressedCommunityPublicKey,
DisplayName: "DisplayName_5",
Description: "Description_6",
MembersCount: 7,
Color: "Color_8",
Icon: &protobuf.UnfurledLinkThumbnail{
Width: 30,
Height: 40,
Payload: thumbnailPayload,
},
Banner: &protobuf.UnfurledLinkThumbnail{
Width: 50,
Height: 60,
Payload: thumbnailPayload,
},
}
channel := &protobuf.UnfurledStatusChannelLink{
ChannelUuid: "ChannelUuid_11",
Emoji: "Emoji_12",
DisplayName: "DisplayName_13",
Description: "Description_14",
Color: "Color_15",
Community: &protobuf.UnfurledStatusCommunityLink{
CommunityId: compressedCommunityPublicKey2,
DisplayName: "DisplayName_17",
Description: "Description_18",
MembersCount: 19,
Color: "Color_20",
Icon: &protobuf.UnfurledLinkThumbnail{
Width: 70,
Height: 80,
Payload: thumbnailPayload,
},
Banner: &protobuf.UnfurledLinkThumbnail{
Width: 90,
Height: 100,
Payload: thumbnailPayload,
},
},
}
msg := Message{
ID: "42",
ChatMessage: &protobuf.ChatMessage{
UnfurledStatusLinks: &protobuf.UnfurledStatusLinks{
UnfurledStatusLinks: []*protobuf.UnfurledStatusLink{
{
Url: "https://status.app/u/",
Payload: &protobuf.UnfurledStatusLink_Contact{
Contact: contact,
},
},
{
Url: "https://status.app/c/",
Payload: &protobuf.UnfurledStatusLink_Community{
Community: community,
},
},
{
Url: "https://status.app/cc/",
Payload: &protobuf.UnfurledStatusLink_Channel{
Channel: channel,
},
},
},
},
},
}
urlMaker := func(msgID string, linkURL string, imageID MediaServerImageID) string {
return "https://localhost:6666/" + msgID + "-" + linkURL + "-" + string(imageID)
}
previews := msg.ConvertFromProtoToStatusLinkPreviews(urlMaker)
require.Len(t, previews, 3)
// Contact preview
p1 := previews[0]
require.Equal(t, "https://status.app/u/", p1.URL)
require.NotNil(t, p1.Contact)
require.Nil(t, p1.Community)
require.Nil(t, p1.Channel)
c1 := p1.Contact
require.NotNil(t, c1)
require.Equal(t, contactID, c1.PublicKey)
require.Equal(t, contact.DisplayName, c1.DisplayName)
require.Equal(t, contact.Description, c1.Description)
require.NotNil(t, c1.Icon)
require.Equal(t, int(contact.Icon.Width), c1.Icon.Width)
require.Equal(t, int(contact.Icon.Height), c1.Icon.Height)
require.Equal(t, "", c1.Icon.DataURI)
require.Equal(t, "https://localhost:6666/42-https://status.app/u/-contact-icon", c1.Icon.URL)
// Community preview
p2 := previews[1]
require.Equal(t, "https://status.app/c/", p2.URL)
require.NotNil(t, p2.Community)
require.Nil(t, p2.Contact)
require.Nil(t, p2.Channel)
c2 := p2.Community
require.Equal(t, communityID, c2.CommunityID)
require.Equal(t, community.DisplayName, c2.DisplayName)
require.Equal(t, community.Description, c2.Description)
require.Equal(t, community.MembersCount, c2.MembersCount)
require.Equal(t, community.Color, c2.Color)
require.NotNil(t, c2.Icon)
require.Equal(t, int(community.Icon.Width), c2.Icon.Width)
require.Equal(t, int(community.Icon.Height), c2.Icon.Height)
require.Equal(t, "", c2.Icon.DataURI)
require.Equal(t, "https://localhost:6666/42-https://status.app/c/-community-icon", c2.Icon.URL)
require.NotNil(t, c2.Banner)
require.Equal(t, int(community.Banner.Width), c2.Banner.Width)
require.Equal(t, int(community.Banner.Height), c2.Banner.Height)
require.Equal(t, "", c2.Banner.DataURI)
require.Equal(t, "https://localhost:6666/42-https://status.app/c/-community-banner", c2.Banner.URL)
// Channel preview
p3 := previews[2]
require.Equal(t, "https://status.app/cc/", p3.URL)
require.NotNil(t, p3.Channel)
require.Nil(t, p3.Contact)
require.Nil(t, p3.Community)
c3 := previews[2].Channel
require.Equal(t, channel.ChannelUuid, c3.ChannelUUID)
require.Equal(t, channel.Emoji, c3.Emoji)
require.Equal(t, channel.DisplayName, c3.DisplayName)
require.Equal(t, channel.Description, c3.Description)
require.Equal(t, channel.Color, c3.Color)
require.NotNil(t, p3.Channel.Community)
require.Equal(t, communityID2, c3.Community.CommunityID)
require.Equal(t, channel.Community.DisplayName, c3.Community.DisplayName)
require.Equal(t, channel.Community.Color, c3.Community.Color)
require.Equal(t, channel.Community.Description, c3.Community.Description)
require.Equal(t, channel.Community.MembersCount, c3.Community.MembersCount)
require.NotNil(t, c3.Community.Icon)
require.Equal(t, int(channel.Community.Icon.Width), c3.Community.Icon.Width)
require.Equal(t, int(channel.Community.Icon.Height), c3.Community.Icon.Height)
require.Equal(t, "", c3.Community.Icon.DataURI)
require.Equal(t, "https://localhost:6666/42-https://status.app/cc/-community-channel-icon", c3.Community.Icon.URL)
require.NotNil(t, c3.Community.Banner)
require.Equal(t, int(channel.Community.Banner.Width), c3.Community.Banner.Width)
require.Equal(t, int(channel.Community.Banner.Height), c3.Community.Banner.Height)
require.Equal(t, "", c3.Community.Banner.DataURI)
require.Equal(t, "https://localhost:6666/42-https://status.app/cc/-community-channel-banner", c3.Community.Banner.URL)
}
func assertMarshalAndUnmarshalJSON[T any](t *testing.T, obj *T, msgAndArgs ...any) {
rawJSON, err := json.Marshal(obj)
require.NoError(t, err, msgAndArgs...)
var unmarshalled T
err = json.Unmarshal(rawJSON, &unmarshalled)
require.NoError(t, err, msgAndArgs...)
require.Equal(t, obj, &unmarshalled, msgAndArgs...)
}
func TestMarshalMessageJSON(t *testing.T) {
msg := NewMessage()
msg.ID = "1"
msg.From = "0x04c51631b3354242d5a56f044c3b7703bcc001e8c725c4706928b3fac3c2a12ec9019e1e224d487f5c893389405bcec998bc687307f290a569d6a97d24b711bca8"
msg.LinkPreviews = []LinkPreview{
{
Type: protobuf.UnfurledLink_LINK,
Description: "GitHub is where people build software.",
Hostname: "github.com",
Title: "Build software better, together",
URL: "https://github.com",
Thumbnail: LinkPreviewThumbnail{
Width: 100,
Height: 200,
URL: "http://localhost:9999",
DataURI: "data:image/png;base64,iVBORw0KGgoAAAANSUg=",
},
},
}
msg.PaymentRequests = []*protobuf.PaymentRequest{
{
Receiver: "0x7F47C2e98a4BBf5487E6fb082eC2D9Ab0E6d8882",
Symbol: "ETH",
Amount: "1000000000000000000",
ChainId: 1,
TokenKey: "eth",
LogoUri: "https://example.com/eth-logo.png",
},
{
Receiver: "0x1234567890123456789012345678901234567890",
Symbol: "USDC",
Amount: "500000000",
ChainId: 1,
TokenKey: "usdc",
LogoUri: "https://example.com/usdc-logo.png",
},
}
assertMarshalAndUnmarshalJSON(t, msg, "message ID='%s'", msg.ID)
}