github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/systests/delegate_id3_ui_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  package systests
     5  
     6  import (
     7  	"fmt"
     8  	"sync"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/keybase/client/go/client"
    13  	"github.com/keybase/client/go/libkb"
    14  	"github.com/keybase/client/go/service"
    15  
    16  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    17  	"github.com/keybase/go-framed-msgpack-rpc/rpc"
    18  	"github.com/stretchr/testify/require"
    19  	context "golang.org/x/net/context"
    20  )
    21  
    22  type delegateID3UI struct {
    23  	libkb.Contextified
    24  	sync.Mutex
    25  	T  *testing.T
    26  	ch chan struct{}
    27  
    28  	guiid         keybase1.Identify3GUIID
    29  	displayedCard bool
    30  
    31  	launchedGithub  bool
    32  	foundGithub     bool
    33  	launchedTwitter bool
    34  	foundTwitter    bool
    35  }
    36  
    37  // delegateUI implements the keybase1.IdentifyUiInterface
    38  var _ keybase1.Identify3UiInterface = (*delegateID3UI)(nil)
    39  
    40  func (d *delegateID3UI) Identify3ShowTracker(_ context.Context, arg keybase1.Identify3ShowTrackerArg) error {
    41  	d.Lock()
    42  	defer d.Unlock()
    43  	d.guiid = arg.GuiID
    44  	require.Equal(d.T, string(arg.Assertion), "t_alice")
    45  	return nil
    46  }
    47  
    48  func (d *delegateID3UI) Identify3UpdateRow(_ context.Context, arg keybase1.Identify3Row) error {
    49  	d.Lock()
    50  	defer d.Unlock()
    51  	require.Equal(d.T, d.guiid, arg.GuiID)
    52  
    53  	poke := func(launched *bool, found *bool) {
    54  		switch arg.State {
    55  		case keybase1.Identify3RowState_CHECKING:
    56  			require.False(d.T, *found)
    57  			require.False(d.T, *launched)
    58  			*launched = true
    59  		case keybase1.Identify3RowState_VALID:
    60  			require.True(d.T, *launched)
    61  			require.False(d.T, *found)
    62  			*found = true
    63  		default:
    64  			require.Fail(d.T, fmt.Sprintf("unexpected state: %+v", arg))
    65  		}
    66  	}
    67  	switch arg.Key {
    68  	case "twitter":
    69  		poke(&d.launchedTwitter, &d.foundTwitter)
    70  	case "github":
    71  		poke(&d.launchedGithub, &d.foundGithub)
    72  	}
    73  	return nil
    74  }
    75  
    76  func (d *delegateID3UI) Identify3UserReset(context.Context, keybase1.Identify3GUIID) error {
    77  	require.Fail(d.T, "did not exect reset user scenario")
    78  	return nil
    79  }
    80  
    81  func (d *delegateID3UI) Identify3UpdateUserCard(_ context.Context, arg keybase1.Identify3UpdateUserCardArg) error {
    82  	d.Lock()
    83  	defer d.Unlock()
    84  	require.Equal(d.T, d.guiid, arg.GuiID)
    85  	d.displayedCard = true
    86  	return nil
    87  }
    88  
    89  func (d *delegateID3UI) Identify3TrackerTimedOut(context.Context, keybase1.Identify3GUIID) error {
    90  	require.Fail(d.T, "did not expect a tracker time out")
    91  	return nil
    92  }
    93  
    94  func (d *delegateID3UI) Identify3Result(_ context.Context, arg keybase1.Identify3ResultArg) error {
    95  	d.Lock()
    96  	require.Equal(d.T, arg.GuiID, d.guiid)
    97  	require.Equal(d.T, arg.Result, keybase1.Identify3ResultType_OK)
    98  	d.Unlock()
    99  	close(d.ch)
   100  	return nil
   101  }
   102  
   103  func (d *delegateID3UI) Identify3Summary(_ context.Context, arg keybase1.Identify3Summary) error {
   104  	return nil
   105  }
   106  
   107  func newDelegateID3UI(g *libkb.GlobalContext, t *testing.T) *delegateID3UI {
   108  	return &delegateID3UI{
   109  		Contextified: libkb.NewContextified(g),
   110  		T:            t,
   111  		ch:           make(chan struct{}),
   112  	}
   113  }
   114  
   115  // checkSuccess makes sure that all 3 success markers are true. It would be nice
   116  // if we just checked all 3 bools, but there's a race because of Notify() use,
   117  // since we don't get a guarantee of when the Notify()s go out.
   118  func (d *delegateID3UI) checkSuccess() {
   119  
   120  	check := func() bool {
   121  		d.Lock()
   122  		defer d.Unlock()
   123  		if !d.foundTwitter {
   124  			d.T.Logf("delegate3IDUI#checkSuccess: check twitter failed")
   125  			return false
   126  		}
   127  		if !d.foundGithub {
   128  			d.T.Logf("delegate3IDUI#checkSuccess: check github failed")
   129  			return false
   130  		}
   131  		if !d.displayedCard {
   132  			d.T.Logf("delegate3IDUI#checkSuccess: didn't display card")
   133  			return false
   134  		}
   135  		return true
   136  	}
   137  	n := 10
   138  	wait := 2 * time.Millisecond
   139  	for i := 0; i < n; i++ {
   140  		if check() {
   141  			return
   142  		}
   143  		d.T.Logf("Hit a race! Waiting %v for delegateID3UI#checkSuccess check to work", wait)
   144  		time.Sleep(wait)
   145  		wait *= 2
   146  	}
   147  	d.T.Fatalf("Tried %d times to get successes and failed", n)
   148  }
   149  
   150  func TestDelegateIdentify3UI(t *testing.T) {
   151  	tc := setupTest(t, "delegate_ui")
   152  	defer tc.Cleanup()
   153  	tc1 := cloneContext(tc)
   154  	defer tc1.Cleanup()
   155  	tc2 := cloneContext(tc)
   156  	defer tc2.Cleanup()
   157  
   158  	stopCh := make(chan error)
   159  	svc := service.NewService(tc.G, false)
   160  	startCh := svc.GetStartChannel()
   161  	go func() {
   162  		err := svc.Run()
   163  		if err != nil {
   164  			t.Logf("Running the service produced an error: %v", err)
   165  		}
   166  		stopCh <- err
   167  	}()
   168  
   169  	// Wait for the server to start up
   170  	<-startCh
   171  	dui := newDelegateID3UI(tc.G, t)
   172  
   173  	launchDelegateUI := func(dui *delegateID3UI) error {
   174  		cli, xp, err := client.GetRPCClientWithContext(tc2.G)
   175  		if err != nil {
   176  			return err
   177  		}
   178  		srv := rpc.NewServer(xp, nil)
   179  		if err = srv.Register(keybase1.Identify3UiProtocol(dui)); err != nil {
   180  			return err
   181  		}
   182  		ncli := keybase1.DelegateUiCtlClient{Cli: cli}
   183  		return ncli.RegisterIdentify3UI(context.TODO())
   184  	}
   185  
   186  	// Launch the delegate UI
   187  	err := launchDelegateUI(dui)
   188  	require.NoError(t, err)
   189  
   190  	id := client.NewCmdIDRunner(tc1.G)
   191  	id.SetUser("t_alice")
   192  	id.UseDelegateUI()
   193  	err = id.Run()
   194  	require.NoError(t, err)
   195  
   196  	// We should get a close on this channel when the UI is read to go.
   197  	_, eof := <-dui.ch
   198  	require.False(t, eof)
   199  	dui.checkSuccess()
   200  
   201  	err = CtlStop(tc1.G)
   202  	require.NoError(t, err)
   203  
   204  	// If the server failed, it's also an error
   205  	err = <-stopCh
   206  	require.NoError(t, err)
   207  }