github.com/vipernet-xyz/tm@v0.34.24/abci/client/socket_client_test.go (about) 1 package abcicli_test 2 3 import ( 4 "fmt" 5 "sync" 6 "testing" 7 "time" 8 9 "github.com/stretchr/testify/assert" 10 "github.com/stretchr/testify/require" 11 12 abcicli "github.com/vipernet-xyz/tm/abci/client" 13 "github.com/vipernet-xyz/tm/abci/server" 14 "github.com/vipernet-xyz/tm/abci/types" 15 tmrand "github.com/vipernet-xyz/tm/libs/rand" 16 "github.com/vipernet-xyz/tm/libs/service" 17 ) 18 19 func TestProperSyncCalls(t *testing.T) { 20 app := slowApp{} 21 22 s, c := setupClientServer(t, app) 23 t.Cleanup(func() { 24 if err := s.Stop(); err != nil { 25 t.Error(err) 26 } 27 }) 28 t.Cleanup(func() { 29 if err := c.Stop(); err != nil { 30 t.Error(err) 31 } 32 }) 33 34 resp := make(chan error, 1) 35 go func() { 36 // This is BeginBlockSync unrolled.... 37 reqres := c.BeginBlockAsync(types.RequestBeginBlock{}) 38 err := c.FlushSync() 39 require.NoError(t, err) 40 res := reqres.Response.GetBeginBlock() 41 require.NotNil(t, res) 42 resp <- c.Error() 43 }() 44 45 select { 46 case <-time.After(time.Second): 47 require.Fail(t, "No response arrived") 48 case err, ok := <-resp: 49 require.True(t, ok, "Must not close channel") 50 assert.NoError(t, err, "This should return success") 51 } 52 } 53 54 func TestHangingSyncCalls(t *testing.T) { 55 app := slowApp{} 56 57 s, c := setupClientServer(t, app) 58 t.Cleanup(func() { 59 if err := s.Stop(); err != nil { 60 t.Log(err) 61 } 62 }) 63 t.Cleanup(func() { 64 if err := c.Stop(); err != nil { 65 t.Log(err) 66 } 67 }) 68 69 resp := make(chan error, 1) 70 go func() { 71 // Start BeginBlock and flush it 72 reqres := c.BeginBlockAsync(types.RequestBeginBlock{}) 73 flush := c.FlushAsync() 74 // wait 20 ms for all events to travel socket, but 75 // no response yet from server 76 time.Sleep(20 * time.Millisecond) 77 // kill the server, so the connections break 78 err := s.Stop() 79 require.NoError(t, err) 80 81 // wait for the response from BeginBlock 82 reqres.Wait() 83 flush.Wait() 84 resp <- c.Error() 85 }() 86 87 select { 88 case <-time.After(time.Second): 89 require.Fail(t, "No response arrived") 90 case err, ok := <-resp: 91 require.True(t, ok, "Must not close channel") 92 assert.Error(t, err, "We should get EOF error") 93 } 94 } 95 96 func setupClientServer(t *testing.T, app types.Application) ( 97 service.Service, abcicli.Client) { 98 // some port between 20k and 30k 99 port := 20000 + tmrand.Int32()%10000 100 addr := fmt.Sprintf("localhost:%d", port) 101 102 s, err := server.NewServer(addr, "socket", app) 103 require.NoError(t, err) 104 err = s.Start() 105 require.NoError(t, err) 106 107 c := abcicli.NewSocketClient(addr, true) 108 err = c.Start() 109 require.NoError(t, err) 110 111 return s, c 112 } 113 114 type slowApp struct { 115 types.BaseApplication 116 } 117 118 func (slowApp) BeginBlock(req types.RequestBeginBlock) types.ResponseBeginBlock { 119 time.Sleep(200 * time.Millisecond) 120 return types.ResponseBeginBlock{} 121 } 122 123 // TestCallbackInvokedWhenSetLaet ensures that the callback is invoked when 124 // set after the client completes the call into the app. Currently this 125 // test relies on the callback being allowed to be invoked twice if set multiple 126 // times, once when set early and once when set late. 127 func TestCallbackInvokedWhenSetLate(t *testing.T) { 128 wg := &sync.WaitGroup{} 129 wg.Add(1) 130 app := blockedABCIApplication{ 131 wg: wg, 132 } 133 _, c := setupClientServer(t, app) 134 reqRes := c.CheckTxAsync(types.RequestCheckTx{}) 135 136 done := make(chan struct{}) 137 cb := func(_ *types.Response) { 138 close(done) 139 } 140 reqRes.SetCallback(cb) 141 app.wg.Done() 142 <-done 143 144 var called bool 145 cb = func(_ *types.Response) { 146 called = true 147 } 148 reqRes.SetCallback(cb) 149 require.True(t, called) 150 } 151 152 type blockedABCIApplication struct { 153 wg *sync.WaitGroup 154 types.BaseApplication 155 } 156 157 func (b blockedABCIApplication) CheckTx(r types.RequestCheckTx) types.ResponseCheckTx { 158 b.wg.Wait() 159 return b.BaseApplication.CheckTx(r) 160 } 161 162 // TestCallbackInvokedWhenSetEarly ensures that the callback is invoked when 163 // set before the client completes the call into the app. 164 func TestCallbackInvokedWhenSetEarly(t *testing.T) { 165 wg := &sync.WaitGroup{} 166 wg.Add(1) 167 app := blockedABCIApplication{ 168 wg: wg, 169 } 170 _, c := setupClientServer(t, app) 171 reqRes := c.CheckTxAsync(types.RequestCheckTx{}) 172 173 done := make(chan struct{}) 174 cb := func(_ *types.Response) { 175 close(done) 176 } 177 reqRes.SetCallback(cb) 178 app.wg.Done() 179 180 called := func() bool { 181 select { 182 case <-done: 183 return true 184 default: 185 return false 186 } 187 } 188 require.Eventually(t, called, time.Second, time.Millisecond*25) 189 }