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  }