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  }