github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/systests/delegate_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  	"testing"
     9  	"time"
    10  
    11  	"github.com/keybase/client/go/client"
    12  	"github.com/keybase/client/go/service"
    13  
    14  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    15  	"github.com/keybase/go-framed-msgpack-rpc/rpc"
    16  	context "golang.org/x/net/context"
    17  )
    18  
    19  type delegateUI struct {
    20  	T         *testing.T
    21  	ch        chan error
    22  	delegated bool
    23  	started   bool
    24  	finished  bool
    25  	canceled  bool
    26  
    27  	launchedGithub  bool
    28  	foundGithub     bool
    29  	launchedTwitter bool
    30  	foundTwitter    bool
    31  }
    32  
    33  // delegateUI implements the keybase1.IdentifyUiInterface
    34  var _ keybase1.IdentifyUiInterface = (*delegateUI)(nil)
    35  
    36  func (d *delegateUI) checkDelegated() error {
    37  	if !d.delegated {
    38  		return d.setError(fmt.Errorf("Can't run UI since it wasn't properly delegated"))
    39  	}
    40  	return nil
    41  }
    42  
    43  func (d *delegateUI) setError(e error) error {
    44  	d.T.Logf("delegateUI error: %v", e)
    45  	fmt.Printf("delegateUI error: %v\n", e)
    46  	go func() { d.ch <- e }()
    47  	return e
    48  }
    49  
    50  func (d *delegateUI) checkStarted() error {
    51  	if err := d.checkDelegated(); err != nil {
    52  		return err
    53  	}
    54  	if !d.started {
    55  		return d.setError(fmt.Errorf("Can't run UI since it wasn't properly started"))
    56  	}
    57  	if d.canceled {
    58  		return d.setError(fmt.Errorf("Can't run UI after Cancel() was called"))
    59  	}
    60  	return nil
    61  }
    62  
    63  func (d *delegateUI) DelegateIdentifyUI(context.Context) (int, error) {
    64  	d.delegated = true
    65  	return 1, nil
    66  }
    67  func (d *delegateUI) Start(context.Context, keybase1.StartArg) error {
    68  	if err := d.checkDelegated(); err != nil {
    69  		return err
    70  	}
    71  	d.started = true
    72  	return nil
    73  }
    74  
    75  func (d *delegateUI) DisplayKey(context.Context, keybase1.DisplayKeyArg) error {
    76  	return d.checkStarted()
    77  }
    78  func (d *delegateUI) ReportLastTrack(context.Context, keybase1.ReportLastTrackArg) error {
    79  	return d.checkStarted()
    80  }
    81  func (d *delegateUI) LaunchNetworkChecks(_ context.Context, arg keybase1.LaunchNetworkChecksArg) error {
    82  	if err := d.checkStarted(); err != nil {
    83  		return err
    84  	}
    85  	for _, proof := range arg.Identity.Proofs {
    86  		switch proof.Proof.Key {
    87  		case "twitter":
    88  			d.launchedTwitter = true
    89  		case "github":
    90  			d.launchedGithub = true
    91  		}
    92  	}
    93  	return nil
    94  }
    95  func (d *delegateUI) DisplayTrackStatement(context.Context, keybase1.DisplayTrackStatementArg) error {
    96  	return d.checkStarted()
    97  }
    98  func (d *delegateUI) ReportTrackToken(context.Context, keybase1.ReportTrackTokenArg) error {
    99  	return d.checkStarted()
   100  }
   101  func (d *delegateUI) FinishWebProofCheck(context.Context, keybase1.FinishWebProofCheckArg) error {
   102  	return d.checkStarted()
   103  }
   104  func (d *delegateUI) FinishSocialProofCheck(_ context.Context, arg keybase1.FinishSocialProofCheckArg) error {
   105  	if err := d.checkStarted(); err != nil {
   106  		return err
   107  	}
   108  	switch arg.Rp.Key {
   109  	case "twitter":
   110  		d.foundTwitter = true
   111  	case "github":
   112  		d.foundGithub = true
   113  	}
   114  	return nil
   115  }
   116  func (d *delegateUI) DisplayCryptocurrency(context.Context, keybase1.DisplayCryptocurrencyArg) error {
   117  	return d.checkStarted()
   118  }
   119  func (d *delegateUI) DisplayStellarAccount(context.Context, keybase1.DisplayStellarAccountArg) error {
   120  	return d.checkStarted()
   121  }
   122  func (d *delegateUI) DisplayUserCard(context.Context, keybase1.DisplayUserCardArg) error {
   123  	return d.checkStarted()
   124  }
   125  func (d *delegateUI) Confirm(context.Context, keybase1.ConfirmArg) (res keybase1.ConfirmResult, err error) {
   126  	if err = d.checkStarted(); err != nil {
   127  		return res, err
   128  	}
   129  	res.IdentityConfirmed = true
   130  	res.RemoteConfirmed = true
   131  	return res, nil
   132  }
   133  func (d *delegateUI) Cancel(context.Context, int) error {
   134  	close(d.ch)
   135  	d.canceled = true
   136  	return nil
   137  }
   138  func (d *delegateUI) Finish(context.Context, int) error {
   139  	if err := d.checkStarted(); err != nil {
   140  		return err
   141  	}
   142  	d.finished = true
   143  	return nil
   144  }
   145  func (d *delegateUI) Dismiss(context.Context, keybase1.DismissArg) error {
   146  	return d.checkStarted()
   147  }
   148  
   149  func (d *delegateUI) DisplayTLFCreateWithInvite(context.Context, keybase1.DisplayTLFCreateWithInviteArg) error {
   150  	return nil
   151  }
   152  
   153  func (d *delegateUI) checkSuccess() error {
   154  	if !d.launchedGithub || !d.foundGithub || !d.launchedTwitter || !d.foundTwitter || !d.canceled {
   155  		return fmt.Errorf("Bad final state for delegate UI: %+v", d)
   156  	}
   157  	return nil
   158  }
   159  
   160  func newDelegateUI(t *testing.T) *delegateUI {
   161  	return &delegateUI{
   162  		T:  t,
   163  		ch: make(chan error),
   164  	}
   165  }
   166  
   167  func TestDelegateUI(t *testing.T) {
   168  	tc := setupTest(t, "delegate_ui")
   169  	defer tc.Cleanup()
   170  
   171  	tc1 := cloneContext(tc)
   172  	defer tc1.Cleanup()
   173  	tc2 := cloneContext(tc)
   174  	defer tc2.Cleanup()
   175  
   176  	stopCh := make(chan error)
   177  	svc := service.NewService(tc.G, false)
   178  	startCh := svc.GetStartChannel()
   179  	go func() {
   180  		err := svc.Run()
   181  		if err != nil {
   182  			t.Logf("Running the service produced an error: %v", err)
   183  		}
   184  		stopCh <- err
   185  	}()
   186  
   187  	// Wait for the server to start up
   188  	<-startCh
   189  	dui := newDelegateUI(t)
   190  
   191  	launchDelegateUI := func(dui *delegateUI) error {
   192  		cli, xp, err := client.GetRPCClientWithContext(tc2.G)
   193  		if err != nil {
   194  			return err
   195  		}
   196  		srv := rpc.NewServer(xp, nil)
   197  		if err = srv.Register(keybase1.IdentifyUiProtocol(dui)); err != nil {
   198  			return err
   199  		}
   200  		ncli := keybase1.DelegateUiCtlClient{Cli: cli}
   201  		return ncli.RegisterIdentifyUI(context.TODO())
   202  	}
   203  
   204  	// Launch the delegate UI
   205  	if err := launchDelegateUI(dui); err != nil {
   206  		t.Fatal(err)
   207  	}
   208  
   209  	id := client.NewCmdIDRunner(tc1.G)
   210  	id.SetUser("t_alice")
   211  	id.UseDelegateUI()
   212  	if err := id.Run(); err != nil {
   213  		t.Fatalf("Error in Run: %v", err)
   214  	}
   215  
   216  	// We should get either a 'done' or an 'error' from the delegateUI.
   217  	select {
   218  	case err, ok := <-dui.ch:
   219  		if err != nil {
   220  			t.Errorf("Error with delegate UI: %v", err)
   221  		} else if ok {
   222  			t.Errorf("Delegate UI didn't close the channel properly")
   223  		} else if err = dui.checkSuccess(); err != nil {
   224  			t.Error(err)
   225  		}
   226  	case <-time.After(20 * time.Second):
   227  		t.Fatal("no callback from delegate UI")
   228  	}
   229  
   230  	if err := CtlStop(tc1.G); err != nil {
   231  		t.Errorf("Error in stopping service: %v", err)
   232  	}
   233  
   234  	// If the server failed, it's also an error
   235  	if err := <-stopCh; err != nil {
   236  		t.Fatal(err)
   237  	}
   238  }