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