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}