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

     1  // Copyright 2015 Keybase, Inc. All rights reserved. Use of
     2  // this source code is governed by the included BSD license.
     3  
     4  // There are two test files by this name. One in libkb, one in engine.
     5  
     6  package engine
     7  
     8  import (
     9  	"sync"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/keybase/client/go/libkb"
    14  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    15  	"github.com/keybase/clockwork"
    16  	"github.com/stretchr/testify/require"
    17  	"golang.org/x/net/context"
    18  )
    19  
    20  func TestLoadDeviceKeyNew(t *testing.T) {
    21  	tc := SetupEngineTest(t, "clu")
    22  	defer tc.Cleanup()
    23  
    24  	t.Logf("create new user")
    25  	fu := NewFakeUserOrBust(t, "paper")
    26  	arg := MakeTestSignupEngineRunArg(fu)
    27  	arg.SkipPaper = false
    28  	loginUI := &paperLoginUI{Username: fu.Username}
    29  	uis := libkb.UIs{
    30  		LogUI:    tc.G.UI.GetLogUI(),
    31  		GPGUI:    &gpgtestui{},
    32  		SecretUI: fu.NewSecretUI(),
    33  		LoginUI:  loginUI,
    34  	}
    35  	s := NewSignupEngine(tc.G, &arg)
    36  	err := RunEngine2(NewMetaContextForTest(tc).WithUIs(uis), s)
    37  	if err != nil {
    38  		tc.T.Fatal(err)
    39  	}
    40  	t.Logf("using username:%+v", fu.Username)
    41  	loadArg := libkb.NewLoadUserByNameArg(tc.G, fu.Username).WithPublicKeyOptional()
    42  	user, err := libkb.LoadUser(loadArg)
    43  	if err != nil {
    44  		tc.T.Fatal(err)
    45  	}
    46  	t.Logf("using username:%+v uid: %+v", user.GetNormalizedName(), user.GetUID())
    47  
    48  	assertNumDevicesAndKeys(tc, fu, 2, 4)
    49  
    50  	devices, _ := getActiveDevicesAndKeys(tc, fu)
    51  	var device1 *libkb.Device
    52  	for _, device := range devices {
    53  		if device.Type != keybase1.DeviceTypeV2_PAPER {
    54  			device1 = device.Device
    55  		}
    56  	}
    57  	require.NotNil(t, device1, "device1 should be non-nil")
    58  	t.Logf("using device1:%+v", device1.ID)
    59  
    60  	t.Logf("load existing device key")
    61  	upk, deviceKey, revoked, err := tc.G.GetUPAKLoader().LoadDeviceKey(context.TODO(), user.GetUID(), device1.ID)
    62  	require.NoError(t, err)
    63  	require.Equal(t, user.GetNormalizedName().String(), upk.Base.Username, "usernames must match")
    64  	require.Equal(t, device1.ID, deviceKey.DeviceID, "deviceID must match")
    65  	require.Equal(t, *device1.Description, deviceKey.DeviceDescription, "device name must match")
    66  	require.Nil(t, revoked, "device not revoked")
    67  	err = tc.G.GetFullSelfer().WithSelf(context.TODO(), func(u *libkb.User) error {
    68  		dev, err := u.GetDevice(device1.ID)
    69  		require.NoError(t, err)
    70  		require.NotNil(t, dev)
    71  		return nil
    72  	})
    73  	require.NoError(t, err)
    74  
    75  	Logout(tc)
    76  
    77  	if len(loginUI.PaperPhrase) == 0 {
    78  		t.Fatal("login ui has no paper key phrase")
    79  	}
    80  
    81  	t.Logf("create new device")
    82  	// redo SetupEngineTest to get a new home directory...should look like a new device.
    83  	tc2 := SetupEngineTest(t, "login")
    84  	defer tc2.Cleanup()
    85  
    86  	secUI := fu.NewSecretUI()
    87  	provUI := newTestProvisionUIPaper()
    88  	provLoginUI := &libkb.TestLoginUI{Username: fu.Username}
    89  	uis = libkb.UIs{
    90  		ProvisionUI: provUI,
    91  		LogUI:       tc2.G.UI.GetLogUI(),
    92  		SecretUI:    secUI,
    93  		LoginUI:     provLoginUI,
    94  		GPGUI:       &gpgtestui{},
    95  	}
    96  
    97  	eng := NewPaperProvisionEngine(tc2.G, fu.Username, "fakedevice", loginUI.PaperPhrase)
    98  	m := NewMetaContextForTest(tc2).WithUIs(uis)
    99  	if err := RunEngine2(m, eng); err != nil {
   100  		t.Fatal(err)
   101  	}
   102  	t.Logf("d2 provisioned (1)")
   103  
   104  	testUserHasDeviceKey(tc2)
   105  	require.NoError(t, AssertProvisioned(tc2))
   106  	t.Logf("d2 provisioned (2)")
   107  
   108  	devices, _ = getActiveDevicesAndKeys(tc, fu)
   109  	var device2 *libkb.Device
   110  	for _, device := range devices {
   111  		if device.Type != keybase1.DeviceTypeV2_PAPER && device.ID != device1.ID {
   112  			device2 = device.Device
   113  		}
   114  	}
   115  	require.NotNil(t, device2, "device2 should be non-nil")
   116  	t.Logf("using device2:%+v", device2.ID)
   117  
   118  	t.Logf("load brand new device (while old is cached)")
   119  	upk, deviceKey, revoked, err = tc.G.GetUPAKLoader().LoadDeviceKey(context.TODO(), user.GetUID(), device2.ID)
   120  	require.NoError(t, err)
   121  	require.Equal(t, user.GetNormalizedName().String(), upk.Base.Username, "usernames must match")
   122  	require.Equal(t, device2.ID, deviceKey.DeviceID, "deviceID must match")
   123  	require.Equal(t, *device2.Description, deviceKey.DeviceDescription, "device name must match")
   124  	require.Nil(t, revoked, "device not revoked")
   125  	err = tc.G.GetFullSelfer().WithSelf(context.TODO(), func(u *libkb.User) error {
   126  		dev, err := u.GetDevice(deviceKey.DeviceID)
   127  		require.NoError(t, err)
   128  		require.NotNil(t, dev)
   129  		return nil
   130  	})
   131  	require.NoError(t, err)
   132  }
   133  
   134  func TestLoadDeviceKeyRevoked(t *testing.T) {
   135  	tc := SetupEngineTest(t, "clu")
   136  	defer tc.Cleanup()
   137  
   138  	fu := CreateAndSignupFakeUserPaper(tc, "rev")
   139  	t.Logf("using username:%+v", fu.Username)
   140  	loadArg := libkb.NewLoadUserByNameArg(tc.G, fu.Username).WithPublicKeyOptional()
   141  	user, err := libkb.LoadUser(loadArg)
   142  	if err != nil {
   143  		tc.T.Fatal(err)
   144  	}
   145  	t.Logf("using username:%+v uid: %+v", user.GetNormalizedName(), user.GetUID())
   146  
   147  	assertNumDevicesAndKeys(tc, fu, 2, 4)
   148  
   149  	devices, _ := getActiveDevicesAndKeys(tc, fu)
   150  	var thisDevice *libkb.Device
   151  	for _, device := range devices {
   152  		if device.Type != keybase1.DeviceTypeV2_PAPER {
   153  			thisDevice = device.Device
   154  		}
   155  	}
   156  
   157  	// Revoke the current device with --force
   158  	err = doRevokeDevice(tc, fu, thisDevice.ID, true, false)
   159  	if err != nil {
   160  		tc.T.Fatal(err)
   161  	}
   162  
   163  	assertNumDevicesAndKeys(tc, fu, 1, 2)
   164  
   165  	t.Logf("load revoked device")
   166  	upk, deviceKey, revoked, err := tc.G.GetUPAKLoader().LoadDeviceKey(context.TODO(), user.GetUID(), thisDevice.ID)
   167  	require.NoError(t, err)
   168  	require.Equal(t, user.GetNormalizedName().String(), upk.Base.Username, "usernames must match")
   169  	require.Equal(t, thisDevice.ID, deviceKey.DeviceID, "deviceID must match")
   170  	require.Equal(t, *thisDevice.Description, deviceKey.DeviceDescription, "device name must match")
   171  	require.NotNil(t, revoked, "device should be revoked")
   172  	err = tc.G.GetFullSelfer().WithSelf(context.TODO(), func(u *libkb.User) error {
   173  		dev, err := u.GetDevice(deviceKey.DeviceID)
   174  		require.NoError(t, err)
   175  		require.NotNil(t, dev)
   176  		require.False(t, dev.IsActive())
   177  		dev, err = u.GetDevice(thisDevice.ID)
   178  		require.NoError(t, err)
   179  		require.NotNil(t, dev)
   180  		return nil
   181  	})
   182  	require.NoError(t, err)
   183  }
   184  
   185  func TestFullSelfCacherFlushSingleMachine(t *testing.T) {
   186  	tc := SetupEngineTest(t, "fsc")
   187  	defer tc.Cleanup()
   188  	sigVersion := libkb.GetDefaultSigVersion(tc.G)
   189  
   190  	fu := CreateAndSignupFakeUser(tc, "fsc")
   191  
   192  	var scv keybase1.Seqno
   193  	err := tc.G.GetFullSelfer().WithSelf(context.TODO(), func(u *libkb.User) error {
   194  		require.NotNil(t, u)
   195  		scv = u.GetSigChainLastKnownSeqno()
   196  		return nil
   197  	})
   198  	require.NoError(t, err)
   199  	trackAlice(tc, fu, sigVersion)
   200  	defer untrackAlice(tc, fu, sigVersion)
   201  	err = tc.G.GetFullSelfer().WithSelf(context.TODO(), func(u *libkb.User) error {
   202  		require.NotNil(t, u)
   203  		require.True(t, u.GetSigChainLastKnownSeqno() > scv)
   204  		return nil
   205  	})
   206  	require.NoError(t, err)
   207  }
   208  
   209  func TestFullSelfCacherFlushTwoMachines(t *testing.T) {
   210  	tc := SetupEngineTest(t, "fsc")
   211  	defer tc.Cleanup()
   212  	fakeClock := clockwork.NewFakeClockAt(time.Now())
   213  	tc.G.SetClock(fakeClock)
   214  
   215  	t.Logf("create new user")
   216  	fu := NewFakeUserOrBust(t, "paper")
   217  	arg := MakeTestSignupEngineRunArg(fu)
   218  	arg.SkipPaper = false
   219  	loginUI := &paperLoginUI{Username: fu.Username}
   220  	uis := libkb.UIs{
   221  		LogUI:    tc.G.UI.GetLogUI(),
   222  		GPGUI:    &gpgtestui{},
   223  		SecretUI: fu.NewSecretUI(),
   224  		LoginUI:  loginUI,
   225  	}
   226  	s := NewSignupEngine(tc.G, &arg)
   227  	err := RunEngine2(NewMetaContextForTest(tc).WithUIs(uis), s)
   228  	if err != nil {
   229  		tc.T.Fatal(err)
   230  	}
   231  	t.Logf("using username:%+v", fu.Username)
   232  
   233  	var scv keybase1.Seqno
   234  	err = tc.G.GetFullSelfer().WithSelf(context.TODO(), func(u *libkb.User) error {
   235  		require.NotNil(t, u)
   236  		scv = u.GetSigChainLastKnownSeqno()
   237  		return nil
   238  	})
   239  	require.NoError(t, err)
   240  
   241  	if len(loginUI.PaperPhrase) == 0 {
   242  		t.Fatal("login ui has no paper key phrase")
   243  	}
   244  
   245  	t.Logf("create new device")
   246  	// redo SetupEngineTest to get a new home directory...should look like a new device.
   247  	tc2 := SetupEngineTest(t, "login")
   248  	defer tc2.Cleanup()
   249  
   250  	secUI := fu.NewSecretUI()
   251  	provUI := newTestProvisionUIPaper()
   252  	provLoginUI := &libkb.TestLoginUI{Username: fu.Username}
   253  	uis = libkb.UIs{
   254  		ProvisionUI: provUI,
   255  		LogUI:       tc2.G.UI.GetLogUI(),
   256  		SecretUI:    secUI,
   257  		LoginUI:     provLoginUI,
   258  		GPGUI:       &gpgtestui{},
   259  	}
   260  
   261  	eng := NewPaperProvisionEngine(tc2.G, fu.Username, "fakedevice", loginUI.PaperPhrase)
   262  	m := NewMetaContextForTest(tc2).WithUIs(uis)
   263  	if err := RunEngine2(m, eng); err != nil {
   264  		t.Fatal(err)
   265  	}
   266  	t.Logf("d2 provisioned (1)")
   267  
   268  	// Without pubsub (not available on engine tests), we don't get any
   269  	// invalidation of the user on the first machine (tc). So this
   270  	// user's sigchain should stay the same.
   271  	err = tc.G.GetFullSelfer().WithSelf(context.TODO(), func(u *libkb.User) error {
   272  		require.NotNil(t, u)
   273  		require.True(t, u.GetSigChainLastKnownSeqno() == scv)
   274  		return nil
   275  	})
   276  	require.NoError(t, err)
   277  
   278  	// After the CachedUserTimeout, the FullSelfer ought to repoll.
   279  	// Check that the sigchain is updated after the repoll, which reflects
   280  	// the new device having been added.
   281  	fakeClock.Advance(libkb.CachedUserTimeout + time.Second)
   282  	err = tc.G.GetFullSelfer().WithSelf(context.TODO(), func(u *libkb.User) error {
   283  		require.NotNil(t, u)
   284  		require.True(t, u.GetSigChainLastKnownSeqno() > scv)
   285  		return nil
   286  	})
   287  	require.NoError(t, err)
   288  }
   289  
   290  func TestUPAKDeadlock(t *testing.T) {
   291  	tc := SetupEngineTest(t, "upak")
   292  	defer tc.Cleanup()
   293  	fu := CreateAndSignupFakeUserPaper(tc, "upak")
   294  
   295  	// First clear the cache
   296  	tc.G.KeyfamilyChanged(context.TODO(), fu.UID())
   297  
   298  	var wg sync.WaitGroup
   299  
   300  	ch := make(chan struct{})
   301  
   302  	tc.G.GetFullSelfer().(*libkb.CachedFullSelf).TestDeadlocker = func() {
   303  		<-ch
   304  	}
   305  
   306  	tc.G.GetUPAKLoader().(*libkb.CachedUPAKLoader).TestDeadlocker = func() {
   307  		ch <- struct{}{}
   308  	}
   309  
   310  	wg.Add(1)
   311  	go func() {
   312  		_ = tc.G.GetFullSelfer().WithSelf(context.TODO(), func(u *libkb.User) error {
   313  			require.Equal(t, u.GetUID(), fu.UID(), "right UID")
   314  			return nil
   315  		})
   316  		wg.Done()
   317  	}()
   318  
   319  	wg.Add(1)
   320  	go func() {
   321  		un, err := tc.G.GetUPAKLoader().LookupUsername(context.TODO(), fu.UID())
   322  		require.NoError(t, err)
   323  		if un.String() != fu.Username {
   324  			t.Errorf("username mismatch: %s != %s", un, fu.Username)
   325  		}
   326  		wg.Done()
   327  	}()
   328  
   329  	doneCh := make(chan struct{})
   330  	go func() {
   331  		wg.Wait()
   332  		doneCh <- struct{}{}
   333  	}()
   334  
   335  	select {
   336  	case <-doneCh:
   337  		break
   338  	case <-time.After(20 * time.Second):
   339  		t.Fatal("deadlocked!")
   340  	}
   341  }
   342  
   343  func TestLoadAfterAcctReset1(t *testing.T) {
   344  	// One context for user that will be doing LoadUser, and another
   345  	// for user that will sign up and reset itself.
   346  	tc := SetupEngineTest(t, "clu")
   347  	defer tc.Cleanup()
   348  
   349  	resetUserTC := SetupEngineTest(t, "clu2")
   350  	defer resetUserTC.Cleanup()
   351  
   352  	t.Logf("create new user")
   353  	fu := CreateAndSignupFakeUser(resetUserTC, "res")
   354  
   355  	fakeClock := clockwork.NewFakeClockAt(time.Now())
   356  	tc.G.SetClock(fakeClock)
   357  
   358  	loadUpak := func() error {
   359  		t.Logf("loadUpak: using username:%+v", fu.Username)
   360  		loadArg := libkb.NewLoadUserArg(tc.G).WithUID(fu.UID()).WithNetContext(context.TODO()).WithStaleOK(false).WithForceMerkleServerPolling(true)
   361  
   362  		upak, _, err := tc.G.GetUPAKLoader().Load(loadArg)
   363  		if err != nil {
   364  			return err
   365  		}
   366  
   367  		t.Logf("loadUpak done: using username:%+v uid: %+v keys: %d", upak.Base.Username, upak.Base.Uid, len(upak.Base.DeviceKeys))
   368  		return nil
   369  	}
   370  
   371  	err := loadUpak()
   372  	if err != nil {
   373  		t.Fatalf("Failed to load user: %+v", err)
   374  	}
   375  
   376  	ResetAccount(resetUserTC, fu)
   377  
   378  	loadUpakExpectFailure := func() {
   379  		err := loadUpak()
   380  		if err == nil {
   381  			t.Fatalf("Expected UPAKLoader.Load to fail on nuked account.")
   382  		} else if _, ok := err.(libkb.NoKeyError); !ok {
   383  			t.Fatalf("Expected UPAKLoader.Load to fail with NoKeyError, instead failed with: %+v", err)
   384  		}
   385  	}
   386  
   387  	// advance the clock past the cache timeout
   388  	fakeClock.Advance(libkb.CachedUserTimeout * 10)
   389  	loadUpakExpectFailure()
   390  
   391  	// Try again, see if still errors out (this time user should not
   392  	// be in cache at all).
   393  	fakeClock.Advance(libkb.CachedUserTimeout * 10)
   394  	loadUpakExpectFailure()
   395  }
   396  
   397  func TestLoadAfterAcctReset2(t *testing.T) {
   398  	// One context for user that will be doing LoadUser, and another
   399  	// for user that will sign up and reset itself.
   400  	tc := SetupEngineTest(t, "clu")
   401  	defer tc.Cleanup()
   402  
   403  	resetUserTC := SetupEngineTest(t, "clu2")
   404  	defer resetUserTC.Cleanup()
   405  
   406  	t.Logf("create new user")
   407  	fu := CreateAndSignupFakeUser(resetUserTC, "res")
   408  
   409  	fakeClock := clockwork.NewFakeClockAt(time.Now())
   410  	tc.G.SetClock(fakeClock)
   411  
   412  	loadUpak := func() (*keybase1.UserPlusAllKeys, error) {
   413  		t.Logf("loadUpak: using username:%+v", fu.Username)
   414  		loadArg := libkb.NewLoadUserArg(tc.G).WithUID(fu.UID()).WithPublicKeyOptional().WithNetContext(context.TODO()).WithStaleOK(false)
   415  		upak, _, err := tc.G.GetUPAKLoader().Load(loadArg)
   416  		if err != nil {
   417  			return nil, err
   418  		}
   419  
   420  		t.Logf("loadUpak done: using username:%+v uid: %+v keys: %d", upak.Base.Username, upak.Base.Uid, len(upak.Base.DeviceKeys))
   421  		return upak, nil
   422  	}
   423  
   424  	upak1, err := loadUpak()
   425  	if err != nil {
   426  		t.Fatalf("Failed to load user: %+v", err)
   427  	}
   428  
   429  	// Reset account and then login again to establish new eldest and
   430  	// add new device keys.
   431  	ResetAccount(resetUserTC, fu)
   432  	tcp := SetupEngineTest(t, "login")
   433  	defer tcp.Cleanup()
   434  
   435  	fu.LoginOrBust(tcp)
   436  	if err := AssertProvisioned(tcp); err != nil {
   437  		t.Fatal(err)
   438  	}
   439  
   440  	fakeClock.Advance(libkb.CachedUserTimeout * 10)
   441  	upak2, err := loadUpak()
   442  	if err != nil {
   443  		t.Fatalf("Failed to load user after reset+login with: %+v", err)
   444  	}
   445  
   446  	if upak1.Base.DeviceKeys[0].KID == upak2.Base.DeviceKeys[0].KID {
   447  		t.Fatal("Found old device key after LoadUser.")
   448  	}
   449  }
   450  
   451  // Test the bug in CORE-6943: after a reset, if we did two
   452  // logins in a row, right on top of each other, previous subchains
   453  // would be dropped from the self UPAK.
   454  func TestLoadAfterAcctResetCORE6943(t *testing.T) {
   455  	tc := SetupEngineTest(t, "clu")
   456  	defer tc.Cleanup()
   457  	sigVersion := libkb.GetDefaultSigVersion(tc.G)
   458  
   459  	t.Logf("create new user")
   460  	fu := CreateAndSignupFakeUser(tc, "res")
   461  
   462  	trackAlice(tc, fu, sigVersion)
   463  
   464  	loadUpak := func() (*keybase1.UserPlusAllKeys, error) {
   465  		t.Logf("loadUpak: using username:%+v", fu.Username)
   466  		loadArg := libkb.NewLoadUserArg(tc.G).WithUID(fu.UID()).WithPublicKeyOptional().WithNetContext(context.TODO()).WithStaleOK(false)
   467  		upak, _, err := tc.G.GetUPAKLoader().Load(loadArg)
   468  		if err != nil {
   469  			return nil, err
   470  		}
   471  
   472  		t.Logf("loadUpak done: using username:%+v uid: %+v keys: %d", upak.Base.Username, upak.Base.Uid, len(upak.Base.DeviceKeys))
   473  		return upak, nil
   474  	}
   475  
   476  	upak1, err := loadUpak()
   477  	if err != nil {
   478  		t.Fatalf("Failed to load user: %+v", err)
   479  	}
   480  
   481  	// Reset account and then login again to establish new eldest and
   482  	// add new device keys.
   483  	ResetAccount(tc, fu)
   484  
   485  	tc.G.GetUPAKLoader().Invalidate(context.TODO(), fu.UID())
   486  
   487  	fu.LoginOrBust(tc)
   488  	if err := AssertProvisioned(tc); err != nil {
   489  		t.Fatal(err)
   490  	}
   491  	// login a second time to force the bug.
   492  	fu.LoginOrBust(tc)
   493  
   494  	// Make sure that we can load the eldest key from the previous subchain
   495  	_, _, _, err = tc.G.GetUPAKLoader().LoadKeyV2(context.TODO(), fu.UID(), upak1.Base.DeviceKeys[0].KID)
   496  
   497  	if err != nil {
   498  		t.Fatal("Failed to load a UID/KID combo from first incarnation")
   499  	}
   500  
   501  	_, err = loadUpak()
   502  	if err != nil {
   503  		t.Fatalf("Failed to load user: %+v", err)
   504  	}
   505  }
   506  
   507  func TestUPAKUnstub(t *testing.T) {
   508  	tc := SetupEngineTest(t, "login")
   509  	defer tc.Cleanup()
   510  
   511  	u1 := CreateAndSignupFakeUser(tc, "first")
   512  	Logout(tc)
   513  	u2 := CreateAndSignupFakeUser(tc, "secon")
   514  
   515  	testTrack(t, tc, libkb.KeybaseSignatureV2, "t_alice")
   516  	testTrack(t, tc, libkb.KeybaseSignatureV2, u1.Username)
   517  
   518  	// The last link is always unstubbed, so this is a throw-away so that we have some links that
   519  	// are stubbed (the two just above).
   520  	testTrack(t, tc, libkb.KeybaseSignatureV2, "t_bob")
   521  
   522  	Logout(tc)
   523  	t.Logf("first logging back in")
   524  	u1.LoginOrBust(tc)
   525  
   526  	upl := tc.G.GetUPAKLoader()
   527  	mctx := NewMetaContextForTest(tc)
   528  
   529  	// wipe out all the caches
   530  	_, err := tc.G.LocalDb.Nuke()
   531  	require.NoError(t, err)
   532  	upl.Invalidate(mctx.Ctx(), u2.UID())
   533  
   534  	assertStubbed := func() {
   535  		arg := libkb.NewLoadUserArgWithMetaContext(mctx).WithUID(u2.UID())
   536  		upak, _, err := upl.LoadV2(arg)
   537  		require.NoError(t, err)
   538  		require.Equal(t, 1, len(upak.Current.RemoteTracks))
   539  		require.Equal(t, "t_bob", upak.Current.RemoteTracks[keybase1.UID("afb5eda3154bc13c1df0189ce93ba119")].Username)
   540  		require.False(t, upak.Current.Unstubbed)
   541  	}
   542  
   543  	assertStubbed()
   544  
   545  	Logout(tc)
   546  	t.Logf("second logging back in")
   547  	u2.LoginOrBust(tc)
   548  
   549  	assertStubbed()
   550  
   551  	assertAllLinks := func(stubMode libkb.StubMode) {
   552  		arg := libkb.NewLoadUserArgWithMetaContext(mctx).WithUID(u2.UID()).WithStubMode(stubMode)
   553  		upak, _, err := upl.LoadV2(arg)
   554  		require.NoError(t, err)
   555  		require.Equal(t, 3, len(upak.Current.RemoteTracks))
   556  		require.Equal(t, u1.Username, upak.Current.RemoteTracks[u1.UID()].Username)
   557  		require.True(t, upak.Current.Unstubbed)
   558  	}
   559  
   560  	assertAllLinks(libkb.StubModeUnstubbed)
   561  
   562  	Logout(tc)
   563  	t.Logf("first logging back in")
   564  	u1.LoginOrBust(tc)
   565  
   566  	assertAllLinks(libkb.StubModeUnstubbed)
   567  	assertAllLinks(libkb.StubModeStubbed)
   568  }
   569  
   570  func TestInvalidation(t *testing.T) {
   571  	tc := SetupEngineTest(t, "login")
   572  	defer tc.Cleanup()
   573  	u := CreateAndSignupFakeUser(tc, "first")
   574  	upl := tc.G.GetUPAKLoader()
   575  	mctx := NewMetaContextForTest(tc)
   576  	arg := libkb.NewLoadUserArgWithMetaContext(mctx).WithUID(u.UID())
   577  	upak, _, err := upl.LoadV2(arg)
   578  	require.NoError(t, err)
   579  	require.NotNil(t, upak)
   580  	upl.Invalidate(mctx.Ctx(), u.UID())
   581  	arg = libkb.NewLoadUserArgWithMetaContext(mctx).WithUID(u.UID()).WithCachedOnly(true)
   582  	_, _, err = upl.LoadV2(arg)
   583  	require.Error(t, err)
   584  	require.IsType(t, libkb.UserNotFoundError{}, err)
   585  	require.Contains(t, err.Error(), "cached user found, but it was stale, and cached only")
   586  	arg = libkb.NewLoadUserArgWithMetaContext(mctx).WithUID(u.UID()).WithCachedOnly(true).WithStaleOK(true)
   587  	upak, _, err = upl.LoadV2(arg)
   588  	require.NoError(t, err)
   589  	require.NotNil(t, upak)
   590  }