github.com/vipernet-xyz/tm@v0.34.24/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/vipernet-xyz/tm/libs/log" 12 "github.com/vipernet-xyz/tm/libs/service" 13 14 abcicli "github.com/vipernet-xyz/tm/abci/client" 15 "github.com/vipernet-xyz/tm/abci/example/code" 16 abciserver "github.com/vipernet-xyz/tm/abci/server" 17 "github.com/vipernet-xyz/tm/abci/types" 18 tmproto "github.com/vipernet-xyz/tm/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 := os.MkdirTemp("/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 := os.MkdirTemp("/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 // add a validator, remove a validator, update a validator 120 func TestValUpdates(t *testing.T) { 121 dir, err := os.MkdirTemp("/tmp", "abci-kvstore-test") // TODO 122 if err != nil { 123 t.Fatal(err) 124 } 125 kvstore := NewPersistentKVStoreApplication(dir) 126 127 // init with some validators 128 total := 10 129 nInit := 5 130 vals := RandVals(total) 131 // initialize with the first nInit 132 kvstore.InitChain(types.RequestInitChain{ 133 Validators: vals[:nInit], 134 }) 135 136 vals1, vals2 := vals[:nInit], kvstore.Validators() 137 valsEqual(t, vals1, vals2) 138 139 var v1, v2, v3 types.ValidatorUpdate 140 141 // add some validators 142 v1, v2 = vals[nInit], vals[nInit+1] 143 diff := []types.ValidatorUpdate{v1, v2} 144 tx1 := MakeValSetChangeTx(v1.PubKey, v1.Power) 145 tx2 := MakeValSetChangeTx(v2.PubKey, v2.Power) 146 147 makeApplyBlock(t, kvstore, 1, diff, tx1, tx2) 148 149 vals1, vals2 = vals[:nInit+2], kvstore.Validators() 150 valsEqual(t, vals1, vals2) 151 152 // remove some validators 153 v1, v2, v3 = vals[nInit-2], vals[nInit-1], vals[nInit] 154 v1.Power = 0 155 v2.Power = 0 156 v3.Power = 0 157 diff = []types.ValidatorUpdate{v1, v2, v3} 158 tx1 = MakeValSetChangeTx(v1.PubKey, v1.Power) 159 tx2 = MakeValSetChangeTx(v2.PubKey, v2.Power) 160 tx3 := MakeValSetChangeTx(v3.PubKey, v3.Power) 161 162 makeApplyBlock(t, kvstore, 2, diff, tx1, tx2, tx3) 163 164 vals1 = append(vals[:nInit-2], vals[nInit+1]) //nolint: gocritic 165 vals2 = kvstore.Validators() 166 valsEqual(t, vals1, vals2) 167 168 // update some validators 169 v1 = vals[0] 170 if v1.Power == 5 { 171 v1.Power = 6 172 } else { 173 v1.Power = 5 174 } 175 diff = []types.ValidatorUpdate{v1} 176 tx1 = MakeValSetChangeTx(v1.PubKey, v1.Power) 177 178 makeApplyBlock(t, kvstore, 3, diff, tx1) 179 180 vals1 = append([]types.ValidatorUpdate{v1}, vals1[1:]...) 181 vals2 = kvstore.Validators() 182 valsEqual(t, vals1, vals2) 183 } 184 185 func makeApplyBlock( 186 t *testing.T, 187 kvstore types.Application, 188 heightInt int, 189 diff []types.ValidatorUpdate, 190 txs ...[]byte, 191 ) { 192 // make and apply block 193 height := int64(heightInt) 194 hash := []byte("foo") 195 header := tmproto.Header{ 196 Height: height, 197 } 198 199 kvstore.BeginBlock(types.RequestBeginBlock{Hash: hash, Header: header}) 200 for _, tx := range txs { 201 if r := kvstore.DeliverTx(types.RequestDeliverTx{Tx: tx}); r.IsErr() { 202 t.Fatal(r) 203 } 204 } 205 resEndBlock := kvstore.EndBlock(types.RequestEndBlock{Height: header.Height}) 206 kvstore.Commit() 207 208 valsEqual(t, diff, resEndBlock.ValidatorUpdates) 209 } 210 211 // order doesn't matter 212 func valsEqual(t *testing.T, vals1, vals2 []types.ValidatorUpdate) { 213 if len(vals1) != len(vals2) { 214 t.Fatalf("vals dont match in len. got %d, expected %d", len(vals2), len(vals1)) 215 } 216 sort.Sort(types.ValidatorUpdates(vals1)) 217 sort.Sort(types.ValidatorUpdates(vals2)) 218 for i, v1 := range vals1 { 219 v2 := vals2[i] 220 if !v1.PubKey.Equal(v2.PubKey) || 221 v1.Power != v2.Power { 222 t.Fatalf("vals dont match at index %d. got %X/%d , expected %X/%d", i, v2.PubKey, v2.Power, v1.PubKey, v1.Power) 223 } 224 } 225 } 226 227 func makeSocketClientServer(app types.Application, name string) (abcicli.Client, service.Service, error) { 228 // Start the listener 229 socket := fmt.Sprintf("unix://%s.sock", name) 230 logger := log.TestingLogger() 231 232 server := abciserver.NewSocketServer(socket, app) 233 server.SetLogger(logger.With("module", "abci-server")) 234 if err := server.Start(); err != nil { 235 return nil, nil, err 236 } 237 238 // Connect to the socket 239 client := abcicli.NewSocketClient(socket, false) 240 client.SetLogger(logger.With("module", "abci-client")) 241 if err := client.Start(); err != nil { 242 if err = server.Stop(); err != nil { 243 return nil, nil, err 244 } 245 return nil, nil, err 246 } 247 248 return client, server, nil 249 } 250 251 func makeGRPCClientServer(app types.Application, name string) (abcicli.Client, service.Service, error) { 252 // Start the listener 253 socket := fmt.Sprintf("unix://%s.sock", name) 254 logger := log.TestingLogger() 255 256 gapp := types.NewGRPCApplication(app) 257 server := abciserver.NewGRPCServer(socket, gapp) 258 server.SetLogger(logger.With("module", "abci-server")) 259 if err := server.Start(); err != nil { 260 return nil, nil, err 261 } 262 263 client := abcicli.NewGRPCClient(socket, true) 264 client.SetLogger(logger.With("module", "abci-client")) 265 if err := client.Start(); err != nil { 266 if err := server.Stop(); err != nil { 267 return nil, nil, err 268 } 269 return nil, nil, err 270 } 271 return client, server, nil 272 } 273 274 func TestClientServer(t *testing.T) { 275 // set up socket app 276 kvstore := NewApplication() 277 client, server, err := makeSocketClientServer(kvstore, "kvstore-socket") 278 require.NoError(t, err) 279 t.Cleanup(func() { 280 if err := server.Stop(); err != nil { 281 t.Error(err) 282 } 283 }) 284 t.Cleanup(func() { 285 if err := client.Stop(); err != nil { 286 t.Error(err) 287 } 288 }) 289 290 runClientTests(t, client) 291 292 // set up grpc app 293 kvstore = NewApplication() 294 gclient, gserver, err := makeGRPCClientServer(kvstore, "/tmp/kvstore-grpc") 295 require.NoError(t, err) 296 297 t.Cleanup(func() { 298 if err := gserver.Stop(); err != nil { 299 t.Error(err) 300 } 301 }) 302 t.Cleanup(func() { 303 if err := gclient.Stop(); err != nil { 304 t.Error(err) 305 } 306 }) 307 308 runClientTests(t, gclient) 309 } 310 311 func runClientTests(t *testing.T, client abcicli.Client) { 312 // run some tests.... 313 key := testKey 314 value := key 315 tx := []byte(key) 316 testClient(t, client, tx, key, value) 317 318 value = testValue 319 tx = []byte(key + "=" + value) 320 testClient(t, client, tx, key, value) 321 } 322 323 func testClient(t *testing.T, app abcicli.Client, tx []byte, key, value string) { 324 ar, err := app.DeliverTxSync(types.RequestDeliverTx{Tx: tx}) 325 require.NoError(t, err) 326 require.False(t, ar.IsErr(), ar) 327 // repeating tx doesn't raise error 328 ar, err = app.DeliverTxSync(types.RequestDeliverTx{Tx: tx}) 329 require.NoError(t, err) 330 require.False(t, ar.IsErr(), ar) 331 // commit 332 _, err = app.CommitSync() 333 require.NoError(t, err) 334 335 info, err := app.InfoSync(types.RequestInfo{}) 336 require.NoError(t, err) 337 require.NotZero(t, info.LastBlockHeight) 338 339 // make sure query is fine 340 resQuery, err := app.QuerySync(types.RequestQuery{ 341 Path: "/store", 342 Data: []byte(key), 343 }) 344 require.Nil(t, err) 345 require.Equal(t, code.CodeTypeOK, resQuery.Code) 346 require.Equal(t, key, string(resQuery.Key)) 347 require.Equal(t, value, string(resQuery.Value)) 348 require.EqualValues(t, info.LastBlockHeight, resQuery.Height) 349 350 // make sure proof is fine 351 resQuery, err = app.QuerySync(types.RequestQuery{ 352 Path: "/store", 353 Data: []byte(key), 354 Prove: true, 355 }) 356 require.Nil(t, err) 357 require.Equal(t, code.CodeTypeOK, resQuery.Code) 358 require.Equal(t, key, string(resQuery.Key)) 359 require.Equal(t, value, string(resQuery.Value)) 360 require.EqualValues(t, info.LastBlockHeight, resQuery.Height) 361 }