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  }