github.com/Finschia/ostracon@v1.1.5/abci/example/kvstore/kvstore_test.go (about)

     1  package kvstore
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"sort"
     7  	"testing"
     8  
     9  	"github.com/stretchr/testify/require"
    10  
    11  	"github.com/tendermint/tendermint/abci/types"
    12  	tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
    13  
    14  	abcicli "github.com/Finschia/ostracon/abci/client"
    15  	"github.com/Finschia/ostracon/abci/example/code"
    16  	abciserver "github.com/Finschia/ostracon/abci/server"
    17  	ocabci "github.com/Finschia/ostracon/abci/types"
    18  	"github.com/Finschia/ostracon/libs/log"
    19  	"github.com/Finschia/ostracon/libs/service"
    20  )
    21  
    22  const (
    23  	testKey   = "abc"
    24  	testValue = "def"
    25  )
    26  
    27  func testKVStore(t *testing.T, app ocabci.Application, tx []byte, key, value string) {
    28  	req := types.RequestDeliverTx{Tx: tx}
    29  	ar := app.DeliverTx(req)
    30  	require.False(t, ar.IsErr(), ar)
    31  	// repeating tx doesn't raise error
    32  	ar = app.DeliverTx(req)
    33  	require.False(t, ar.IsErr(), ar)
    34  	// commit
    35  	app.Commit()
    36  
    37  	info := app.Info(types.RequestInfo{})
    38  	require.NotZero(t, info.LastBlockHeight)
    39  
    40  	// make sure query is fine
    41  	resQuery := app.Query(types.RequestQuery{
    42  		Path: "/store",
    43  		Data: []byte(key),
    44  	})
    45  	require.Equal(t, code.CodeTypeOK, resQuery.Code)
    46  	require.Equal(t, key, string(resQuery.Key))
    47  	require.Equal(t, value, string(resQuery.Value))
    48  	require.EqualValues(t, info.LastBlockHeight, resQuery.Height)
    49  
    50  	// make sure proof is fine
    51  	resQuery = app.Query(types.RequestQuery{
    52  		Path:  "/store",
    53  		Data:  []byte(key),
    54  		Prove: true,
    55  	})
    56  	require.EqualValues(t, code.CodeTypeOK, resQuery.Code)
    57  	require.Equal(t, key, string(resQuery.Key))
    58  	require.Equal(t, value, string(resQuery.Value))
    59  	require.EqualValues(t, info.LastBlockHeight, resQuery.Height)
    60  }
    61  
    62  func TestKVStoreKV(t *testing.T) {
    63  	kvstore := NewApplication()
    64  	key := testKey
    65  	value := key
    66  	tx := []byte(key)
    67  	testKVStore(t, kvstore, tx, key, value)
    68  
    69  	value = testValue
    70  	tx = []byte(key + "=" + value)
    71  	testKVStore(t, kvstore, tx, key, value)
    72  }
    73  
    74  func TestPersistentKVStoreKV(t *testing.T) {
    75  	dir, err := os.MkdirTemp("/tmp", "abci-kvstore-test") // TODO
    76  	if err != nil {
    77  		t.Fatal(err)
    78  	}
    79  	kvstore := NewPersistentKVStoreApplication(dir)
    80  	key := testKey
    81  	value := key
    82  	tx := []byte(key)
    83  	testKVStore(t, kvstore, tx, key, value)
    84  
    85  	value = testValue
    86  	tx = []byte(key + "=" + value)
    87  	testKVStore(t, kvstore, tx, key, value)
    88  }
    89  
    90  func TestPersistentKVStoreInfo(t *testing.T) {
    91  	dir, err := os.MkdirTemp("/tmp", "abci-kvstore-test") // TODO
    92  	if err != nil {
    93  		t.Fatal(err)
    94  	}
    95  	kvstore := NewPersistentKVStoreApplication(dir)
    96  	InitKVStore(kvstore)
    97  	height := int64(0)
    98  
    99  	resInfo := kvstore.Info(types.RequestInfo{})
   100  	if resInfo.LastBlockHeight != height {
   101  		t.Fatalf("expected height of %d, got %d", height, resInfo.LastBlockHeight)
   102  	}
   103  
   104  	// make and apply block
   105  	height = int64(1)
   106  	hash := []byte("foo")
   107  	header := tmproto.Header{
   108  		Height: height,
   109  	}
   110  	kvstore.BeginBlock(ocabci.RequestBeginBlock{Hash: hash, Header: header})
   111  	kvstore.EndBlock(types.RequestEndBlock{Height: header.Height})
   112  	kvstore.Commit()
   113  
   114  	resInfo = kvstore.Info(types.RequestInfo{})
   115  	if resInfo.LastBlockHeight != height {
   116  		t.Fatalf("expected height of %d, got %d", height, resInfo.LastBlockHeight)
   117  	}
   118  }
   119  
   120  // add a validator, remove a validator, update a validator
   121  func TestValUpdates(t *testing.T) {
   122  	dir, err := os.MkdirTemp("/tmp", "abci-kvstore-test") // TODO
   123  	if err != nil {
   124  		t.Fatal(err)
   125  	}
   126  	kvstore := NewPersistentKVStoreApplication(dir)
   127  
   128  	// init with some validators
   129  	total := 10
   130  	nInit := 5
   131  	vals := RandVals(total)
   132  	// initialize with the first nInit
   133  	kvstore.InitChain(types.RequestInitChain{
   134  		Validators: vals[:nInit],
   135  	})
   136  
   137  	vals1, vals2 := vals[:nInit], kvstore.Validators()
   138  	valsEqual(t, vals1, vals2)
   139  
   140  	var v1, v2, v3 types.ValidatorUpdate
   141  
   142  	// add some validators
   143  	v1, v2 = vals[nInit], vals[nInit+1]
   144  	diff := []types.ValidatorUpdate{v1, v2}
   145  	tx1 := MakeValSetChangeTx(v1.PubKey, v1.Power)
   146  	tx2 := MakeValSetChangeTx(v2.PubKey, v2.Power)
   147  
   148  	makeApplyBlock(t, kvstore, 1, diff, tx1, tx2)
   149  
   150  	vals1, vals2 = vals[:nInit+2], kvstore.Validators()
   151  	valsEqual(t, vals1, vals2)
   152  
   153  	// remove some validators
   154  	v1, v2, v3 = vals[nInit-2], vals[nInit-1], vals[nInit]
   155  	v1.Power = 0
   156  	v2.Power = 0
   157  	v3.Power = 0
   158  	diff = []types.ValidatorUpdate{v1, v2, v3}
   159  	tx1 = MakeValSetChangeTx(v1.PubKey, v1.Power)
   160  	tx2 = MakeValSetChangeTx(v2.PubKey, v2.Power)
   161  	tx3 := MakeValSetChangeTx(v3.PubKey, v3.Power)
   162  
   163  	makeApplyBlock(t, kvstore, 2, diff, tx1, tx2, tx3)
   164  
   165  	vals1 = append(vals[:nInit-2], vals[nInit+1]) // nolint: gocritic
   166  	vals2 = kvstore.Validators()
   167  	valsEqual(t, vals1, vals2)
   168  
   169  	// update some validators
   170  	v1 = vals[0]
   171  	if v1.Power == 5 {
   172  		v1.Power = 6
   173  	} else {
   174  		v1.Power = 5
   175  	}
   176  	diff = []types.ValidatorUpdate{v1}
   177  	tx1 = MakeValSetChangeTx(v1.PubKey, v1.Power)
   178  
   179  	makeApplyBlock(t, kvstore, 3, diff, tx1)
   180  
   181  	vals1 = append([]types.ValidatorUpdate{v1}, vals1[1:]...)
   182  	vals2 = kvstore.Validators()
   183  	valsEqual(t, vals1, vals2)
   184  }
   185  
   186  func makeApplyBlock(
   187  	t *testing.T,
   188  	kvstore ocabci.Application,
   189  	heightInt int,
   190  	diff []types.ValidatorUpdate,
   191  	txs ...[]byte,
   192  ) {
   193  	// make and apply block
   194  	height := int64(heightInt)
   195  	hash := []byte("foo")
   196  	header := tmproto.Header{
   197  		Height: height,
   198  	}
   199  
   200  	kvstore.BeginBlock(ocabci.RequestBeginBlock{Hash: hash, Header: header})
   201  	for _, tx := range txs {
   202  		if r := kvstore.DeliverTx(types.RequestDeliverTx{Tx: tx}); r.IsErr() {
   203  			t.Fatal(r)
   204  		}
   205  	}
   206  	resEndBlock := kvstore.EndBlock(types.RequestEndBlock{Height: header.Height})
   207  	kvstore.Commit()
   208  
   209  	valsEqual(t, diff, resEndBlock.ValidatorUpdates)
   210  }
   211  
   212  // order doesn't matter
   213  func valsEqual(t *testing.T, vals1, vals2 []types.ValidatorUpdate) {
   214  	if len(vals1) != len(vals2) {
   215  		t.Fatalf("vals dont match in len. got %d, expected %d", len(vals2), len(vals1))
   216  	}
   217  	sort.Sort(ocabci.ValidatorUpdates(vals1))
   218  	sort.Sort(ocabci.ValidatorUpdates(vals2))
   219  	for i, v1 := range vals1 {
   220  		v2 := vals2[i]
   221  		if !v1.PubKey.Equal(v2.PubKey) ||
   222  			v1.Power != v2.Power {
   223  			t.Fatalf("vals dont match at index %d. got %X/%d , expected %X/%d", i, v2.PubKey, v2.Power, v1.PubKey, v1.Power)
   224  		}
   225  	}
   226  }
   227  
   228  func makeSocketClientServer(app ocabci.Application, name string) (abcicli.Client, service.Service, error) {
   229  	// Start the listener
   230  	socket := fmt.Sprintf("unix://%s.sock", name)
   231  	logger := log.TestingLogger()
   232  
   233  	server := abciserver.NewSocketServer(socket, app)
   234  	server.SetLogger(logger.With("module", "abci-server"))
   235  	if err := server.Start(); err != nil {
   236  		return nil, nil, err
   237  	}
   238  
   239  	// Connect to the socket
   240  	client := abcicli.NewSocketClient(socket, false)
   241  	client.SetLogger(logger.With("module", "abci-client"))
   242  	if err := client.Start(); err != nil {
   243  		if err = server.Stop(); err != nil {
   244  			return nil, nil, err
   245  		}
   246  		return nil, nil, err
   247  	}
   248  
   249  	return client, server, nil
   250  }
   251  
   252  func makeGRPCClientServer(app ocabci.Application, name string) (abcicli.Client, service.Service, error) {
   253  	// Start the listener
   254  	socket := fmt.Sprintf("unix://%s.sock", name)
   255  	logger := log.TestingLogger()
   256  
   257  	gapp := ocabci.NewGRPCApplication(app)
   258  	server := abciserver.NewGRPCServer(socket, gapp)
   259  	server.SetLogger(logger.With("module", "abci-server"))
   260  	if err := server.Start(); err != nil {
   261  		return nil, nil, err
   262  	}
   263  
   264  	client := abcicli.NewGRPCClient(socket, true)
   265  	client.SetLogger(logger.With("module", "abci-client"))
   266  	if err := client.Start(); err != nil {
   267  		if err := server.Stop(); err != nil {
   268  			return nil, nil, err
   269  		}
   270  		return nil, nil, err
   271  	}
   272  	return client, server, nil
   273  }
   274  
   275  func TestClientServer(t *testing.T) {
   276  	// set up socket app
   277  	kvstore := NewApplication()
   278  	client, server, err := makeSocketClientServer(kvstore, "kvstore-socket")
   279  	require.NoError(t, err)
   280  	t.Cleanup(func() {
   281  		if err := server.Stop(); err != nil {
   282  			t.Error(err)
   283  		}
   284  	})
   285  	t.Cleanup(func() {
   286  		if err := client.Stop(); err != nil {
   287  			t.Error(err)
   288  		}
   289  	})
   290  
   291  	runClientTests(t, client)
   292  
   293  	// set up grpc app
   294  	kvstore = NewApplication()
   295  	gclient, gserver, err := makeGRPCClientServer(kvstore, "/tmp/kvstore-grpc")
   296  	require.NoError(t, err)
   297  
   298  	t.Cleanup(func() {
   299  		if err := gserver.Stop(); err != nil {
   300  			t.Error(err)
   301  		}
   302  	})
   303  	t.Cleanup(func() {
   304  		if err := gclient.Stop(); err != nil {
   305  			t.Error(err)
   306  		}
   307  	})
   308  
   309  	runClientTests(t, gclient)
   310  }
   311  
   312  func runClientTests(t *testing.T, client abcicli.Client) {
   313  	// run some tests....
   314  	key := testKey
   315  	value := key
   316  	tx := []byte(key)
   317  	testClient(t, client, tx, key, value)
   318  
   319  	value = testValue
   320  	tx = []byte(key + "=" + value)
   321  	testClient(t, client, tx, key, value)
   322  }
   323  
   324  func testClient(t *testing.T, app abcicli.Client, tx []byte, key, value string) {
   325  	ar, err := app.DeliverTxSync(types.RequestDeliverTx{Tx: tx})
   326  	require.NoError(t, err)
   327  	require.False(t, ar.IsErr(), ar)
   328  	// repeating tx doesn't raise error
   329  	ar, err = app.DeliverTxSync(types.RequestDeliverTx{Tx: tx})
   330  	require.NoError(t, err)
   331  	require.False(t, ar.IsErr(), ar)
   332  	// commit
   333  	_, err = app.CommitSync()
   334  	require.NoError(t, err)
   335  
   336  	info, err := app.InfoSync(types.RequestInfo{})
   337  	require.NoError(t, err)
   338  	require.NotZero(t, info.LastBlockHeight)
   339  
   340  	// make sure query is fine
   341  	resQuery, err := app.QuerySync(types.RequestQuery{
   342  		Path: "/store",
   343  		Data: []byte(key),
   344  	})
   345  	require.Nil(t, err)
   346  	require.Equal(t, code.CodeTypeOK, resQuery.Code)
   347  	require.Equal(t, key, string(resQuery.Key))
   348  	require.Equal(t, value, string(resQuery.Value))
   349  	require.EqualValues(t, info.LastBlockHeight, resQuery.Height)
   350  
   351  	// make sure proof is fine
   352  	resQuery, err = app.QuerySync(types.RequestQuery{
   353  		Path:  "/store",
   354  		Data:  []byte(key),
   355  		Prove: true,
   356  	})
   357  	require.Nil(t, err)
   358  	require.Equal(t, code.CodeTypeOK, resQuery.Code)
   359  	require.Equal(t, key, string(resQuery.Key))
   360  	require.Equal(t, value, string(resQuery.Value))
   361  	require.EqualValues(t, info.LastBlockHeight, resQuery.Height)
   362  }