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