package proxy import ( "context" "errors" "fmt" "os" "os/signal" "strings" "syscall" "testing" "time" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" abciclient "github.com/tendermint/tendermint/abci/client" abcimocks "github.com/tendermint/tendermint/abci/client/mocks" "github.com/tendermint/tendermint/abci/example/kvstore" "github.com/tendermint/tendermint/abci/server" "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/log" tmrand "github.com/tendermint/tendermint/libs/rand" "gotest.tools/assert" ) //---------------------------------------- type appConnTestI interface { Echo(context.Context, string) (*types.ResponseEcho, error) Flush(context.Context) error Info(context.Context, types.RequestInfo) (*types.ResponseInfo, error) } type appConnTest struct { appConn abciclient.Client } func newAppConnTest(appConn abciclient.Client) appConnTestI { return &appConnTest{appConn} } func (app *appConnTest) Echo(ctx context.Context, msg string) (*types.ResponseEcho, error) { return app.appConn.Echo(ctx, msg) } func (app *appConnTest) Flush(ctx context.Context) error { return app.appConn.Flush(ctx) } func (app *appConnTest) Info(ctx context.Context, req types.RequestInfo) (*types.ResponseInfo, error) { return app.appConn.Info(ctx, req) } //---------------------------------------- var SOCKET = "socket" func TestEcho(t *testing.T) { sockPath := fmt.Sprintf("unix:///tmp/echo_%v.sock", tmrand.Str(6)) logger := log.TestingLogger() client, err := abciclient.NewClient(logger, sockPath, SOCKET, true) if err != nil { t.Fatal(err) } ctx, cancel := context.WithCancel(context.Background()) defer cancel() // Start server s := server.NewSocketServer(logger.With("module", "abci-server"), sockPath, kvstore.NewApplication()) require.NoError(t, s.Start(ctx), "error starting socket server") t.Cleanup(func() { cancel(); s.Wait() }) // Start client require.NoError(t, client.Start(ctx), "Error starting ABCI client") proxy := newAppConnTest(client) t.Log("Connected") for i := 0; i < 1000; i++ { _, err = proxy.Echo(ctx, fmt.Sprintf("echo-%v", i)) if err != nil { t.Error(err) } // flush sometimes if i%128 == 0 { if err := proxy.Flush(ctx); err != nil { t.Error(err) } } } if err := proxy.Flush(ctx); err != nil { t.Error(err) } } func BenchmarkEcho(b *testing.B) { b.StopTimer() // Initialize sockPath := fmt.Sprintf("unix:///tmp/echo_%v.sock", tmrand.Str(6)) logger := log.TestingLogger() client, err := abciclient.NewClient(logger, sockPath, SOCKET, true) if err != nil { b.Fatal(err) } ctx, cancel := context.WithCancel(context.Background()) defer cancel() // Start server s := server.NewSocketServer(logger.With("module", "abci-server"), sockPath, kvstore.NewApplication()) require.NoError(b, s.Start(ctx), "Error starting socket server") b.Cleanup(func() { cancel(); s.Wait() }) // Start client require.NoError(b, client.Start(ctx), "Error starting ABCI client") proxy := newAppConnTest(client) b.Log("Connected") echoString := strings.Repeat(" ", 200) b.StartTimer() // Start benchmarking tests for i := 0; i < b.N; i++ { _, err = proxy.Echo(ctx, echoString) if err != nil { b.Error(err) } // flush sometimes if i%128 == 0 { if err := proxy.Flush(ctx); err != nil { b.Error(err) } } } if err := proxy.Flush(ctx); err != nil { b.Error(err) } b.StopTimer() // info := proxy.Info(types.RequestInfo{""}) // b.Log("N: ", b.N, info) } func TestInfo(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() sockPath := fmt.Sprintf("unix:///tmp/echo_%v.sock", tmrand.Str(6)) logger := log.TestingLogger() client, err := abciclient.NewClient(logger, sockPath, SOCKET, true) if err != nil { t.Fatal(err) } // Start server s := server.NewSocketServer(logger.With("module", "abci-server"), sockPath, kvstore.NewApplication()) require.NoError(t, s.Start(ctx), "Error starting socket server") t.Cleanup(func() { cancel(); s.Wait() }) // Start client require.NoError(t, client.Start(ctx), "Error starting ABCI client") proxy := newAppConnTest(client) t.Log("Connected") resInfo, err := proxy.Info(ctx, RequestInfo) require.NoError(t, err) if resInfo.Data != "{\"size\":0}" { t.Error("Expected ResponseInfo with one element '{\"size\":0}' but got something else") } } type noopStoppableClientImpl struct { abciclient.Client count int } func (c *noopStoppableClientImpl) Stop() { c.count++ } func TestAppConns_Start_Stop(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() clientMock := &abcimocks.Client{} clientMock.On("Start", mock.Anything).Return(nil) clientMock.On("Error").Return(nil) clientMock.On("IsRunning").Return(true) clientMock.On("Wait").Return(nil).Times(1) cl := &noopStoppableClientImpl{Client: clientMock} appConns := New(cl, log.TestingLogger(), NopMetrics()) err := appConns.Start(ctx) require.NoError(t, err) time.Sleep(200 * time.Millisecond) cancel() appConns.Wait() clientMock.AssertExpectations(t) assert.Equal(t, 1, cl.count) } // Upon failure, we call tmos.Kill func TestAppConns_Failure(t *testing.T) { c := make(chan os.Signal, 1) signal.Notify(c, syscall.SIGTERM, syscall.SIGABRT) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() clientMock := &abcimocks.Client{} clientMock.On("SetLogger", mock.Anything).Return() clientMock.On("Start", mock.Anything).Return(nil) clientMock.On("IsRunning").Return(true) clientMock.On("Wait").Return(nil) clientMock.On("Error").Return(errors.New("EOF")) cl := &noopStoppableClientImpl{Client: clientMock} appConns := New(cl, log.TestingLogger(), NopMetrics()) err := appConns.Start(ctx) require.NoError(t, err) t.Cleanup(func() { cancel(); appConns.Wait() }) select { case sig := <-c: t.Logf("signal %q successfully received", sig) case <-ctx.Done(): t.Fatal("expected process to receive SIGTERM signal") } }