github.com/MetalBlockchain/metalgo@v1.11.9/database/rpcdb/db_test.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package rpcdb 5 6 import ( 7 "context" 8 "fmt" 9 "testing" 10 11 "github.com/stretchr/testify/require" 12 13 "github.com/MetalBlockchain/metalgo/database" 14 "github.com/MetalBlockchain/metalgo/database/corruptabledb" 15 "github.com/MetalBlockchain/metalgo/database/memdb" 16 "github.com/MetalBlockchain/metalgo/vms/rpcchainvm/grpcutils" 17 18 rpcdbpb "github.com/MetalBlockchain/metalgo/proto/pb/rpcdb" 19 ) 20 21 type testDatabase struct { 22 client *DatabaseClient 23 server *memdb.Database 24 } 25 26 func setupDB(t testing.TB) *testDatabase { 27 require := require.New(t) 28 29 db := &testDatabase{ 30 server: memdb.New(), 31 } 32 33 listener, err := grpcutils.NewListener() 34 require.NoError(err) 35 serverCloser := grpcutils.ServerCloser{} 36 37 server := grpcutils.NewServer() 38 rpcdbpb.RegisterDatabaseServer(server, NewServer(db.server)) 39 serverCloser.Add(server) 40 41 go grpcutils.Serve(listener, server) 42 43 conn, err := grpcutils.Dial(listener.Addr().String()) 44 require.NoError(err) 45 46 db.client = NewClient(rpcdbpb.NewDatabaseClient(conn)) 47 48 t.Cleanup(func() { 49 serverCloser.Stop() 50 _ = conn.Close() 51 _ = listener.Close() 52 }) 53 54 return db 55 } 56 57 func TestInterface(t *testing.T) { 58 for name, test := range database.Tests { 59 t.Run(name, func(t *testing.T) { 60 db := setupDB(t) 61 test(t, db.client) 62 }) 63 } 64 } 65 66 func FuzzKeyValue(f *testing.F) { 67 db := setupDB(f) 68 database.FuzzKeyValue(f, db.client) 69 } 70 71 func FuzzNewIteratorWithPrefix(f *testing.F) { 72 db := setupDB(f) 73 database.FuzzNewIteratorWithPrefix(f, db.client) 74 } 75 76 func FuzzNewIteratorWithStartAndPrefix(f *testing.F) { 77 db := setupDB(f) 78 database.FuzzNewIteratorWithStartAndPrefix(f, db.client) 79 } 80 81 func BenchmarkInterface(b *testing.B) { 82 for _, size := range database.BenchmarkSizes { 83 keys, values := database.SetupBenchmark(b, size[0], size[1], size[2]) 84 for name, bench := range database.Benchmarks { 85 b.Run(fmt.Sprintf("rpcdb_%d_pairs_%d_keys_%d_values_%s", size[0], size[1], size[2], name), func(b *testing.B) { 86 db := setupDB(b) 87 bench(b, db.client, keys, values) 88 }) 89 } 90 } 91 } 92 93 func TestHealthCheck(t *testing.T) { 94 scenarios := []struct { 95 name string 96 testDatabase *testDatabase 97 testFn func(db *corruptabledb.Database) error 98 wantErr bool 99 wantErrMsg string 100 }{ 101 { 102 name: "healthcheck success", 103 testDatabase: setupDB(t), 104 testFn: func(_ *corruptabledb.Database) error { 105 return nil 106 }, 107 }, 108 { 109 name: "healthcheck failed db closed", 110 testDatabase: setupDB(t), 111 testFn: func(db *corruptabledb.Database) error { 112 return db.Close() 113 }, 114 wantErr: true, 115 wantErrMsg: "closed", 116 }, 117 } 118 for _, scenario := range scenarios { 119 t.Run(scenario.name, func(t *testing.T) { 120 require := require.New(t) 121 122 baseDB := setupDB(t) 123 db := corruptabledb.New(baseDB.server) 124 defer db.Close() 125 require.NoError(scenario.testFn(db)) 126 127 // check db HealthCheck 128 _, err := db.HealthCheck(context.Background()) 129 if scenario.wantErr { 130 require.Error(err) //nolint:forbidigo 131 require.Contains(err.Error(), scenario.wantErrMsg) 132 return 133 } 134 require.NoError(err) 135 136 // check rpc HealthCheck 137 _, err = baseDB.client.HealthCheck(context.Background()) 138 require.NoError(err) 139 }) 140 } 141 }