gitlab.com/gpdionisio/tendermint@v0.34.19-dev2/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/require" 10 11 "github.com/tendermint/tendermint/libs/log" 12 "github.com/tendermint/tendermint/libs/service" 13 14 abcicli "github.com/tendermint/tendermint/abci/client" 15 "github.com/tendermint/tendermint/abci/example/code" 16 abciserver "github.com/tendermint/tendermint/abci/server" 17 "github.com/tendermint/tendermint/abci/types" 18 tmproto "github.com/tendermint/tendermint/proto/tendermint/types" 19 ) 20 21 const ( 22 testKey = "abc" 23 testValue = "def" 24 ) 25 26 func testKVStore(t *testing.T, app types.Application, tx []byte, key, value string) { 27 req := types.RequestDeliverTx{Tx: tx} 28 ar := app.DeliverTx(req) 29 require.False(t, ar.IsErr(), ar) 30 // repeating tx doesn't raise error 31 ar = app.DeliverTx(req) 32 require.False(t, ar.IsErr(), ar) 33 // commit 34 app.Commit() 35 36 info := app.Info(types.RequestInfo{}) 37 require.NotZero(t, info.LastBlockHeight) 38 39 // make sure query is fine 40 resQuery := app.Query(types.RequestQuery{ 41 Path: "/store", 42 Data: []byte(key), 43 }) 44 require.Equal(t, code.CodeTypeOK, resQuery.Code) 45 require.Equal(t, key, string(resQuery.Key)) 46 require.Equal(t, value, string(resQuery.Value)) 47 require.EqualValues(t, info.LastBlockHeight, resQuery.Height) 48 49 // make sure proof is fine 50 resQuery = app.Query(types.RequestQuery{ 51 Path: "/store", 52 Data: []byte(key), 53 Prove: true, 54 }) 55 require.EqualValues(t, code.CodeTypeOK, resQuery.Code) 56 require.Equal(t, key, string(resQuery.Key)) 57 require.Equal(t, value, string(resQuery.Value)) 58 require.EqualValues(t, info.LastBlockHeight, resQuery.Height) 59 } 60 61 func TestKVStoreKV(t *testing.T) { 62 kvstore := NewApplication() 63 key := testKey 64 value := key 65 tx := []byte(key) 66 testKVStore(t, kvstore, tx, key, value) 67 68 value = testValue 69 tx = []byte(key + "=" + value) 70 testKVStore(t, kvstore, tx, key, value) 71 } 72 73 func TestPersistentKVStoreKV(t *testing.T) { 74 dir, err := ioutil.TempDir("/tmp", "abci-kvstore-test") // TODO 75 if err != nil { 76 t.Fatal(err) 77 } 78 kvstore := NewPersistentKVStoreApplication(dir) 79 key := testKey 80 value := key 81 tx := []byte(key) 82 testKVStore(t, kvstore, tx, key, value) 83 84 value = testValue 85 tx = []byte(key + "=" + value) 86 testKVStore(t, kvstore, tx, key, value) 87 } 88 89 func TestPersistentKVStoreInfo(t *testing.T) { 90 dir, err := ioutil.TempDir("/tmp", "abci-kvstore-test") // TODO 91 if err != nil { 92 t.Fatal(err) 93 } 94 kvstore := NewPersistentKVStoreApplication(dir) 95 InitKVStore(kvstore) 96 height := int64(0) 97 98 resInfo := kvstore.Info(types.RequestInfo{}) 99 if resInfo.LastBlockHeight != height { 100 t.Fatalf("expected height of %d, got %d", height, resInfo.LastBlockHeight) 101 } 102 103 // make and apply block 104 height = int64(1) 105 hash := []byte("foo") 106 header := tmproto.Header{ 107 Height: height, 108 } 109 kvstore.BeginBlock(types.RequestBeginBlock{Hash: hash, Header: header}) 110 kvstore.EndBlock(types.RequestEndBlock{Height: header.Height}) 111 kvstore.Commit() 112 113 resInfo = kvstore.Info(types.RequestInfo{}) 114 if resInfo.LastBlockHeight != height { 115 t.Fatalf("expected height of %d, got %d", height, resInfo.LastBlockHeight) 116 } 117 118 } 119 120 // add a validator, remove a validator, update a validator 121 func TestValUpdates(t *testing.T) { 122 dir, err := ioutil.TempDir("/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 187 func makeApplyBlock( 188 t *testing.T, 189 kvstore types.Application, 190 heightInt int, 191 diff []types.ValidatorUpdate, 192 txs ...[]byte) { 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(types.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 213 // order doesn't matter 214 func valsEqual(t *testing.T, vals1, vals2 []types.ValidatorUpdate) { 215 if len(vals1) != len(vals2) { 216 t.Fatalf("vals dont match in len. got %d, expected %d", len(vals2), len(vals1)) 217 } 218 sort.Sort(types.ValidatorUpdates(vals1)) 219 sort.Sort(types.ValidatorUpdates(vals2)) 220 for i, v1 := range vals1 { 221 v2 := vals2[i] 222 if !v1.PubKey.Equal(v2.PubKey) || 223 v1.Power != v2.Power { 224 t.Fatalf("vals dont match at index %d. got %X/%d , expected %X/%d", i, v2.PubKey, v2.Power, v1.PubKey, v1.Power) 225 } 226 } 227 } 228 229 func makeSocketClientServer(app types.Application, name string) (abcicli.Client, service.Service, error) { 230 // Start the listener 231 socket := fmt.Sprintf("unix://%s.sock", name) 232 logger := log.TestingLogger() 233 234 server := abciserver.NewSocketServer(socket, app) 235 server.SetLogger(logger.With("module", "abci-server")) 236 if err := server.Start(); err != nil { 237 return nil, nil, err 238 } 239 240 // Connect to the socket 241 client := abcicli.NewSocketClient(socket, false) 242 client.SetLogger(logger.With("module", "abci-client")) 243 if err := client.Start(); err != nil { 244 if err = server.Stop(); err != nil { 245 return nil, nil, err 246 } 247 return nil, nil, err 248 } 249 250 return client, server, nil 251 } 252 253 func makeGRPCClientServer(app types.Application, name string) (abcicli.Client, service.Service, error) { 254 // Start the listener 255 socket := fmt.Sprintf("unix://%s.sock", name) 256 logger := log.TestingLogger() 257 258 gapp := types.NewGRPCApplication(app) 259 server := abciserver.NewGRPCServer(socket, gapp) 260 server.SetLogger(logger.With("module", "abci-server")) 261 if err := server.Start(); err != nil { 262 return nil, nil, err 263 } 264 265 client := abcicli.NewGRPCClient(socket, true) 266 client.SetLogger(logger.With("module", "abci-client")) 267 if err := client.Start(); err != nil { 268 if err := server.Stop(); err != nil { 269 return nil, nil, err 270 } 271 return nil, nil, err 272 } 273 return client, server, nil 274 } 275 276 func TestClientServer(t *testing.T) { 277 // set up socket app 278 kvstore := NewApplication() 279 client, server, err := makeSocketClientServer(kvstore, "kvstore-socket") 280 require.NoError(t, err) 281 t.Cleanup(func() { 282 if err := server.Stop(); err != nil { 283 t.Error(err) 284 } 285 }) 286 t.Cleanup(func() { 287 if err := client.Stop(); err != nil { 288 t.Error(err) 289 } 290 }) 291 292 runClientTests(t, client) 293 294 // set up grpc app 295 kvstore = NewApplication() 296 gclient, gserver, err := makeGRPCClientServer(kvstore, "/tmp/kvstore-grpc") 297 require.NoError(t, err) 298 299 t.Cleanup(func() { 300 if err := gserver.Stop(); err != nil { 301 t.Error(err) 302 } 303 }) 304 t.Cleanup(func() { 305 if err := gclient.Stop(); err != nil { 306 t.Error(err) 307 } 308 }) 309 310 runClientTests(t, gclient) 311 } 312 313 func runClientTests(t *testing.T, client abcicli.Client) { 314 // run some tests.... 315 key := testKey 316 value := key 317 tx := []byte(key) 318 testClient(t, client, tx, key, value) 319 320 value = testValue 321 tx = []byte(key + "=" + value) 322 testClient(t, client, tx, key, value) 323 } 324 325 func testClient(t *testing.T, app abcicli.Client, tx []byte, key, value string) { 326 ar, err := app.DeliverTxSync(types.RequestDeliverTx{Tx: tx}) 327 require.NoError(t, err) 328 require.False(t, ar.IsErr(), ar) 329 // repeating tx doesn't raise error 330 ar, err = app.DeliverTxSync(types.RequestDeliverTx{Tx: tx}) 331 require.NoError(t, err) 332 require.False(t, ar.IsErr(), ar) 333 // commit 334 _, err = app.CommitSync() 335 require.NoError(t, err) 336 337 info, err := app.InfoSync(types.RequestInfo{}) 338 require.NoError(t, err) 339 require.NotZero(t, info.LastBlockHeight) 340 341 // make sure query is fine 342 resQuery, err := app.QuerySync(types.RequestQuery{ 343 Path: "/store", 344 Data: []byte(key), 345 }) 346 require.Nil(t, err) 347 require.Equal(t, code.CodeTypeOK, resQuery.Code) 348 require.Equal(t, key, string(resQuery.Key)) 349 require.Equal(t, value, string(resQuery.Value)) 350 require.EqualValues(t, info.LastBlockHeight, resQuery.Height) 351 352 // make sure proof is fine 353 resQuery, err = app.QuerySync(types.RequestQuery{ 354 Path: "/store", 355 Data: []byte(key), 356 Prove: true, 357 }) 358 require.Nil(t, err) 359 require.Equal(t, code.CodeTypeOK, resQuery.Code) 360 require.Equal(t, key, string(resQuery.Key)) 361 require.Equal(t, value, string(resQuery.Value)) 362 require.EqualValues(t, info.LastBlockHeight, resQuery.Height) 363 }