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 }