github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/systests/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 systests 5 6 import ( 7 "testing" 8 9 "github.com/davecgh/go-spew/spew" 10 11 "github.com/keybase/client/go/contacts" 12 "github.com/keybase/client/go/kbtest" 13 "github.com/keybase/client/go/libkb" 14 "github.com/keybase/client/go/protocol/keybase1" 15 16 "github.com/stretchr/testify/require" 17 context "golang.org/x/net/context" 18 ) 19 20 func TestLookupContactList(t *testing.T) { 21 tt := newTeamTester(t) 22 defer tt.cleanup() 23 24 ann := tt.addUser("ann") 25 26 phone := keybase1.PhoneNumber("+" + kbtest.GenerateTestPhoneNumber()) 27 28 phoneCli := keybase1.PhoneNumbersClient{Cli: ann.teamsClient.Cli} 29 err := phoneCli.AddPhoneNumber(context.Background(), keybase1.AddPhoneNumberArg{ 30 PhoneNumber: phone, 31 Visibility: keybase1.IdentityVisibility_PUBLIC, 32 }) 33 require.NoError(t, err) 34 35 code, err := kbtest.GetPhoneVerificationCode(ann.MetaContext(), phone) 36 require.NoError(t, err) 37 38 err = phoneCli.VerifyPhoneNumber(context.Background(), keybase1.VerifyPhoneNumberArg{ 39 PhoneNumber: phone, 40 Code: code, 41 }) 42 require.NoError(t, err) 43 44 contactsCli := keybase1.ContactsClient{Cli: ann.teamsClient.Cli} 45 rawPhone := keybase1.RawPhoneNumber(phone) 46 res, err := contactsCli.LookupContactList(context.Background(), keybase1.LookupContactListArg{ 47 Contacts: []keybase1.Contact{ 48 {Name: "It's me", 49 Components: []keybase1.ContactComponent{ 50 { 51 PhoneNumber: &rawPhone, 52 }, 53 }, 54 }, 55 }, 56 }) 57 require.NoError(t, err) 58 require.Len(t, res, 1) 59 contactRes := res[0] 60 require.True(t, contactRes.Resolved) 61 require.Equal(t, ann.uid, contactRes.Uid) 62 require.NotNil(t, contactRes.Component.PhoneNumber) 63 require.Equal(t, rawPhone, *contactRes.Component.PhoneNumber) 64 65 mctx := libkb.NewMetaContextForTest(*ann.tc) 66 emailAddr := keybase1.EmailAddress(ann.userInfo.email) 67 err = kbtest.VerifyEmailAuto(mctx, emailAddr) 68 require.NoError(t, err) 69 70 emailCli := keybase1.EmailsClient{Cli: ann.teamsClient.Cli} 71 err = emailCli.SetVisibilityEmail(context.Background(), keybase1.SetVisibilityEmailArg{ 72 Email: emailAddr, 73 Visibility: keybase1.IdentityVisibility_PUBLIC, 74 }) 75 require.NoError(t, err) 76 77 res, err = contactsCli.LookupContactList(context.Background(), keybase1.LookupContactListArg{ 78 Contacts: []keybase1.Contact{ 79 {Name: "It's me", 80 Components: []keybase1.ContactComponent{ 81 { 82 Email: &emailAddr, 83 }, 84 }, 85 }, 86 }, 87 }) 88 require.NoError(t, err) 89 require.Len(t, res, 1) 90 contactRes = res[0] 91 require.True(t, contactRes.Resolved) 92 require.Equal(t, ann.uid, contactRes.Uid) 93 require.NotNil(t, contactRes.Component.Email) 94 require.Equal(t, emailAddr, *contactRes.Component.Email) 95 } 96 97 func TestBulkLookupContacts(t *testing.T) { 98 tt := newTeamTester(t) 99 defer tt.cleanup() 100 101 // User looking up other contacts 102 actor := tt.addUser("blac") 103 actorCtx := libkb.NewMetaContextForTest(*actor.tc) 104 105 // Some helper functions because we're preparing a few accounts at once 106 addEmailAddress := func(user *userPlusDevice, vis keybase1.IdentityVisibility, verify bool) keybase1.EmailAddress { 107 emailAddr := keybase1.EmailAddress(user.userInfo.email) 108 109 mctx := libkb.NewMetaContextForTest(*user.tc) 110 if verify { 111 err := kbtest.VerifyEmailAuto(mctx, emailAddr) 112 require.NoError(t, err) 113 } 114 115 if vis == keybase1.IdentityVisibility_PUBLIC { 116 emailCli := keybase1.EmailsClient{Cli: user.teamsClient.Cli} 117 err := emailCli.SetVisibilityEmail(context.Background(), keybase1.SetVisibilityEmailArg{ 118 Email: emailAddr, 119 Visibility: keybase1.IdentityVisibility_PUBLIC, 120 }) 121 require.NoError(t, err) 122 } 123 124 return emailAddr 125 } 126 addPhoneNumber := func(user *userPlusDevice, vis keybase1.IdentityVisibility, verify bool) keybase1.PhoneNumber { 127 phone := keybase1.PhoneNumber("+" + kbtest.GenerateTestPhoneNumber()) 128 phoneCli := keybase1.PhoneNumbersClient{Cli: user.teamsClient.Cli} 129 err := phoneCli.AddPhoneNumber(context.Background(), keybase1.AddPhoneNumberArg{ 130 PhoneNumber: phone, 131 Visibility: vis, 132 }) 133 require.NoError(t, err) 134 135 if verify { 136 code, err := kbtest.GetPhoneVerificationCode(user.MetaContext(), phone) 137 require.NoError(t, err) 138 139 err = phoneCli.VerifyPhoneNumber(context.Background(), keybase1.VerifyPhoneNumberArg{ 140 PhoneNumber: phone, 141 Code: code, 142 }) 143 require.NoError(t, err) 144 } 145 146 return phone 147 } 148 149 // Someone with a verified visible email #1 150 evv1 := tt.addUser("blevv") 151 evv1Email := addEmailAddress(evv1, keybase1.IdentityVisibility_PUBLIC, true) 152 // Someone with a verified visible email #2 153 evv2 := tt.addUser("blevv") 154 evv2Email := addEmailAddress(evv2, keybase1.IdentityVisibility_PUBLIC, true) 155 // Someone with a verified private email 156 evp := tt.addUser("blevp") 157 evpEmail := addEmailAddress(evp, keybase1.IdentityVisibility_PRIVATE, true) 158 // Someone with an unverified visible email 159 epv := tt.addUser("blepv") 160 epvEmail := addEmailAddress(epv, keybase1.IdentityVisibility_PUBLIC, false) 161 162 // Someone with a verified visible phone number #1 163 pvv1 := tt.addUser("blpvv") 164 pvv1Number := addPhoneNumber(pvv1, keybase1.IdentityVisibility_PUBLIC, true) 165 pvv1NumberRaw := keybase1.RawPhoneNumber(pvv1Number) 166 // Someone with a verified private phone number 167 pvp := tt.addUser("blpvp") 168 pvpNumber := addPhoneNumber(pvp, keybase1.IdentityVisibility_PRIVATE, false) 169 pvpNumberRaw := keybase1.RawPhoneNumber(pvpNumber) 170 // Someone with an unverified visible phone number 171 ppv := tt.addUser("blppv") 172 ppvNumber := addPhoneNumber(ppv, keybase1.IdentityVisibility_PUBLIC, false) 173 ppvNumberRaw := keybase1.RawPhoneNumber(ppvNumber) 174 175 // A few unused numbers 176 const randomEmail = "doesnotexist@example.org" 177 const randomNumber = "+12025550116" 178 179 // Run the lookup 180 res, err := contacts.BulkLookupContacts( 181 actorCtx, 182 []keybase1.EmailAddress{ 183 evv1Email, 184 evv2Email, 185 evpEmail, 186 epvEmail, 187 randomEmail, 188 }, 189 []keybase1.RawPhoneNumber{ 190 pvv1NumberRaw, 191 pvpNumberRaw, 192 ppvNumberRaw, 193 randomNumber, 194 }, 195 contacts.NoneToken, 196 ) 197 require.NoError(t, err) 198 199 tableLoop: 200 for _, x := range []struct { 201 LookupKey contacts.ContactLookupKey 202 Match bool 203 Coerced bool 204 }{ 205 {contacts.MakeEmailLookupKey(evv1Email), true, false}, 206 {contacts.MakeEmailLookupKey(evv2Email), true, false}, 207 {contacts.MakeEmailLookupKey(evpEmail), false, false}, 208 {contacts.MakeEmailLookupKey(epvEmail), false, false}, 209 {contacts.MakeEmailLookupKey(randomEmail), false, false}, 210 {contacts.MakePhoneLookupKey(pvv1NumberRaw), true, false}, 211 {contacts.MakePhoneLookupKey(pvpNumberRaw), false, false}, 212 {contacts.MakePhoneLookupKey(ppvNumberRaw), false, false}, 213 {contacts.MakeEmailLookupKey(randomNumber), false, false}, 214 } { 215 for k, v := range res.Results { 216 if k != x.LookupKey { 217 continue 218 } 219 220 // We found one! 221 if !x.Match { 222 require.Fail(t, "found %v in the result", x.LookupKey) 223 continue tableLoop 224 } 225 226 // Evaluate coerced 227 require.True( 228 t, 229 (x.Coerced && v.Coerced != "") || 230 (!x.Coerced && v.Coerced == ""), 231 "%v coerced value was expected to be %v, got %v", 232 x.LookupKey, x.Coerced, v.Coerced != "", 233 ) 234 continue tableLoop 235 } 236 237 // We didn't find anything 238 if x.Match { 239 require.Fail(t, "did not find %v in the result", x.LookupKey) 240 } 241 } 242 } 243 244 func TestLookupSelfAfterRemove(t *testing.T) { 245 tt := newTeamTester(t) 246 defer tt.cleanup() 247 248 ann := tt.addUser("ann") 249 250 contactsCli := keybase1.ContactsClient{Cli: ann.teamsClient.Cli} 251 phoneCli := keybase1.PhoneNumbersClient{Cli: ann.teamsClient.Cli} 252 emailCli := keybase1.EmailsClient{Cli: ann.teamsClient.Cli} 253 254 phoneNum := keybase1.PhoneNumber("+" + kbtest.GenerateTestPhoneNumber()) 255 { 256 // Add and verify a phone number 257 err := phoneCli.AddPhoneNumber(context.Background(), keybase1.AddPhoneNumberArg{ 258 PhoneNumber: phoneNum, 259 Visibility: keybase1.IdentityVisibility_PUBLIC, 260 }) 261 require.NoError(t, err) 262 263 code, err := kbtest.GetPhoneVerificationCode(ann.MetaContext(), phoneNum) 264 require.NoError(t, err) 265 266 err = phoneCli.VerifyPhoneNumber(context.Background(), keybase1.VerifyPhoneNumberArg{ 267 PhoneNumber: phoneNum, 268 Code: code, 269 }) 270 require.NoError(t, err) 271 } 272 273 emailAddr := keybase1.EmailAddress(randomUser("newemail").email) 274 { 275 // Add and verify an email address 276 err := emailCli.AddEmail(context.Background(), keybase1.AddEmailArg{ 277 Email: emailAddr, 278 Visibility: keybase1.IdentityVisibility_PUBLIC, 279 }) 280 require.NoError(t, err) 281 282 err = kbtest.VerifyEmailAuto(ann.MetaContext(), emailAddr) 283 require.NoError(t, err) 284 } 285 286 rawPhone := keybase1.RawPhoneNumber(phoneNum) 287 miscEmailAddr := keybase1.EmailAddress(randomUser("newemail").email) 288 miscPhoneNum := keybase1.RawPhoneNumber("+" + kbtest.GenerateTestPhoneNumber()) 289 290 rawContacts := []keybase1.Contact{ 291 { 292 Name: "Ann Test", 293 Components: []keybase1.ContactComponent{ 294 {Label: "phone", PhoneNumber: &rawPhone}, 295 {Label: "email", Email: &emailAddr}, 296 }, 297 }, 298 { 299 Name: "Ann Test 2", 300 Components: []keybase1.ContactComponent{ 301 {Email: &emailAddr}, 302 }, 303 }, 304 { 305 Name: "Test Ann", 306 Components: []keybase1.ContactComponent{ 307 {PhoneNumber: &rawPhone}, 308 }, 309 }, 310 { 311 Name: "Someone else", 312 Components: []keybase1.ContactComponent{ 313 {PhoneNumber: &miscPhoneNum}, 314 }, 315 }, 316 { 317 Name: "Someone else", 318 Components: []keybase1.ContactComponent{ 319 {Email: &miscEmailAddr}, 320 }, 321 }, 322 } 323 324 { 325 _, err := contactsCli.SaveContactList(context.Background(), keybase1.SaveContactListArg{ 326 Contacts: rawContacts, 327 }) 328 require.NoError(t, err) 329 330 list, err := contactsCli.LookupSavedContactsList(context.Background(), 0) 331 require.NoError(t, err) 332 333 var foundMiscEmail, foundMiscPhone, foundOurEmail, foundOurPhone int 334 for _, v := range list { 335 if v.Component.Email != nil { 336 switch { 337 case *v.Component.Email == miscEmailAddr: 338 foundMiscEmail++ 339 require.False(t, v.Resolved) 340 case *v.Component.Email == emailAddr: 341 foundOurEmail++ 342 require.True(t, v.Resolved) 343 require.Equal(t, ann.username, v.Username) 344 require.Equal(t, ann.uid, v.Uid) 345 default: 346 require.Fail(t, "Found unexpected email in contacts: %s", *v.Component.Email) 347 } 348 } else if v.Component.PhoneNumber != nil { 349 switch { 350 case *v.Component.PhoneNumber == miscPhoneNum: 351 foundMiscPhone++ 352 require.False(t, v.Resolved) 353 case *v.Component.PhoneNumber == rawPhone: 354 foundOurPhone++ 355 require.True(t, v.Resolved) 356 require.Equal(t, ann.username, v.Username) 357 require.Equal(t, ann.uid, v.Uid) 358 default: 359 require.Fail(t, "Found unexpected email in contacts: %s", *v.Component.Email) 360 } 361 } 362 } 363 364 require.Equal(t, 1, foundMiscEmail) 365 require.Equal(t, 1, foundMiscPhone) 366 require.Equal(t, 2, foundOurPhone) 367 require.Equal(t, 2, foundOurEmail) 368 } 369 370 { 371 // Delete both phone and email 372 err := emailCli.DeleteEmail(context.Background(), keybase1.DeleteEmailArg{ 373 Email: emailAddr, 374 }) 375 require.NoError(t, err) 376 err = phoneCli.DeletePhoneNumber(context.Background(), keybase1.DeletePhoneNumberArg{ 377 PhoneNumber: phoneNum, 378 }) 379 require.NoError(t, err) 380 } 381 382 { 383 // Fetch our contacts again, deleting should automatically affect our 384 // synced contacts. 385 386 for i := 0; i < 2; i++ { 387 // 1. Inspect saved contacts list, 388 // 2. sync contacts again, 389 // 3. inspect again to see if stale cache did not overwrite our 390 // unresolved entries back to resolved. 391 list, err := contactsCli.LookupSavedContactsList(context.Background(), 0) 392 require.NoError(t, err) 393 394 var foundMiscEmail, foundMiscPhone, foundOurEmail, foundOurPhone int 395 for _, v := range list { 396 t.Logf("Checking component %s, should be UNRESOLVED", spew.Sdump(v)) 397 if v.Component.Email != nil { 398 switch { 399 case *v.Component.Email == miscEmailAddr: 400 foundMiscEmail++ 401 case *v.Component.Email == emailAddr: 402 foundOurEmail++ 403 default: 404 require.Fail(t, "Found unexpected email in contacts: %s", *v.Component.Email) 405 } 406 } else if v.Component.PhoneNumber != nil { 407 switch { 408 case *v.Component.PhoneNumber == miscPhoneNum: 409 foundMiscPhone++ 410 case *v.Component.PhoneNumber == rawPhone: 411 foundOurPhone++ 412 default: 413 require.Fail(t, "Found unexpected email in contacts: %s", *v.Component.Email) 414 } 415 } 416 require.False(t, v.Resolved) 417 require.True(t, v.Uid.IsNil()) 418 require.Empty(t, v.Username) 419 require.Empty(t, v.FullName) 420 } 421 422 require.Equal(t, 1, foundMiscEmail) 423 require.Equal(t, 1, foundMiscPhone) 424 require.Equal(t, 2, foundOurPhone) 425 require.Equal(t, 2, foundOurEmail) 426 427 if i == 0 { 428 _, err := contactsCli.SaveContactList(context.Background(), keybase1.SaveContactListArg{ 429 Contacts: rawContacts, 430 }) 431 require.NoError(t, err) 432 } 433 } 434 } 435 }