Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

link_test.gno

5.34 Kb · 140 lines
  1package wesh
  2
  3import (
  4	"encoding/hex"
  5	"testing"
  6
  7	"gno.land/p/moul/x/daily/b58/v0"
  8	"gno.land/p/nt/uassert/v0"
  9)
 10
 11// bertyGoldenBlob is the base58 payload from berty's own links_test.go
 12// ("simple-contact"), produced by MarshalLink from
 13// PublicRendezvousSeed = 16 x 0x01 and AccountPk = 16 x 0x02.
 14//
 15// It is the ground truth for this package's wire format. The keys are 16 bytes
 16// because that test predates the length rule, so the vector is checked against
 17// the raw encoder rather than through Contact.Blob, which would (correctly)
 18// reject them.
 19const bertyGoldenBlob = "3geQXHmsW9rxRfQFJdu8CEuPtWkfTWgJH13NzAoGatcnh4brusu3"
 20
 21func TestBlobEncodingMatchesBertyGoldenVector(t *testing.T) {
 22	id := appendBytesField(nil, fieldBertyIDSeed, repeat(0x01, 16))
 23	id = appendBytesField(id, fieldBertyIDAccountPK, repeat(0x02, 16))
 24	blob := appendBytesField(nil, fieldBertyLinkBertyID, id)
 25
 26	uassert.Equal(t,
 27		"12240a1001010101010101010101010101010101121002020202020202020202020202020202",
 28		hex.EncodeToString(blob),
 29		"protobuf wire bytes")
 30	uassert.Equal(t, bertyGoldenBlob, b58.Encode(blob), "base58 payload berty produces")
 31}
 32
 33// TestWebLink pins the full link for a real 32-byte identity.
 34func TestWebLink(t *testing.T) {
 35	c := Contact{
 36		AccountPK: mustHex(t, testAccountPKHex),
 37		Seed:      mustHex(t, testSeedHex),
 38	}
 39	link, err := c.WebLink()
 40	uassert.NoError(t, err)
 41	uassert.Equal(t,
 42		"https://berty.tech/id#contact/oZBLFpzghxrATkepWvDPNX9pHYqi6BWgP45xGWhqxcwmqN2bnMMbU7UcwUuTaCcDyUvMjmWRMDWcP96bXAndcjNiAZ1Vz9X",
 43		link)
 44}
 45
 46// TestWebLinkDisplayNameEscaping matches berty's url.Values encoding: space
 47// becomes '+', and everything outside the unreserved set is percent-escaped.
 48func TestWebLinkDisplayNameEscaping(t *testing.T) {
 49	c := Contact{
 50		AccountPK:   mustHex(t, testAccountPKHex),
 51		Seed:        mustHex(t, testSeedHex),
 52		DisplayName: "Hello World!",
 53	}
 54	link, err := c.WebLink()
 55	uassert.NoError(t, err)
 56	uassert.True(t, endsWith(link, "/name=Hello+World%21"), "display name is query-escaped: "+link)
 57}
 58
 59func TestWebLinkRoundTrip(t *testing.T) {
 60	cases := []struct {
 61		name string
 62		in   Contact
 63	}{
 64		{"no display name", Contact{AccountPK: repeat(0x11, 32), Seed: repeat(0x22, 32)}},
 65		{"with a display name", Contact{AccountPK: repeat(0x11, 32), Seed: repeat(0x22, 32), DisplayName: "gno support"}},
 66		{"name needing escapes", Contact{AccountPK: repeat(0x11, 32), Seed: repeat(0x22, 32), DisplayName: "a/b c&d=e"}},
 67		{"leading zero bytes", Contact{AccountPK: repeat(0x00, 32), Seed: repeat(0x00, 32)}},
 68	}
 69	for _, tc := range cases {
 70		link, err := tc.in.WebLink()
 71		uassert.NoError(t, err, tc.name)
 72
 73		got, err := ParseWebLink(link)
 74		uassert.NoError(t, err, tc.name)
 75		uassert.Equal(t, hex.EncodeToString(tc.in.AccountPK), hex.EncodeToString(got.AccountPK), tc.name+": account key")
 76		uassert.Equal(t, hex.EncodeToString(tc.in.Seed), hex.EncodeToString(got.Seed), tc.name+": seed")
 77		uassert.Equal(t, tc.in.DisplayName, got.DisplayName, tc.name+": display name")
 78	}
 79}
 80
 81func TestParseWebLinkRejects(t *testing.T) {
 82	valid, err := Contact{AccountPK: repeat(0x11, 32), Seed: repeat(0x22, 32)}.WebLink()
 83	uassert.NoError(t, err)
 84
 85	cases := []struct {
 86		name string
 87		in   string
 88		want error
 89	}{
 90		{"not a berty link", "https://example.com/id#contact/abc", ErrNotAWebLink},
 91		{"no payload segment", WebLinkPrefix + "contact", ErrNotAWebLink},
 92		{"a group link, not a contact", WebLinkPrefix + "group/" + b58.Encode([]byte{1, 2, 3}), ErrNotAContactLink},
 93		{"payload is not base58", WebLinkPrefix + "contact/not-base58-0OIl", ErrBadLinkPayload},
 94		{"payload is not a BertyLink", WebLinkPrefix + "contact/" + b58.Encode([]byte{0xff, 0xff}), ErrBadLinkPayload},
 95		{"berty's own 16-byte golden vector is too short now", WebLinkPrefix + "contact/" + bertyGoldenBlob, ErrBadSeedLen},
 96		{"truncated payload", valid[:len(valid)-8], ErrBadLinkPayload},
 97	}
 98	for _, tc := range cases {
 99		_, err := ParseWebLink(tc.in)
100		uassert.ErrorIs(t, err, tc.want, tc.name)
101	}
102}
103
104// TestParseWebLinkIgnoresUnknownQueryKeys matches berty's UnmarshalLink, which
105// accepts a link carrying query keys it does not know.
106func TestParseWebLinkIgnoresUnknownQueryKeys(t *testing.T) {
107	link, err := Contact{AccountPK: repeat(0x11, 32), Seed: repeat(0x22, 32)}.WebLink()
108	uassert.NoError(t, err)
109
110	got, err := ParseWebLink(link + "/foo=bar&name=Alice")
111	uassert.NoError(t, err)
112	uassert.Equal(t, "Alice", got.DisplayName)
113}
114
115// TestLookupBytesFieldSkipsOtherWireTypes covers the decoder's skip paths: a
116// varint, a 64-bit and a 32-bit field ahead of the one being looked up.
117func TestLookupBytesFieldSkipsOtherWireTypes(t *testing.T) {
118	buf := []byte{0x08, 0x96, 0x01} // field 1, varint 150
119	buf = append(buf, 0x11)         // field 2, 64-bit
120	buf = append(buf, repeat(0xaa, 8)...)
121	buf = append(buf, 0x1d) // field 3, 32-bit
122	buf = append(buf, repeat(0xbb, 4)...)
123	buf = appendBytesField(buf, 4, []byte("found"))
124
125	got, ok := lookupBytesField(buf, 4)
126	uassert.True(t, ok, "the length-delimited field is reachable past the others")
127	uassert.Equal(t, "found", string(got))
128
129	_, ok = lookupBytesField(buf, 9)
130	uassert.False(t, ok, "an absent field is reported missing, not guessed")
131}
132
133func TestBlobRejectsAnInvalidContact(t *testing.T) {
134	_, err := Contact{AccountPK: repeat(1, 31), Seed: repeat(2, 32)}.Blob()
135	uassert.ErrorIs(t, err, ErrBadAccountPKLen)
136}
137
138func endsWith(s, suffix string) bool {
139	return len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix
140}