github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/systests/profile_test.go (about) 1 package systests 2 3 import ( 4 "context" 5 6 "io" 7 "net/http" 8 "testing" 9 10 "github.com/davecgh/go-spew/spew" 11 "github.com/keybase/client/go/engine" 12 "github.com/keybase/client/go/kbtest" 13 "github.com/keybase/client/go/libkb" 14 keybase1 "github.com/keybase/client/go/protocol/keybase1" 15 "github.com/stretchr/testify/require" 16 ) 17 18 func TestProofSuggestions(t *testing.T) { 19 tt := newTeamTester(t) 20 defer tt.cleanup() 21 22 alice := tt.addUser("abc") 23 24 res, err := alice.userClient.ProofSuggestions(context.Background(), 0) 25 require.NoError(t, err) 26 t.Logf("suggestions: %v", spew.Sdump(res)) 27 expected := keybase1.ProofSuggestionsRes{ 28 ShowMore: true, 29 Suggestions: []keybase1.ProofSuggestion{{ 30 Key: "twitter", 31 ProfileText: "Prove your Twitter", 32 PickerText: "Twitter", 33 PickerSubtext: "twitter.com", 34 }, { 35 Key: "github", 36 ProfileText: "Prove your GitHub", 37 PickerText: "GitHub", 38 PickerSubtext: "github.com", 39 }, { 40 Key: "reddit", 41 ProfileText: "Prove your Reddit", 42 PickerText: "Reddit", 43 PickerSubtext: "reddit.com", 44 }, { 45 Key: "hackernews", 46 ProfileText: "Prove your Hacker News", 47 PickerText: "Hacker News", 48 PickerSubtext: "news.ycombinator.com", 49 }, { 50 Key: "rooter", 51 ProfileText: "Prove your Rooter", 52 PickerText: "Rooter", 53 PickerSubtext: "", 54 }, { 55 Key: "gubble.social", 56 ProfileText: "Prove your Gubble.social", 57 PickerText: "Gubble.social", 58 PickerSubtext: "Gubble instance", 59 }, { 60 Key: "web", 61 ProfileText: "Prove your website", 62 PickerText: "Your own website", 63 PickerSubtext: "", 64 }, { 65 Key: "pgp", 66 ProfileText: "Add a PGP key", 67 PickerText: "PGP key", 68 PickerSubtext: "", 69 }, { 70 Key: "btc", 71 ProfileText: "Set a Bitcoin address", 72 PickerText: "Bitcoin address", 73 PickerSubtext: "", 74 }, { 75 Key: "zcash", 76 ProfileText: "Set a Zcash address", 77 PickerText: "Zcash address", 78 PickerSubtext: "", 79 }, { 80 Key: "gubble.cloud", 81 BelowFold: true, 82 ProfileText: "Prove your Gubble.cloud", 83 PickerText: "Gubble.cloud", 84 PickerSubtext: "Gubble instance", 85 }, { 86 Key: "theqrl.org", 87 BelowFold: true, 88 ProfileText: "Prove your Quantum Resistant Ledger", 89 PickerText: "Quantum Resistant Ledger", 90 PickerSubtext: "theqrl.org", 91 }}} 92 require.Equal(t, expected.ShowMore, res.ShowMore) 93 require.True(t, len(res.Suggestions) >= len(expected.Suggestions), "should be at least as many results as expected") 94 iconExempt := map[string]struct{}{ 95 "gubble-with-dashes.dot": {}, 96 "mastodon.local": {}, 97 } 98 for _, b := range res.Suggestions { 99 if _, exempt := iconExempt[b.Key]; exempt { 100 // Skip checking for logos for this one. 101 continue 102 } 103 require.Len(t, b.ProfileIcon, 2) 104 for _, icon := range b.ProfileIcon { 105 checkIcon(t, icon) 106 } 107 require.Len(t, b.ProfileIconDarkmode, 2) 108 for _, icon := range b.ProfileIconDarkmode { 109 checkIcon(t, icon) 110 } 111 require.Len(t, b.ProfileIcon, 2) 112 for _, icon := range b.PickerIcon { 113 checkIcon(t, icon) 114 } 115 require.Len(t, b.PickerIconDarkmode, 2) 116 for _, icon := range b.PickerIconDarkmode { 117 checkIcon(t, icon) 118 } 119 120 } 121 var found int 122 for i, b := range res.Suggestions { 123 if found >= len(expected.Suggestions) { 124 t.Logf("done") 125 break 126 } 127 t.Logf("row %v %v", i, b.Key) 128 a := expected.Suggestions[found] 129 if a.Key != b.Key { 130 t.Logf("skipping %v (mismatch)", a.Key) 131 continue 132 } 133 found++ 134 require.Equal(t, a.Key, b.Key) 135 require.Equal(t, a.BelowFold, b.BelowFold) 136 require.Equal(t, a.ProfileText, b.ProfileText) 137 require.Equal(t, a.PickerText, b.PickerText) 138 require.Equal(t, a.PickerSubtext, b.PickerSubtext) 139 140 } 141 require.Len(t, expected.Suggestions, found) 142 } 143 144 func checkIcon(t testing.TB, icon keybase1.SizedImage) { 145 if icon.Width < 2 { 146 t.Fatalf("unreasonable icon size") 147 } 148 if kbtest.SkipIconRemoteTest() { 149 t.Logf("Skipping icon remote test") 150 require.True(t, len(icon.Path) > 8) 151 } else { 152 resp, err := http.Get(icon.Path) 153 require.Equal(t, 200, resp.StatusCode, "icon file should be reachable: %v", icon.Path) 154 require.NoError(t, err) 155 body, err := io.ReadAll(resp.Body) 156 require.NoError(t, err) 157 if len(body) < 150 { 158 t.Fatalf("unreasonable icon payload size") 159 } 160 } 161 } 162 163 func TestProofSuggestionsOmitProven(t *testing.T) { 164 tt := newTeamTester(t) 165 defer tt.cleanup() 166 alice := tt.addUser("abc") 167 168 assertOmitted := func(service string) { 169 res, err := alice.userClient.ProofSuggestions(context.Background(), 0) 170 require.NoError(t, err) 171 for _, suggestion := range res.Suggestions { 172 require.NotEqual(t, service, suggestion.Key) 173 } 174 } 175 176 alice.proveRooter() 177 t.Logf("alice proved rooter, so rooter is no longer suggested") 178 assertOmitted("rooter") 179 180 eng := engine.NewCryptocurrencyEngine(alice.MetaContext().G(), keybase1.RegisterAddressArg{ 181 Address: "zcCk6rKzynC4tT1Rmg325A5Xw81Ck3S6nD6mtPWCXaMtyFczkyU4kYjEhrcz2QKfF5T2siWGyJNxWo43XWT3qk5YpPhFGj2", 182 }) 183 err := engine.RunEngine2(alice.MetaContext().WithUIs(libkb.UIs{ 184 LogUI: alice.MetaContext().G().Log, 185 SecretUI: alice.newSecretUI(), 186 }), eng) 187 require.NoError(t, err) 188 t.Logf("alice added a zcash address, so zcash is no longer suggested") 189 assertOmitted("zcash") 190 }