github.com/line/ostracon@v1.0.10-0.20230328032236-7f20145f065d/abci/example/kvstore/kvstore_test.go (about)

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