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 }