github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/contacts/contacts_test.go (about) 1 // Copyright 2019 Keybase, Inc. All rights reserved. Use of 2 // this source code is governed by the included BSD license. 3 4 package contacts 5 6 import ( 7 "context" 8 "fmt" 9 "testing" 10 11 "github.com/keybase/client/go/externals" 12 "github.com/keybase/client/go/libkb" 13 "github.com/keybase/client/go/protocol/keybase1" 14 "github.com/stretchr/testify/require" 15 ) 16 17 func stringifyResults(res []keybase1.ProcessedContact) (ret []string) { 18 ret = make([]string, len(res)) 19 for i, r := range res { 20 if r.Resolved { 21 ret[i] = fmt.Sprintf("keybase:%s", r.Username) 22 } else { 23 var phoneOrEmail string 24 if r.Component.PhoneNumber != nil { 25 phoneOrEmail = string(*r.Component.PhoneNumber) 26 } else if r.Component.Email != nil { 27 phoneOrEmail = string(*r.Component.Email) 28 } 29 ret[i] = fmt.Sprintf("%s %s (%s)", r.DisplayName, phoneOrEmail, r.Component.Label) 30 } 31 } 32 return ret 33 } 34 35 func displayResults(res []keybase1.ProcessedContact) (ret []string) { 36 ret = make([]string, len(res)) 37 for i, v := range res { 38 ret[i] = fmt.Sprintf("%q %q", v.DisplayName, v.DisplayLabel) 39 } 40 return ret 41 } 42 43 func TestLookupEmptyList(t *testing.T) { 44 tc := libkb.SetupTest(t, "TestLookupContacts", 1) 45 defer tc.Cleanup() 46 47 provider := &ErrorContactsProvider{T: t} 48 contactList := []keybase1.Contact{} 49 50 res, err := ResolveContacts(libkb.NewMetaContextForTest(tc), provider, contactList) 51 require.NoError(t, err) 52 require.Len(t, res, 0) 53 } 54 55 func TestLookupContacts(t *testing.T) { 56 tc := libkb.SetupTest(t, "TestLookupContacts", 1) 57 defer tc.Cleanup() 58 59 contactList := []keybase1.Contact{ 60 { 61 Name: "Joe", 62 Components: []keybase1.ContactComponent{ 63 MakePhoneComponent("Home", "+1111222"), 64 MakePhoneComponent("Work", "+199123"), 65 MakeEmailComponent("E-mail", "joe@linux.org"), 66 }, 67 }, 68 } 69 70 provider := MakeMockProvider(t) 71 72 actx := externals.MakeStaticAssertionContext(context.Background()) 73 74 // None of the contact components resolved (empty mock provider). Return all 75 // 3 unresolved components to the caller. 76 res, err := ResolveContacts(libkb.NewMetaContextForTest(tc), provider, contactList) 77 require.NoError(t, err) 78 require.Len(t, res, 3) 79 for i, r := range res { 80 require.Equal(t, "Joe", r.DisplayName) 81 require.False(t, r.Resolved) 82 require.True(t, r.Uid.IsNil()) 83 component := contactList[0].Components[i] 84 assertion, err := AssertionFromComponent(actx, component, "") 85 require.NoError(t, err) 86 require.Equal(t, assertion, r.Assertion) 87 } 88 89 // Phone number component will not resolve. Still save all 3 components. 90 mockJoe := MakeMockLookupUser("joe", "JOE") 91 provider.PhoneNumbers["+1111222"] = mockJoe 92 93 res, err = ResolveContacts(libkb.NewMetaContextForTest(tc), provider, contactList) 94 require.NoError(t, err) 95 require.Len(t, res, 3) 96 require.True(t, res[0].Resolved) 97 require.NotNil(t, res[0].Component.PhoneNumber) 98 require.Equal(t, "1111222@phone", res[0].Assertion) 99 for i, v := range res { 100 if i != 0 { 101 require.False(t, v.Resolved) 102 require.Equal(t, "Joe", v.DisplayName) 103 assertion, err := AssertionFromComponent(actx, v.Component, "") 104 require.NoError(t, err) 105 require.Equal(t, assertion, v.Assertion) 106 } 107 } 108 109 // Second number also belongs to joe now. Still save all entries. 110 provider.PhoneNumbers["+199123"] = mockJoe 111 res, err = ResolveContacts(libkb.NewMetaContextForTest(tc), provider, contactList) 112 require.NoError(t, err) 113 require.Len(t, res, 3) 114 for i, v := range res { 115 if i == 2 { 116 require.False(t, v.Resolved) 117 require.Equal(t, "Joe", v.DisplayName) 118 assertion, err := AssertionFromComponent(actx, v.Component, "") 119 require.NoError(t, err) 120 require.Equal(t, assertion, v.Assertion) 121 } 122 } 123 124 // Suddenly this number resolves to someone else, despite being in same contact. 125 provider.PhoneNumbers["+199123"] = MakeMockLookupUser("ed", "") 126 res, err = ResolveContacts(libkb.NewMetaContextForTest(tc), provider, contactList) 127 require.NoError(t, err) 128 require.Len(t, res, 3) 129 require.Equal(t, []string{ 130 "keybase:joe", 131 "keybase:ed", 132 "Joe joe@linux.org (E-mail)", 133 }, stringifyResults(res)) 134 // Even if contact resolves to a Keybase user, assertion should still be an 135 // imptofu assertion (rather than username or username@keybase). This is 136 // required, so resolver is involved when starting a conversation. 137 require.Equal(t, "1111222@phone", res[0].Assertion) 138 require.Equal(t, "199123@phone", res[1].Assertion) 139 require.Equal(t, "[joe@linux.org]@email", res[2].Assertion) 140 141 // Test with email 142 provider = MakeMockProvider(t) // *new provider* 143 provider.Emails["joe@linux.org"] = mockJoe 144 res, err = ResolveContacts(libkb.NewMetaContextForTest(tc), provider, contactList) 145 require.NoError(t, err) 146 require.Len(t, res, 3) 147 // Only last component (the one with email) resolves, rest is unresolved. 148 for i, v := range res { 149 if i != 2 { 150 require.False(t, v.Resolved) 151 require.Equal(t, "Joe", v.DisplayName) 152 assertion, err := AssertionFromComponent(actx, v.Component, "") 153 require.NoError(t, err) 154 require.Equal(t, assertion, v.Assertion) 155 } 156 } 157 require.Equal(t, "joe", res[2].DisplayName) 158 require.Equal(t, "E-mail", res[2].Component.Label) 159 require.Nil(t, res[2].Component.PhoneNumber) 160 require.NotNil(t, res[2].Component.Email) 161 require.EqualValues(t, "joe@linux.org", *res[2].Component.Email) 162 require.True(t, res[2].Resolved) 163 require.Equal(t, mockJoe.UID, res[2].Uid) 164 } 165 166 func TestLookupContactsMultipleUsers(t *testing.T) { 167 tc := libkb.SetupTest(t, "TestLookupContacts", 1) 168 defer tc.Cleanup() 169 170 contactList := []keybase1.Contact{ 171 { 172 Name: "Alice", 173 Components: []keybase1.ContactComponent{ 174 MakePhoneComponent("Home", "+1111222"), 175 MakePhoneComponent("Work", "+199123"), 176 }, 177 }, 178 { 179 Name: "Bob", 180 Components: []keybase1.ContactComponent{ 181 MakePhoneComponent("Home", "+123456"), 182 }, 183 }, 184 { 185 Name: "Charlie", 186 Components: []keybase1.ContactComponent{ 187 MakeEmailComponent("E-mail", "charlie+test@keyba.se"), 188 }, 189 }, 190 } 191 192 provider := MakeMockProvider(t) 193 194 res, err := ResolveContacts(libkb.NewMetaContextForTest(tc), provider, contactList) 195 require.NoError(t, err) 196 expected := []string{ 197 "Alice +1111222 (Home)", 198 "Alice +199123 (Work)", 199 "Bob +123456 (Home)", 200 "Charlie charlie+test@keyba.se (E-mail)", 201 } 202 require.Equal(t, expected, stringifyResults(res)) 203 204 provider.PhoneNumbers["+123456"] = MakeMockLookupUser("bob", "") 205 provider.Emails["charlie+test@keyba.se"] = MakeMockLookupUser("charlie", "") 206 207 res, err = ResolveContacts(libkb.NewMetaContextForTest(tc), provider, contactList) 208 require.NoError(t, err) 209 expected = []string{ 210 "Alice +1111222 (Home)", 211 "Alice +199123 (Work)", 212 "keybase:bob", 213 "keybase:charlie", 214 } 215 require.Equal(t, expected, stringifyResults(res)) 216 expected = []string{ 217 `"Alice" "+1111222 (Home)"`, 218 `"Alice" "+199123 (Work)"`, 219 `"bob" "Bob"`, 220 `"charlie" "Charlie"`, 221 } 222 require.Equal(t, expected, displayResults(res)) 223 224 require.Equal(t, "1111222@phone", res[0].Assertion) 225 require.Equal(t, "199123@phone", res[1].Assertion) 226 require.Equal(t, "123456@phone", res[2].Assertion) 227 require.Equal(t, "[charlie+test@keyba.se]@email", res[3].Assertion) 228 } 229 230 func TestEmptyComponentLabels(t *testing.T) { 231 tc := libkb.SetupTest(t, "TestLookupContacts", 1) 232 defer tc.Cleanup() 233 234 contactList := []keybase1.Contact{ 235 { 236 Name: "Alice", 237 Components: []keybase1.ContactComponent{ 238 MakePhoneComponent("", "+1111222"), 239 MakeEmailComponent("", "alice+test@keyba.se"), 240 }, 241 }, 242 } 243 244 provider := MakeMockProvider(t) 245 246 res, err := ResolveContacts(libkb.NewMetaContextForTest(tc), provider, contactList) 247 require.NoError(t, err) 248 expected := []string{ 249 "Alice +1111222 ()", 250 "Alice alice+test@keyba.se ()", 251 } 252 require.Equal(t, expected, stringifyResults(res)) 253 expected = []string{ 254 `"Alice" "+1111222"`, 255 `"Alice" "alice+test@keyba.se"`, 256 } 257 require.Equal(t, expected, displayResults(res)) 258 259 provider.Emails["alice+test@keyba.se"] = MockLookupUser{UID: keybase1.UID("1111"), Username: "alice", Fullname: "A L I C E"} 260 res, err = ResolveContacts(libkb.NewMetaContextForTest(tc), provider, contactList) 261 require.NoError(t, err) 262 require.Len(t, res, 2) 263 // First component did not resolve 264 require.False(t, res[0].Resolved) 265 require.Equal(t, "1111222@phone", res[0].Assertion) 266 // Second component resolved 267 require.True(t, res[1].Resolved) 268 require.Equal(t, "alice", res[1].Username) 269 require.Equal(t, "A L I C E", res[1].FullName) 270 require.EqualValues(t, "1111", res[1].Uid) 271 require.False(t, res[1].Following) 272 require.Equal(t, "alice", res[1].DisplayName) 273 require.Equal(t, "Alice", res[1].DisplayLabel) // Because we are not following, contact name is used instead of full name. 274 require.Equal(t, "[alice+test@keyba.se]@email", res[1].Assertion) 275 } 276 277 func TestFollowing(t *testing.T) { 278 tc := libkb.SetupTest(t, "TestLookupContacts", 1) 279 defer tc.Cleanup() 280 281 contactList := []keybase1.Contact{ 282 { 283 Name: "Alice", 284 Components: []keybase1.ContactComponent{ 285 MakePhoneComponent("", "+1111222"), 286 }, 287 }, 288 { 289 Name: "Bob", 290 Components: []keybase1.ContactComponent{ 291 MakeEmailComponent("", "bob+test@keyba.se"), 292 }, 293 }, 294 { 295 Name: "Charlie", 296 Components: []keybase1.ContactComponent{ 297 MakeEmailComponent("", "charlie+test@keyba.se"), 298 }, 299 }, 300 { 301 Name: "", 302 Components: []keybase1.ContactComponent{ 303 MakeEmailComponent("", "doug+test@keyba.se"), 304 }, 305 }, 306 } 307 308 provider := MakeMockProvider(t) 309 310 provider.PhoneNumbers["+1111222"] = MockLookupUser{UID: keybase1.UID("1111"), Username: "alice", Fullname: "CryptoAlice"} 311 provider.Emails["bob+test@keyba.se"] = MockLookupUser{UID: keybase1.UID("2222"), Username: "bob", Fullname: ""} 312 provider.Emails["charlie+test@keyba.se"] = MockLookupUser{UID: keybase1.UID("3333"), Username: "charlie", Fullname: ""} 313 provider.Emails["doug+test@keyba.se"] = MockLookupUser{UID: keybase1.UID("4444"), Username: "doug", Fullname: ""} 314 315 provider.Following[keybase1.UID("1111")] = true 316 provider.Following[keybase1.UID("3333")] = true 317 318 res, err := ResolveContacts(libkb.NewMetaContextForTest(tc), provider, contactList) 319 require.NoError(t, err) 320 require.Len(t, res, 4) 321 expected := []string{ 322 `"alice" "CryptoAlice"`, // followed and have full name, take full name 323 `"bob" "Bob"`, // not followed, no full name, take contact name 324 `"charlie" "Charlie"`, // followed but no full name, take contact name 325 `"doug" "doug+test@keyba.se"`, // not followed, no full name, no contact name, take component 326 } 327 require.Equal(t, expected, displayResults(res)) 328 } 329 330 func TestErrorsInResolution(t *testing.T) { 331 tc := libkb.SetupTest(t, "TestLookupContacts", 1) 332 defer tc.Cleanup() 333 334 contactList := []keybase1.Contact{ 335 { 336 Name: "Alice", 337 Components: []keybase1.ContactComponent{ 338 MakePhoneComponent("home", "+1111222"), 339 MakePhoneComponent("work", "444"), 340 }, 341 }, 342 { 343 Name: "Bob", 344 Components: []keybase1.ContactComponent{ 345 MakeEmailComponent("", "bob+test@keyba.se"), 346 }, 347 }, 348 } 349 350 provider := MakeMockProvider(t) 351 provider.PhoneNumbers["+1111222"] = MakeMockLookupUser("alice", "CryptoAlice") 352 provider.PhoneNumberErrors["444"] = "Mock error for number 444" 353 354 res, err := ResolveContacts(libkb.NewMetaContextForTest(tc), provider, contactList) 355 require.NoError(t, err) 356 require.Len(t, res, 2) 357 require.True(t, res[0].Resolved) 358 require.False(t, res[1].Resolved) 359 expected := []string{ 360 `"alice" "Alice"`, 361 `"Bob" "bob+test@keyba.se"`, 362 } 363 require.Equal(t, expected, displayResults(res)) 364 365 delete(provider.PhoneNumbers, "+1111222") 366 367 res, err = ResolveContacts(libkb.NewMetaContextForTest(tc), provider, contactList) 368 require.NoError(t, err) 369 require.Len(t, res, 2) 370 require.False(t, res[0].Resolved) 371 require.False(t, res[1].Resolved) 372 expected = []string{ 373 `"Alice" "+1111222 (home)"`, 374 `"Bob" "bob+test@keyba.se"`, 375 } 376 require.Equal(t, expected, displayResults(res)) 377 } 378 379 func TestDuplicateEntries(t *testing.T) { 380 tc := libkb.SetupTest(t, "TestLookupContacts", 1) 381 defer tc.Cleanup() 382 383 contactList := []keybase1.Contact{ 384 // Contact with multiple components that will yield the same assertion. 385 { 386 Name: "Alice", 387 Components: []keybase1.ContactComponent{ 388 MakePhoneComponent("home", "+1111222"), 389 MakePhoneComponent("car", "+1111222"), 390 MakePhoneComponent("car", "+1111222"), 391 }, 392 }, 393 // "Duplicated" contacts with the same name and same component. 394 { 395 Name: "Alice", 396 Components: []keybase1.ContactComponent{ 397 MakePhoneComponent("home", "+1111222"), 398 }, 399 }, 400 // Two contacts with same component that's going to get resolved - 401 // resolution should appear only once in results. 402 { 403 Name: "Bob", 404 Components: []keybase1.ContactComponent{ 405 MakeEmailComponent("email", "bob+test@keyba.se"), 406 }, 407 }, 408 { 409 Name: "Robert B.", 410 Components: []keybase1.ContactComponent{ 411 MakeEmailComponent("E-Mail", "bob+test@keyba.se"), 412 }, 413 }, 414 } 415 416 provider := MakeMockProvider(t) 417 provider.Emails["bob+test@keyba.se"] = MakeMockLookupUser("bob", "Bobby") 418 419 // We expect one entry for Alice, +1111222 - because there is only one 420 // unique combination of "Alice" contact name ane component value 421 // "+1111222". Both "bob+test@keyba.se" values for Bob/Robert should have 422 // own, resolved, entry. 423 res, err := ResolveContacts(libkb.NewMetaContextForTest(tc), provider, contactList) 424 require.NoError(t, err) 425 require.Len(t, res, 3) 426 expected := []string{ 427 `"Alice" "+1111222 (home)"`, 428 `"bob" "Bob"`, 429 `"bob" "Robert B."`, 430 } 431 require.Equal(t, expected, displayResults(res)) 432 require.False(t, res[0].Resolved) 433 require.True(t, res[1].Resolved) 434 require.True(t, res[2].Resolved) 435 }