github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/uidmap/uidmap_test.go (about)

     1  package uidmap
     2  
     3  import (
     4  	"sync"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/keybase/client/go/libkb"
     9  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    10  	"github.com/keybase/clockwork"
    11  	"github.com/stretchr/testify/require"
    12  	"golang.org/x/net/context"
    13  )
    14  
    15  type testPair struct {
    16  	uid      string
    17  	username string
    18  }
    19  
    20  const mikem = keybase1.UID("95e88f2087e480cae28f08d81554bc00")
    21  const max = keybase1.UID("dbb165b7879fe7b1174df73bed0b9500")
    22  
    23  func TestLookupUsernameOnly(t *testing.T) {
    24  	tc := libkb.SetupTest(t, "TestLookup", 1)
    25  	defer tc.Cleanup()
    26  
    27  	var seed = []testPair{
    28  		{"afb5eda3154bc13c1df0189ce93ba119", "t_bob"},
    29  		{"00000000000000000000000000000119", ""},
    30  		{"295a7eea607af32040647123732bc819", "t_alice"},
    31  		{"00000000000000000000000000000219", ""},
    32  		{"9cbca30c38afba6ab02d76b206515919", "t_helen"},
    33  		{"00000000000000000000000000000319", ""},
    34  		{string(max), "max"},
    35  		{"00000000000000000000000000000419", ""},
    36  		{string(mikem), "mikem"},
    37  		{"00000000000000000000000000000519", ""},
    38  		{"9f9611a4b7920637b1c2a839b2a0e119", "t_george"},
    39  		{"00000000000000000000000000000619", ""},
    40  		{"359c7644857203be38bfd3bf79bf1819", "t_frank"},
    41  		{"00000000000000000000000000000719", ""},
    42  	}
    43  
    44  	var tests []testPair
    45  	batchSize = 7
    46  	for len(tests) < batchSize*10 {
    47  		tests = append(tests, seed...)
    48  	}
    49  
    50  	var uids []keybase1.UID
    51  	for _, test := range tests {
    52  		uid, err := keybase1.UIDFromString(test.uid)
    53  		require.NoError(t, err)
    54  		uids = append(uids, uid)
    55  	}
    56  
    57  	uidMap := NewUIDMap(10)
    58  
    59  	for i := 0; i < 4; i++ {
    60  		results, err := uidMap.MapUIDsToUsernamePackages(context.TODO(), tc.G, uids, 0, 0, false)
    61  		require.NoError(t, err)
    62  		for j, test := range tests {
    63  			require.True(t, results[j].NormalizedUsername.Eq(libkb.NewNormalizedUsername(test.username)))
    64  		}
    65  		if i == 2 {
    66  			uidMap.Clear()
    67  		}
    68  	}
    69  }
    70  
    71  func TestLookupUsernameConcurrent(t *testing.T) {
    72  	tc := libkb.SetupTest(t, "TestLookup", 1)
    73  	defer tc.Cleanup()
    74  
    75  	batchSize = 7
    76  
    77  	testStuff := func() {
    78  		var seed = []testPair{
    79  			{"afb5eda3154bc13c1df0189ce93ba119", "t_bob"},
    80  			{"00000000000000000000000000000119", ""},
    81  			{"295a7eea607af32040647123732bc819", "t_alice"},
    82  			{"00000000000000000000000000000219", ""},
    83  			{"9cbca30c38afba6ab02d76b206515919", "t_helen"},
    84  			{"00000000000000000000000000000319", ""},
    85  			{string(max), "max"},
    86  			{"00000000000000000000000000000419", ""},
    87  			{string(mikem), "mikem"},
    88  			{"00000000000000000000000000000519", ""},
    89  			{"9f9611a4b7920637b1c2a839b2a0e119", "t_george"},
    90  			{"00000000000000000000000000000619", ""},
    91  			{"359c7644857203be38bfd3bf79bf1819", "t_frank"},
    92  			{"00000000000000000000000000000719", ""},
    93  		}
    94  
    95  		var tests []testPair
    96  		for len(tests) < batchSize*10 {
    97  			tests = append(tests, seed...)
    98  		}
    99  
   100  		var uids []keybase1.UID
   101  		for _, test := range tests {
   102  			uid, err := keybase1.UIDFromString(test.uid)
   103  			require.NoError(t, err)
   104  			uids = append(uids, uid)
   105  		}
   106  
   107  		uidMap := NewUIDMap(10)
   108  
   109  		for i := 0; i < 4; i++ {
   110  			results, err := uidMap.MapUIDsToUsernamePackages(context.TODO(), tc.G, uids, 0, 0, false)
   111  			require.NoError(t, err)
   112  			for j, test := range tests {
   113  				require.True(t, results[j].NormalizedUsername.Eq(libkb.NewNormalizedUsername(test.username)))
   114  			}
   115  			if i == 2 {
   116  				uidMap.Clear()
   117  			}
   118  		}
   119  	}
   120  
   121  	var wg sync.WaitGroup
   122  	for i := 1; i < 10; i++ {
   123  		wg.Add(1)
   124  		go func() {
   125  			testStuff()
   126  			wg.Done()
   127  		}()
   128  	}
   129  
   130  	wg.Wait()
   131  }
   132  
   133  const tKB = keybase1.UID("7b7248a1c09d17451f9002d9edc8df19")
   134  
   135  func TestRanOutOfTime(t *testing.T) {
   136  	tc := libkb.SetupTest(t, "TestLookup", 1)
   137  	defer tc.Cleanup()
   138  
   139  	fakeClock := clockwork.NewFakeClockAt(time.Now())
   140  	tc.G.SetClock(fakeClock)
   141  
   142  	uidMap := NewUIDMap(10)
   143  	uids := []keybase1.UID{tKB}
   144  	errmsg := "ran out of time"
   145  
   146  	// This hook runs at the beginning of every iteration though the batch-fetch loop.
   147  	// It allows us to bump our fake clock forward.
   148  	hit := false
   149  	var cachedAt time.Time
   150  	setCachedAt := false
   151  	uidMap.testBatchIterHook = func() {
   152  		hit = true
   153  		fakeClock.Advance(time.Minute)
   154  		if setCachedAt {
   155  			cachedAt = fakeClock.Now()
   156  		}
   157  	}
   158  
   159  	// user t_kb has a fullname, but we're not giving ourselves enough time to grab it
   160  	results, err := uidMap.MapUIDsToUsernamePackages(context.TODO(), tc.G, uids, 0, time.Nanosecond, true)
   161  	require.Error(t, err)
   162  	require.True(t, hit)
   163  	require.Equal(t, err.Error(), errmsg)
   164  	require.True(t, results[0].NormalizedUsername.IsNil())
   165  	require.Nil(t, results[0].FullName)
   166  
   167  	// user mikem has a fullname, but we're again not giving ourselves enough time to grab it;
   168  	// however, he has a hard-coded UID mapping so we should be able to still grab his username
   169  	uids = []keybase1.UID{mikem}
   170  	hit = false
   171  	results, err = uidMap.MapUIDsToUsernamePackages(context.TODO(), tc.G, uids, 0, time.Nanosecond, true)
   172  	require.Error(t, err)
   173  	require.True(t, hit)
   174  	require.Equal(t, err.Error(), errmsg)
   175  	require.True(t, results[0].NormalizedUsername.Eq(libkb.NewNormalizedUsername("mikem")))
   176  	require.Nil(t, results[0].FullName)
   177  
   178  	// now success for user t_kb, who has a non-hardcoded username and a fullname on the
   179  	// server
   180  	t.Logf("tKB: %s", tKB)
   181  	uids = []keybase1.UID{tKB}
   182  	hit = false
   183  	results, err = uidMap.MapUIDsToUsernamePackages(context.TODO(), tc.G, uids, 0, 0, true)
   184  	require.NoError(t, err)
   185  	require.True(t, hit)
   186  	require.Equal(t, results[0].NormalizedUsername, libkb.NewNormalizedUsername("t_kb"))
   187  	require.Equal(t, results[0].FullName.FullName, keybase1.FullName("Joe Keybaser"))
   188  	require.Equal(t, results[0].FullName.EldestSeqno, keybase1.Seqno(1))
   189  	require.Equal(t, results[0].FullName.Status, keybase1.StatusCode_SCOk)
   190  	cachedAt = fakeClock.Now()
   191  
   192  	// Now we're going to simulate that the fullname resolution became expired, and there
   193  	// was an attempt to fetch it from the server, but that we ran out of network fetch time
   194  	// budget. So we should see the stale result and also the error.
   195  	fakeClock.Advance(time.Hour)
   196  	hit = false
   197  	results, err = uidMap.MapUIDsToUsernamePackages(context.TODO(), tc.G, uids, time.Second, time.Nanosecond, false)
   198  	require.Error(t, err)
   199  	require.Equal(t, err.Error(), errmsg)
   200  	require.True(t, hit)
   201  	require.Equal(t, results[0].NormalizedUsername, libkb.NewNormalizedUsername("t_kb"))
   202  	require.Equal(t, results[0].FullName.FullName, keybase1.FullName("Joe Keybaser"))
   203  	require.Equal(t, results[0].FullName.EldestSeqno, keybase1.Seqno(1))
   204  	require.Equal(t, results[0].FullName.CachedAt, keybase1.ToTime(cachedAt))
   205  	require.Equal(t, results[0].FullName.Status, keybase1.StatusCode_SCOk)
   206  
   207  	// Same as above, but give enough time to refresh the name from the server
   208  	hit = false
   209  	setCachedAt = true
   210  	results, err = uidMap.MapUIDsToUsernamePackages(context.TODO(), tc.G, uids, time.Second, 0, false)
   211  	require.NoError(t, err)
   212  	require.True(t, hit)
   213  	require.Equal(t, results[0].NormalizedUsername, libkb.NewNormalizedUsername("t_kb"))
   214  	require.Equal(t, results[0].FullName.FullName, keybase1.FullName("Joe Keybaser"))
   215  	require.Equal(t, results[0].FullName.EldestSeqno, keybase1.Seqno(1))
   216  	require.Equal(t, results[0].FullName.CachedAt, keybase1.ToTime(cachedAt))
   217  	require.Equal(t, results[0].FullName.Status, keybase1.StatusCode_SCOk)
   218  
   219  	// In this case, there's not enough time to make any fetches, but it doesn't matter, since our
   220  	// previous fetch is fresh enough. We should never even hit testBatchIterHook
   221  	fakeClock.Advance(time.Minute)
   222  	hit = false
   223  	setCachedAt = false
   224  	results, err = uidMap.MapUIDsToUsernamePackages(context.TODO(), tc.G, uids, time.Hour, time.Nanosecond, false)
   225  	require.NoError(t, err)
   226  	require.False(t, hit)
   227  	require.Equal(t, results[0].NormalizedUsername, libkb.NewNormalizedUsername("t_kb"))
   228  	require.Equal(t, results[0].FullName.FullName, keybase1.FullName("Joe Keybaser"))
   229  	require.Equal(t, results[0].FullName.EldestSeqno, keybase1.Seqno(1))
   230  	require.Equal(t, results[0].FullName.CachedAt, keybase1.ToTime(cachedAt))
   231  	require.Equal(t, results[0].FullName.Status, keybase1.StatusCode_SCOk)
   232  
   233  	// Do a happy path for several users:
   234  	uids = []keybase1.UID{mikem, tKB, max}
   235  	results, err = uidMap.MapUIDsToUsernamePackages(context.TODO(), tc.G, uids, 0, 0, false)
   236  	require.NoError(t, err)
   237  
   238  	// Everyone gets back a normalized username
   239  	require.Equal(t, results[0].NormalizedUsername, libkb.NewNormalizedUsername("mikem"))
   240  	require.Equal(t, results[1].NormalizedUsername, libkb.NewNormalizedUsername("t_kb"))
   241  	require.Equal(t, results[2].NormalizedUsername, libkb.NewNormalizedUsername("max"))
   242  
   243  	// But only t_kb has a fullname that's found
   244  	require.Nil(t, results[0].FullName)
   245  	require.Equal(t, results[1].FullName.FullName, keybase1.FullName("Joe Keybaser"))
   246  	require.Equal(t, results[1].FullName.CachedAt, keybase1.ToTime(cachedAt))
   247  	require.Equal(t, results[1].FullName.EldestSeqno, keybase1.Seqno(1))
   248  	require.Equal(t, results[1].FullName.Status, keybase1.StatusCode_SCOk)
   249  	require.Nil(t, results[2].FullName)
   250  
   251  	// We should get same results from offline call
   252  	uidMap.testBatchIterHook = func() {
   253  		require.Fail(t, "unexpected network activity during offline uidmap call")
   254  	}
   255  
   256  	resultsCached, err := uidMap.MapUIDsToUsernamePackagesOffline(context.TODO(), tc.G, uids, 0)
   257  	require.NoError(t, err)
   258  	require.Equal(t, results, resultsCached)
   259  }
   260  
   261  func TestOfflineUIDMapNoCache(t *testing.T) {
   262  	tc := libkb.SetupTest(t, "TestOfflineUIDMapNoCache", 1)
   263  	defer tc.Cleanup()
   264  
   265  	uidMap := NewUIDMap(10)
   266  	uids := []keybase1.UID{mikem, max, tKB}
   267  
   268  	uidMap.testBatchIterHook = func() {
   269  		require.Fail(t, "unexpected network activity during offline uidmap call")
   270  	}
   271  
   272  	resultsCached, err := uidMap.MapUIDsToUsernamePackagesOffline(context.TODO(), tc.G, uids, 0)
   273  	require.NoError(t, err)
   274  	require.Len(t, resultsCached, 3)
   275  	require.EqualValues(t, "mikem", resultsCached[0].NormalizedUsername)
   276  	require.EqualValues(t, "max", resultsCached[1].NormalizedUsername)
   277  	require.True(t, resultsCached[2].NormalizedUsername.IsNil())
   278  	for _, v := range resultsCached {
   279  		require.Nil(t, v.FullName)
   280  	}
   281  }
   282  
   283  func TestDuplicateUids(t *testing.T) {
   284  	tc := libkb.SetupTest(t, "TestLookup", 1)
   285  	defer tc.Cleanup()
   286  
   287  	uidMap := NewUIDMap(10)
   288  	uids := []keybase1.UID{tAlice, tTracy, tAlice}
   289  	results, err := uidMap.MapUIDsToUsernamePackages(context.TODO(), tc.G, uids,
   290  		24*time.Hour, 10*time.Second, true)
   291  	require.NoError(t, err)
   292  
   293  	require.EqualValues(t, results[0].NormalizedUsername, "t_alice")
   294  	require.EqualValues(t, results[1].NormalizedUsername, "t_tracy")
   295  	require.Equal(t, results[0], results[2])
   296  }