diff --git a/integration/v3_grpc_test.go b/integration/v3_grpc_test.go index 83b211578c72..263de9f945bf 100644 --- a/integration/v3_grpc_test.go +++ b/integration/v3_grpc_test.go @@ -21,6 +21,7 @@ import ( "math/rand" "os" "reflect" + "strconv" "testing" "time" @@ -147,6 +148,55 @@ func TestV3CompactCurrentRev(t *testing.T) { } } +// TestV3HashKV ensures that multiple calls of HashKV on same node return same hash and compact rev. +func TestV3HashKV(t *testing.T) { + defer testutil.AfterTest(t) + clus := NewClusterV3(t, &ClusterConfig{Size: 1}) + defer clus.Terminate(t) + + kvc := toGRPC(clus.RandClient()).KV + mvc := toGRPC(clus.RandClient()).Maintenance + + var rev int64 + for i := 0; i < 10; i++ { + resp, err := kvc.Put(context.Background(), &pb.PutRequest{Key: []byte("foo"), Value: []byte("bar" + strconv.Itoa(i))}) + if err != nil { + t.Fatal(err) + } + rev = resp.Header.Revision + } + + // ensure appliedIdx is sync with committedIdx + _, err := kvc.Range(context.Background(), &pb.RangeRequest{Key: []byte("foo")}) + if err != nil { + t.Fatal(err) + } + + var ( + prevHash uint32 + prevCompactRev int64 + ) + for i := 0; i < 10; i++ { + resp, err := mvc.HashKV(context.Background(), &pb.HashKVRequest{rev}) + if err != nil { + t.Fatal(err) + } + if prevHash == 0 { + prevHash = resp.Hash + prevCompactRev = resp.CompactRevision + continue + } + + if prevHash != resp.Hash { + t.Fatalf("prevHash %v != Hash %v", prevHash, resp.Hash) + } + + if prevCompactRev != resp.CompactRevision { + t.Fatalf("prevCompactRev %v != CompactRevision %v", prevHash, resp.Hash) + } + } +} + func TestV3TxnTooManyOps(t *testing.T) { defer testutil.AfterTest(t) maxTxnOps := uint(128)