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 }