github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/systests/gregor_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  	"bytes"
     8  	"strings"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/keybase/client/go/client"
    13  	"github.com/keybase/client/go/gregor"
    14  	"github.com/keybase/client/go/libkb"
    15  	gregor1 "github.com/keybase/client/go/protocol/gregor1"
    16  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    17  	"github.com/keybase/client/go/service"
    18  	"github.com/keybase/go-framed-msgpack-rpc/rpc"
    19  	"github.com/stretchr/testify/require"
    20  	context "golang.org/x/net/context"
    21  )
    22  
    23  type electronMock struct {
    24  	libkb.Contextified
    25  	errCh   chan error
    26  	stateCh chan keybase1.PushStateArg
    27  	oobmCh  chan gregor1.OutOfBandMessage
    28  }
    29  
    30  func (e *electronMock) PushState(ctx context.Context, a keybase1.PushStateArg) (err error) {
    31  	e.G().Log.Debug("electronMock::PushState: %#v\n", a)
    32  	e.stateCh <- a
    33  	return nil
    34  }
    35  
    36  func (e *electronMock) PushOutOfBandMessages(_ context.Context, msgs []gregor1.OutOfBandMessage) error {
    37  	for _, m := range msgs {
    38  		e.oobmCh <- m
    39  	}
    40  	return nil
    41  }
    42  
    43  func newElectronMock(g *libkb.GlobalContext) *electronMock {
    44  	return &electronMock{
    45  		Contextified: libkb.NewContextified(g),
    46  		errCh:        make(chan error, 1),
    47  		stateCh:      make(chan keybase1.PushStateArg, 10),
    48  		oobmCh:       make(chan gregor1.OutOfBandMessage, 10),
    49  	}
    50  }
    51  
    52  // filterPubsubdItems removes pubsubd generated IBMs from state
    53  func filterPubsubdItems(items []gregor1.ItemAndMetadata) (res []gregor1.ItemAndMetadata) {
    54  	for _, i := range items {
    55  		categoryStr := i.Category().String()
    56  		if !strings.HasPrefix(categoryStr, "user.") &&
    57  			!strings.HasPrefix(categoryStr, "stellar.") &&
    58  			!strings.HasPrefix(categoryStr, "device.") &&
    59  			!strings.HasPrefix(categoryStr, "home.") {
    60  			res = append(res, i)
    61  		}
    62  	}
    63  	return
    64  }
    65  
    66  func TestGregorForwardToElectron(t *testing.T) {
    67  	tc := setupTest(t, "gregor")
    68  	defer tc.Cleanup()
    69  	tc1 := cloneContext(tc)
    70  	defer tc1.Cleanup()
    71  
    72  	svc := service.NewService(tc.G, false)
    73  	startCh := svc.GetStartChannel()
    74  	stopCh := make(chan error)
    75  	go func() {
    76  		tc.G.Log.Debug("+ Service.Run")
    77  		err := svc.Run()
    78  		tc.G.Log.Debug("- Service.Run")
    79  		require.NoError(t, err)
    80  		stopCh <- err
    81  	}()
    82  
    83  	userInfo := randomUser("grgr")
    84  	sui := signupUI{
    85  		info:         userInfo,
    86  		Contextified: libkb.NewContextified(tc.G),
    87  	}
    88  	tc.G.SetUI(&sui)
    89  	signup := client.NewCmdSignupRunner(tc.G)
    90  	// Signup without e-mail address so we don't get a gregor badge
    91  	// ("unverified email") immediately after signing up.
    92  	signup.SetNoEmail()
    93  	signup.SetTest()
    94  
    95  	// Wait for the server to start up
    96  	<-startCh
    97  	require.NoError(t, signup.Run())
    98  
    99  	cli, xp, err := client.GetRPCClientWithContext(tc1.G)
   100  	require.NoError(t, err)
   101  	srv := rpc.NewServer(xp, nil)
   102  	em := newElectronMock(tc.G)
   103  	err = srv.Register(keybase1.GregorUIProtocol(em))
   104  	require.NoError(t, err)
   105  	ncli := keybase1.DelegateUiCtlClient{Cli: cli}
   106  
   107  	// Spin until gregor comes up; it should come up after signup
   108  	var ok bool
   109  	for i := 0; !ok && i < 200; i++ {
   110  		if ok = svc.HasGregor(); !ok {
   111  			time.Sleep(50 * time.Millisecond)
   112  		} else {
   113  			tc.G.Log.Debug("spinning, waiting for gregor to come up (attempt %d)", i)
   114  		}
   115  	}
   116  	require.True(t, ok)
   117  
   118  	svc.SetGregorPushStateFilter(func(m gregor.Message) bool {
   119  		cat := m.ToInBandMessage().ToStateUpdateMessage().Creation().Category()
   120  		return cat.String() != "user.identity_change" && cat.String() != "user.key_change" &&
   121  			!strings.HasPrefix(cat.String(), "home.") && !strings.HasPrefix(cat.String(), "stellar.")
   122  	})
   123  	require.NoError(t, ncli.RegisterGregorFirehose(context.TODO()))
   124  
   125  	select {
   126  	case a := <-em.stateCh:
   127  		require.Equal(t, keybase1.PushReason_RECONNECTED, a.Reason)
   128  		require.Zero(t, filterPubsubdItems(a.State.Items_))
   129  	case <-time.After(3 * time.Second):
   130  		require.Fail(t, "no reconnect message")
   131  	}
   132  
   133  	msgID, err := svc.GregorInject("foo", []byte("bar"))
   134  	require.NoError(t, err)
   135  	require.NoError(t, svc.GregorInjectOutOfBandMessage("baz", []byte("bip")))
   136  
   137  	checkState := func(s gregor1.State) {
   138  		items := filterPubsubdItems(s.Items_)
   139  		require.Equal(t, 1, len(items))
   140  		i := items[0]
   141  		require.True(t, bytes.Equal(i.Md_.MsgID_.Bytes(), msgID.Bytes()))
   142  		require.Equal(t, "foo", i.Item_.Category_.String())
   143  		require.Equal(t, "bar", string(i.Item_.Body_.Bytes()))
   144  	}
   145  
   146  	// We get two push states, one from the local send, and one from receiving broadcast
   147  	for i := 0; i < 2; i++ {
   148  		select {
   149  		case pushArg := <-em.stateCh:
   150  			checkState(pushArg.State)
   151  			require.Equal(t, keybase1.PushReason_NEW_DATA, pushArg.Reason)
   152  		case <-time.After(10 * time.Second):
   153  			require.Fail(t, "no ibm")
   154  		}
   155  	}
   156  
   157  	pollForTrue(t, tc.G, func(i int) bool {
   158  		select {
   159  		case oobm := <-em.oobmCh:
   160  			if oobm.System_ != "baz" {
   161  				return false
   162  			}
   163  			if s := string(oobm.Body_); s != "bip" {
   164  				return false
   165  			}
   166  			return true
   167  		case <-time.After(3 * time.Second * libkb.CITimeMultiplier(tc.G)):
   168  			return false
   169  		}
   170  	})
   171  
   172  	svc.SimulateGregorCrashForTesting()
   173  	select {
   174  	case pushArg := <-em.stateCh:
   175  		checkState(pushArg.State)
   176  		require.Equal(t, keybase1.PushReason_RECONNECTED, pushArg.Reason)
   177  	case <-time.After(10 * time.Second):
   178  		require.Fail(t, "no ibm")
   179  	}
   180  
   181  	gcli := keybase1.GregorClient{Cli: cli}
   182  	state, err := gcli.GetState(context.TODO())
   183  	require.NoError(t, err)
   184  	checkState(state)
   185  
   186  	require.NoError(t, CtlStop(tc.G))
   187  	// If the server failed, it's also an error
   188  	require.NoError(t, <-stopCh)
   189  }